Administrar citas: crear y manipular, convertir ICS a MSG

Este artículo cubre citas iCalendar (ICS) basadas en estándares manejadas a través del Appointment clase. Para trabajar con Outlook MapiCalendar elementos en su lugar, vea Administrar elementos del calendario de Outlook; para almacenar y leer elementos del calendario dentro de un PST, vea Administrar elementos del calendario en archivos PST.

Crear una cita y guardarla en disco en formato MSG o ICS

El Appointment clase en Aspose.Email para .NET que se puede usar para crear una nueva cita. En este artículo, primero creamos una cita y la guardamos en disco en formato ICS. Se requieren los siguientes pasos para crear una cita y guardarla en disco.

  1. Cree una instancia de la Appointment clase y inicialícela con este constructor.
  2. Pase los siguientes argumentos en el constructor anterior
    1. Ubicación
    2. Resumen
    3. Descripción
    4. Fecha de inicio
    5. Fecha de finalización
    6. Organizador
    7. Asistentes
  3. Llame al Save() método y especificar el nombre del archivo y el formato en los argumentos.

La cita puede abrirse en Microsoft Outlook o en cualquier programa que pueda cargar un archivo ICS. Si el archivo se abre en Microsoft Outlook, este agrega automáticamente la cita al calendario de Outlook.

El siguiente fragmento de código muestra cómo crear y guardar una cita en disco en formato ICS o MSG.


// Create and initialize an instance of the Appointment class
Appointment appointment = new Appointment(
    "Meeting Room 3 at Office Headquarters",// Location
    "Monthly Meeting",                      // Summary
    "Please confirm your availability.",    // Description
    new DateTime(2015, 2, 8, 13, 0, 0),     // Start date
    new DateTime(2015, 2, 8, 14, 0, 0),     // End date
    "from@domain.com",                      // Organizer
    "attendees@domain.com");                // Attendees

// Save the appointment to disk in ICS format            
appointment.Save(fileName + ".ics", new AppointmentIcsSaveOptions());
Console.WriteLine("Appointment created and saved to disk successfully.");

// Save the appointment to disk in MSG format
appointment.Save(fileName + ".msg", new AppointmentMsgSaveOptions());
Console.WriteLine("Appointment created and saved to disk successfully.");

Crear una cita con contenido HTML

Puede especificar representaciones alternativas de la descripción del evento en diferentes tipos de contenido usando el encabezado X-ALT-DESC. Permite a los destinatarios del archivo iCalendar elegir la representación que mejor se adapte a sus necesidades. Por ejemplo, puede incluir una descripción en texto plano usando el tipo de contenido "text/plain" y una descripción en HTML usando el tipo de contenido "text/html". El encabezado X-ALT-DESC se agrega para cada representación alternativa. Para crear una cita con contenido HTML, establezca el HtmlDescription propiedad.

Pruebe el siguiente ejemplo de código para crear una cita con descripción HTML alternativa:

  1. Cree una nueva instancia de la clase Appointment.
  2. Proporcione los parámetros necesarios al constructor Appointment:
    • Especifique la ubicación de la cita.
    • Establezca la fecha y hora de inicio.
    • Establezca la fecha y hora de finalización.
    • Especifique el organizador.
    • Especifique el asistente.
  3. Establezca el HtmlDescription propiedad del objeto de cita, indicando que la descripción está en formato HTML.
  4. Establezca la propiedad Description del objeto de cita a una cadena con formato HTML, encerrada dentro de una cadena multilínea:
    • El marcado HTML incluye un bloque <style> que define una clase CSS llamada "text" con estilos de fuente.
    • El cuerpo HTML contiene una etiqueta de párrafo <p> con la clase CSS "text", y el mensaje real de la invitación.
  5. El objeto de cita ahora está listo, y puede realizar más operaciones o guardarlo como un archivo iCalendar.
var appointment = new Appointment("Bygget 83",
    DateTime.UtcNow, // start date
    DateTime.UtcNow.AddHours(1), // end date
    new MailAddress("TintinStrom@from.com", "Tintin Strom"), // organizer
    new MailAddress("AinaMartensson@to.com", "Aina Martensson")) // attendee
{
    HtmlDescription = @"
    <html>
     <style type=""text/css"">
      .text {
             font-family:'Comic Sans MS';
             font-size:16px;
            }
     </style>
    <body>
     <p class=""text"">Hi, I'm happy to invite you to our party.</p>
    </body>
    </html>"
};

Crear una solicitud de cita borrador

Se mostró en nuestros artículos anteriores cómo crear y guardar una cita en formato ICS. A menudo se requiere crear una solicitud de cita en modo Borrador, de modo que la información básica se agregue y luego la misma cita Borrador se reenvíe a otros usuarios para los cambios necesarios según los usos individuales. Para guardar una cita en modo Borrador, el MethodType la propiedad de la clase Appointment debe establecerse en AppointmentMethodType.Publish. El siguiente fragmento de código muestra cómo crear una solicitud de cita en borrador.

string sender = "test@gmail.com";
string recipient = "test@email.com";

MailMessage message = new MailMessage(sender, recipient, string.Empty, string.Empty);

Appointment app = new Appointment(string.Empty, DateTime.Now, DateTime.Now, sender, recipient)
{
    MethodType = AppointmentMethodType.Publish
};

message.AddAlternateView(app.RequestApointment());

MapiMessage msg = MapiMessage.FromMailMessage(message);

// Save the appointment as draft.
msg.Save(dstDraft);

Console.WriteLine(Environment.NewLine + "Draft saved at " + dstDraft);

Creación de borrador de cita a partir de texto

El siguiente fragmento de código le muestra cómo crear una cita borrador a partir de texto. 

string ical = @"BEGIN:VCALENDAR
METHOD:PUBLISH
PRODID:-//Aspose Ltd//iCalender Builder (v3.0)//EN
VERSION:2.0
BEGIN:VEVENT
ATTENDEE;CN=test@gmail.com:mailto:test@gmail.com
DTSTART:20130220T171439
DTEND:20130220T174439
DTSTAMP:20130220T161439Z
END:VEVENT
END:VCALENDAR";

string sender = "test@gmail.com";
string recipient = "test@email.com";
MailMessage message = new MailMessage(sender, recipient, string.Empty, string.Empty);
AlternateView av = AlternateView.CreateAlternateViewFromString(ical, new ContentType("text/calendar"));
message.AlternateViews.Add(av);
MapiMessage msg = MapiMessage.FromMailMessage(message);
msg.Save(dataDir + "draft_out.msg");

Personalizar citas

Establecer el estado de los participantes de los asistentes a la cita

La API Aspose.Email para .NET le permite establecer el estado de los asistentes a la cita al formular un mensaje de respuesta. Esto agrega la propiedad PARTSTAT al archivo ICS.

DateTime startDate = new DateTime(2011, 12, 10, 10, 12, 11),
         endDate = new DateTime(2012, 11, 13, 13, 11, 12);
MailAddress organizer = new MailAddress("aaa@amail.com", "Organizer");
MailAddressCollection attendees = new MailAddressCollection();
MailAddress attendee1 = new MailAddress("bbb@bmail.com", "First attendee");
MailAddress attendee2 = new MailAddress("ccc@cmail.com", "Second attendee");

attendee1.ParticipationStatus = ParticipationStatus.Accepted;
attendee2.ParticipationStatus = ParticipationStatus.Declined;
attendees.Add(attendee1);
attendees.Add(attendee2);

Appointment target = new Appointment(location, startDate, endDate, organizer, attendees);

Personalizar el identificador de producto para iCalendar

La API Aspose.Email para .NET permite obtener o establecer el identificador del producto que creó el objeto iCalendar.

string description = "Test Description";
Appointment app = new Appointment("location", "test appointment", description, DateTime.Today,
DateTime.Today.AddDays(1), "first@test.com", "second@test.com");

AppointmentIcsSaveOptions saveOptions = AppointmentIcsSaveOptions.Default;
saveOptions.ProductId = "Test Corporation";
app.Save(dataDir + "ChangeProdIdOfICS.ics", saveOptions);

Cargando citas

Además, el Appointment la clase puede usarse para cargar una cita desde un archivo ICS.

Cargar una cita en formato ICS

Para cargar una cita en formato ICS, se requieren los siguientes pasos:

  1. Cree una instancia de la Appointment clase.
  2. Llame al Load() método proporcionando la ruta del archivo ICS.
  3. Lea cualquier propiedad para obtener cualquier información de la cita (archivo ICS).

El siguiente fragmento de código muestra cómo cargar una cita en formato ICS.

// Load an Appointment just created and saved to disk and display its details.
Appointment loadedAppointment = Appointment.Load(dstEmail);
Console.WriteLine(Environment.NewLine + "Loaded Appointment details are as follows:");
// Display the appointment information on screen
Console.WriteLine("Summary: " + loadedAppointment.Summary);
Console.WriteLine("Location: " + loadedAppointment.Location);
Console.WriteLine("Description: " + loadedAppointment.Description);
Console.WriteLine("Start date: " + loadedAppointment.StartDate);
Console.WriteLine("End date: " + loadedAppointment.EndDate);
Console.WriteLine("Organizer: " + loadedAppointment.Organizer);
Console.WriteLine("Attendees: " + loadedAppointment.Attendees);
Console.WriteLine(Environment.NewLine + "Appointment loaded successfully from " + dstEmail);

Convertir ICS a MSG

La API le permite convertir fácilmente una Cita en un objeto de mensaje. El siguiente ejemplo de código muestra cómo convertir una solicitud de cita en un MailMessage o MapiMessage:

var appointment = Appointment.Load("appRequest.ics");

var eml = appointment.ToMailMessage();
var msg = appointment.ToMapiMessage();

Leer varios eventos de un archivo ICS

List<Appointment> appointments = new List<Appointment>();
CalendarReader reader = new CalendarReader(dataDir + "US-Holidays.ics");

while (reader.NextEvent())
{
    appointments.Add(reader.Current);
}
//working with appointments...

Escribir múltiples eventos en archivo ICS

AppointmentIcsSaveOptions saveOptions = new AppointmentIcsSaveOptions();
saveOptions.Action = AppointmentAction.Create;
using (CalendarWriter writer = new CalendarWriter(dataDir + "WriteMultipleEventsToICS_out.ics", saveOptions))
{
    for (int i = 0; i < 10; i++)
    {
        Appointment app = new Appointment(string.Empty, DateTime.Now, DateTime.Now, "sender@domain.com", "receiver@domain.com");
        app.Description = "Test body " + i;
        app.Summary = "Test summary:" + i;
        writer.Write(app);
    }
}

Determinar la versión de la cita

Para determinar la versión de una cita, puede usar el Appointment.Version propiedad del Appointment clase. Esta propiedad ayuda a determinar en qué versión se basan sus archivos, garantizando la integración con otros sistemas y aplicaciones.

El siguiente ejemplo de código muestra cómo implementar esta propiedad en su proyecto:

var app = Appointment.Load("meeting.ics");

// Version is a string that holds the ICS/VCS version, e.g. "2.0" for iCalendar
if (app.Version == "2.0")
{
    // do something
}

Envío y cancelación de solicitudes de reunión

El Appointment clase, junto con el SmtpClient, puede usarse para enviar solicitudes de reunión, solicitudes de reunión recurrentes, actualizaciones y cancelaciones por correo electrónico. La cita se agrega a un MailMessage como vista alternativa.

Enviar una solicitud de reunión

Para enviar una solicitud de reunión, cree un Appointment, agréguelo a un mensaje usando el RequestApointment método, y envíe el mensaje. Guarde la UniqueId valor para que pueda referenciar la misma cita más tarde al enviar una actualización o una cancelación.

// Create an instance of SmtpClient
SmtpClient client = new SmtpClient("smtp.gmail.com", 587, "user@gmail.com", "password");
client.SecurityOptions = SecurityOptions.Auto;

// Gather the attendees
MailAddressCollection attendees = new MailAddressCollection();
attendees.Add(new MailAddress("first.attendee@domain.com", "First Attendee"));
attendees.Add(new MailAddress("second.attendee@domain.com", "Second Attendee"));

// Create the message and the appointment
MailMessage msg = new MailMessage();
msg.From = "organizer@domain.com";
msg.To = attendees;

Appointment app = new Appointment("Meeting Room 1", DateTime.Now, DateTime.Now.AddHours(1), msg.From, attendees);
app.Summary = "Monthly Meeting";
app.Description = "Please confirm your availability.";

// Add the appointment to the message and send it
msg.AddAlternateView(app.RequestApointment());
client.Send(msg);

Enviar una solicitud de reunión recurrente

Para crear una solicitud de reunión con recurrencia, asigne un patrón de recurrencia (por ejemplo, un WeeklyRecurrencePattern) al Appointment.Recurrence propiedad. Guardar el ID único de la cita permite enviar actualizaciones a la cita más tarde.

// Create a mail message
MailMessage msg1 = new MailMessage();
msg1.To.Add("to@domain.com");
msg1.From = new MailAddress("from@gmail.com");

// Fill the appointment object
DateTime startDate = new DateTime(2013, 12, 1, 17, 0, 0);
DateTime endDate = new DateTime(2013, 12, 31, 17, 30, 0);
Appointment agendaAppointment = new Appointment("same place", startDate, endDate, msg1.From, msg1.To);

// Create a unique id so the appointment can be accessed later
string szUniqueId = Guid.NewGuid().ToString();
agendaAppointment.UniqueId = szUniqueId;
agendaAppointment.Description = "----------------";

// Create a weekly recurrence pattern: Mon, Tue and Thu
WeeklyRecurrencePattern pattern1 = new WeeklyRecurrencePattern(14);
pattern1.StartDays = new CalendarDay[3];
pattern1.StartDays[0] = CalendarDay.Monday;
pattern1.StartDays[1] = CalendarDay.Tuesday;
pattern1.StartDays[2] = CalendarDay.Thursday;
pattern1.Interval = 1;

// Set the recurrence pattern for the appointment
agendaAppointment.Recurrence = pattern1;

// Attach the appointment to the mail
msg1.AlternateViews.Add(agendaAppointment.RequestApointment());

// Send the mail with the appointment request
SmtpClient client = new SmtpClient("smtp.gmail.com", 587, "your.email@gmail.com", "your.password");
client.SecurityOptions = SecurityOptions.Auto;
client.Send(msg1);

Enviar una solicitud de actualización de cita

Para enviar una actualización de una cita previamente enviada, se requiere el ID único de la cita. Use el UpdateAppointment método para construir la vista alternativa de actualización.

static public void SendUpdate(string szUniqueId)
{
    DateTime startDate = new DateTime(2013, 12, 12, 17, 0, 0);
    DateTime endDate = new DateTime(2013, 12, 12, 17, 30, 0);
    Appointment appUpdate = new Appointment("Different Place", startDate, endDate,
        "organizer@gmail.com", "attendee@domain.com");
    appUpdate.UniqueId = szUniqueId;
    appUpdate.Summary = "update meeting request summary";
    appUpdate.Description = "update";

    MailMessage msgUpdate = new MailMessage("organizer@gmail.com", "attendee@domain.com",
        "test email - update meeting request", "test email");
    msgUpdate.AddAlternateView(appUpdate.UpdateAppointment());

    SmtpClient smtp = new SmtpClient("server.domain.com", 587, "username", "password");
    smtp.Send(msgUpdate);
}

Cancelar una solicitud de reunión

Para cancelar una reunión, construya el mismo Appointment (usando la información que almacenó cuando se envió la solicitud), agréguelo a un mensaje con el CancelAppointment método, y envíe el mensaje a los asistentes.

// Re-create the attendee collection and the appointment from your stored data
MailAddressCollection attendees = new MailAddressCollection();
attendees.Add(new MailAddress("first.attendee@domain.com", "First Attendee"));
attendees.Add(new MailAddress("second.attendee@domain.com", "Second Attendee"));

Appointment app = new Appointment("Meeting Room 1", "Monthly Meeting", "Please confirm your availability.",
    DateTime.Now, DateTime.Now.AddHours(1),
    new MailAddress("organizer@domain.com", "Organizer"), attendees);

// Create the cancellation message
MailMessage msg = new MailMessage();
msg.From = new MailAddress("organizer@domain.com", "");
msg.To = attendees;
msg.Subject = "Cancel meeting";
msg.AddAlternateView(app.CancelAppointment());

SmtpClient smtp = new SmtpClient("smtp.gmail.com", 587, "user@gmail.com", "password");
smtp.Send(msg);

Trabajando con patrones de recurrencia iCalendar

Un patrón de recurrencia es una forma de describir un horario concreto. Contiene la información suficiente para construir una lista de ocurrencias (fechas y horas) según un horario dado. Un patrón de recurrencia puede contener reglas de recurrencia que describen ciclos que se combinan para formar el patrón global. En general, cuanto más complejo sea un patrón de recurrencia, más reglas de recurrencia contendrá.

Los patrones de recurrencia pueden incluir excepciones (no confundir con excepciones que representan errores durante la ejecución de la aplicación). Las excepciones añaden o quitan fechas de ocurrencia respecto al patrón original. Pueden especificarse como ocurrencias explícitas o como un patrón por sí mismas. Ejemplos de patrones de recurrencia con excepciones:

  • Cada segundo viernes, excepto de junio a agosto.
  • El 1.º de cada mes, excepto en enero, cuando debe ser el 2.

El RFC de iCalendar define componentes, como VEVENT o VTODO, que representan eventos o tareas. Los componentes pueden tener propiedades como fecha/hora de inicio, descripción, ubicación, asistentes y recurrencia. Un patrón de recurrencia normalmente existe como una propiedad de una tarea o evento recurrente. Las propiedades del patrón de recurrencia definidas por iCalendar son:

  • DTSTART – la fecha y hora de inicio del patrón (también representa la primera ocurrencia si no se excluye explícitamente).
  • RRULE – especifica una regla repetitiva para un conjunto de recurrencia.
  • RDATE – define una lista de fechas y horas a incluir en un conjunto de recurrencia.
  • EXRULE – especifica una regla repetitiva para excepciones de un conjunto de recurrencia.
  • EXDATE – define una lista de excepciones de fecha y hora de un conjunto de recurrencia.

Solo se requiere DTSTART, y solo debe haber un DTSTART. Todas las demás propiedades son opcionales y pueden especificarse más de una vez.

El modelo de objetos de recurrencia de Aspose.Email

El Aspose.Email.Calendar.Recurrences el espacio de nombres contiene las clases usadas para trabajar con recurrencias iCalendar. CalendarRecurrence y RecurrenceRule son las clases centrales, y proporcionan implementaciones concretas de los elementos RFC 2445 correspondientes.

  • El CalendarRecurrence la clase representa todo el patrón de recurrencia. Puede crear un nuevo patrón de recurrencia desde cero usando el constructor predeterminado, o cargar un patrón existente en formato iCalendar usando el método estático FromiCalendar método.
  • El RecurrenceRule la clase representa la parte RRULE o EXRULE de un patrón de recurrencia. RecurrenceRule expone una serie de propiedades que se mapean directamente a sus contrapartes en el estándar iCalendar. Por ejemplo, ByMonth se corresponde con BYMONTH en iCalendar, etc. Al examinar o establecer valores de los RecurrenceRule propiedades, puede analizar o modificar una regla de recurrencia.

El siguiente fragmento de código carga un patrón de recurrencia (en el que la parte RRULE contiene la regla de recurrencia) y genera las ocurrencias:

// Ten team meetings, every Monday at 10am.
CalendarRecurrence pattern = new CalendarRecurrence("DTSTART:20040301T100000\n" + "RRULE:FREQ=WEEKLY;COUNT=10;BYDAY=MO");
DateCollection dates = pattern.GenerateOccurrences();

Generar ocurrencias a partir de un patrón de recurrencia

Con Aspose.Email es posible generar ocurrencias a partir de un patrón de recurrencia. Para obtener la ocurrencia "siguiente", use el GenerateOccurrences método con el parámetro nNextOccurrences = 1. El siguiente fragmento de código genera 20 ocurrencias usando GenerateOccurrences(20).

CalendarRecurrence recurrencePattern = new CalendarRecurrence();
recurrencePattern.StartDate = new DateTime(1997, 9, 10, 9, 0, 0);
RecurrenceRule rule = recurrencePattern.RRules.Add();
rule.Frequency = Frequency.Monthly;
rule.Count = 20;
rule.Interval = 18;
rule.ByMonthDay.Add(new int[] { 10, 11, 12, 13, 14, 15 });
DateCollection expectedDates = recurrencePattern.GenerateOccurrences(20);
Console.WriteLine("expectedDates.Count = " + expectedDates.Count);
foreach (DateTime date in expectedDates)
{
    Console.WriteLine("DateTime = " + date);
}

Obtener texto fácil de leer para una recurrencia

El texto fácil de leer para una regla puede obtenerse usando el FriendlyText propiedad. La salida del siguiente código es: "Recurre cada mes en el 1.º y 1.º desde el día(s) final(es) del mes para un máximo de 2 ocurrencias."

RecurrenceRule rule = new RecurrenceRule();
rule.Frequency = Frequency.Monthly;
rule.Count = 2;
rule.ByMonthDay.Add(1);
rule.ByMonthDay.Add(-1);
Console.WriteLine(rule.FriendlyText);

Patrones de recurrencia de muestra

Las siguientes cadenas RRULE de ejemplo ilustran cómo expresar horarios comunes.

  • El último día del mes, cada mes. Si desea una ocurrencia el día antes del último día del mes, use BYMONTHDAY=-2. Si especifica BYMONTHDAY=31, entonces según el estándar iCalendar, no se genera ninguna ocurrencia en los meses que tienen menos de 31 días.

    RRULE:FREQ=MONTHLY;BYMONTHDAY=-1
    
  • El último día laborable de cada mes. Esta regla especifica todos los días laborables de un mes y selecciona el último. El resultado neto es el último día laborable del mes.

    RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1
    
  • El último lunes del año.

    RRULE:FREQ=YEARLY;BYDAY=-1MO
    
  • Viernes de la primera semana ISO 8601 del año. En la especificación ISO 8601, la primera semana del año es la primera que tiene al menos cuatro días.

    FREQ=YEARLY;BYWEEKNO=1;BYDAY=FR
    
  • Primer viernes del año. En 1999, por ejemplo, el primer viernes del año es el 1999/01/01, mientras que el viernes de la primera semana ISO 8601 es el 1999/01/08.

    FREQ=YEARLY;BYDAY=1FR
    

Detalles importantes de iCalendar (RFC 2445)

Las fechas, o fechas con horas asociadas, pueden usarse en los elementos DTSTART, UNTIL, EXDATE y RDATE al especificar un patrón de recurrencia. iCalendar define el tipo de valor DATE para identificar valores que contienen una fecha de calendario, y el tipo DATE‑TIME para identificar valores que especifican una fecha y hora precisas. Los valores DATE‑TIME pueden especificarse en tres formas: hora local, hora UTC y hora local con zona horaria.

  • DATE. Según el estándar iCalendar, los valores DATE deben seguir el yyyyMMdd formato. Por ejemplo, 19970714 representa el 14 de julio de 1997.
  • DATE‑TIME con hora local. La forma de fecha‑hora con hora local es simplemente un valor de fecha‑hora que no contiene el designador UTC y no hace referencia a una zona horaria. Por ejemplo, DTSTART:19980118T230000 representa el 18 de enero de 1998 a las 23:00. Los valores de fecha‑hora de este tipo se consideran "flotantes" y no están ligados a ninguna zona horaria específica. Representan la misma hora, minuto y segundo sin importar la zona horaria observada.
  • DATE‑TIME con hora UTC. La fecha con hora UTC (tiempo absoluto) se identifica con una letra mayúscula Z sufijo añadido al valor de tiempo. Por ejemplo, DTSTART:19980119T070000Z representa el 19 de enero de 1998 a las 07:00 UTC. Note que Aspose.Email ignora el designador UTC Z sufijo y trata la hora como hora local. El estándar RFC 2445 indica que una porción de tiempo especificada en la regla UNTIL de un patrón de recurrencia debe estar en formato UTC; Aspose.Email acepta la hora en cualquier formato en la regla UNTIL.
  • DATE‑TIME con hora local y zona horaria. Para hacer referencia a una zona horaria, DATE‑TIME se modifica con la propiedad TZID. Por ejemplo, DTSTART;TZID=US-Eastern:19980119T020000 representa las 2 a.m. en Nueva York el 19 de enero de 1998. Tenga en cuenta que Aspose.Email actualmente ignora el parámetro TZID y trata la hora como hora local.
  • Cumplimiento de BYWEEKNO e ISO 8601. Use BYWEEKNO solo cuando se requiera conformidad con ISO 8601 es obligatorio. Los números de semana definidos por ISO 8601 difieren de los números de semana en el sentido habitual: el número de semana uno del año calendario es la primera semana que contiene al menos cuatro días. La regla BYWEEKNO especifica una lista separada por comas de números que identifican semanas del año (valores válidos son de 1 a 53 y de -1 a -53), y solo es válida para reglas ANUALES.

Ver también