Ir al contenido principal

Vali-Schedule

IValiSchedule es un constructor fluido para horarios recurrentes. Admite patrones de recurrencia diarios, semanales, mensuales y anuales con fecha de inicio, condiciones de fin y predicados personalizados.

Instalación

dotnet add package Vali-Schedule

Registro

builder.Services.AddValiSchedule();
// o mediante el meta-paquete:
builder.Services.AddValiTempo();
Seguridad en hilos

IValiSchedule no es seguro para hilos y no debe registrarse como singleton. Regístralo como scoped o transient, o crea nuevas instancias según sea necesario. El patrón del constructor mantiene estado entre llamadas.

Enumeraciones

RecurrenceType

Define con qué frecuencia se repite el horario:

ValorDescripción
DailySe repite cada N días
WeeklySe repite cada N semanas en días especificados
MonthlySe repite cada N meses en un día especificado
YearlySe repite cada N años en un mes y día especificados

RecurrenceEnd

Define cuándo se detiene el horario:

ValorDescripción
NeverEl horario se repite indefinidamente
AfterOccurrencesSe detiene después de un número fijo de ocurrencias
OnDateSe detiene en o después de una fecha de fin específica

API de constructor fluido de IValiSchedule

Every

Establece el intervalo y el tipo de recurrencia:

schedule.Every(1, RecurrenceType.Daily)    // cada día
schedule.Every(2, RecurrenceType.Weekly) // cada 2 semanas
schedule.Every(1, RecurrenceType.Monthly) // cada mes
schedule.Every(1, RecurrenceType.Yearly) // cada año

Firma: IValiSchedule Every(int interval, RecurrenceType type)

On

Para horarios semanales: especifica en qué días de la semana incluir:

schedule.Every(1, RecurrenceType.Weekly)
.On(DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday)

Firma: IValiSchedule On(params DayOfWeek[] days)

At

Establece la hora del día para las ocurrencias:

schedule.Every(1, RecurrenceType.Daily)
.At(9, 0) // 9:00 AM

Firma: IValiSchedule At(int hour, int minute = 0, int second = 0)

StartingFrom

Establece la fecha de inicio (por defecto es DateTime.Today):

schedule.Every(1, RecurrenceType.Weekly)
.StartingFrom(new DateTime(2025, 4, 1))

Firma: IValiSchedule StartingFrom(DateTime startDate)

EndsAfter

Detiene el horario después de un número fijo de ocurrencias:

schedule.Every(1, RecurrenceType.Daily)
.EndsAfter(10) // 10 ocurrencias y luego se detiene

Firma: IValiSchedule EndsAfter(int occurrences)

EndsOn

Detiene el horario en o después de una fecha específica:

schedule.Every(1, RecurrenceType.Monthly)
.EndsOn(new DateTime(2026, 12, 31))

Firma: IValiSchedule EndsOn(DateTime endDate)

OnDayOfMonth

Para horarios mensuales: especifica qué día del mes:

schedule.Every(1, RecurrenceType.Monthly)
.OnDayOfMonth(15) // día 15 de cada mes

Firma: IValiSchedule OnDayOfMonth(int day)

WithCustomPredicate

Agrega un filtro personalizado — solo se incluyen las ocurrencias donde el predicado devuelve true:

schedule.Every(1, RecurrenceType.Daily)
.WithCustomPredicate(date => date.DayOfWeek != DayOfWeek.Friday)
// Omite los viernes

Firma: IValiSchedule WithCustomPredicate(Func<DateTime, bool> predicate)

Build

Finaliza el constructor y devuelve un objeto Schedule de solo lectura:

var sched = schedule
.Every(1, RecurrenceType.Weekly)
.On(DayOfWeek.Monday)
.At(9, 0)
.StartingFrom(new DateTime(2025, 1, 6))
.Build();

Firma: Schedule Build()

Métodos de consulta

Una vez que tienes un Schedule, usa estos métodos para consultar ocurrencias:

NextOccurrence

Devuelve la próxima ocurrencia después de una fecha dada:

DateTime? next = sched.NextOccurrence(DateTime.Now);
// → null si el horario ha terminado

Firma: DateTime? NextOccurrence(DateTime after)

PreviousOccurrence

Devuelve la ocurrencia más reciente antes de una fecha dada:

DateTime? prev = sched.PreviousOccurrence(DateTime.Now);

Firma: DateTime? PreviousOccurrence(DateTime before)

OccursOn

Devuelve true si el horario tiene una ocurrencia en la fecha dada (ignorando la hora):

bool occurs = sched.OccursOn(new DateTime(2025, 4, 7));

Firma: bool OccursOn(DateTime date)

Occurrences

Enumera las próximas N ocurrencias desde una fecha dada:

IEnumerable<DateTime> next5 = sched.Occurrences(5, DateTime.Now);

foreach (var occ in next5)
Console.WriteLine(occ.ToString("g"));

Firma: IEnumerable<DateTime> Occurrences(int count, DateTime from)

OccurrencesInRange

Enumera todas las ocurrencias dentro de un rango de fechas:

var range = new DateRange(new DateTime(2025, 4, 1), new DateTime(2025, 6, 30));
IEnumerable<DateTime> q2Meetings = sched.OccurrencesInRange(range);

Firma: IEnumerable<DateTime> OccurrencesInRange(DateRange range)

Ejemplos completos

Standup semanal

public class StandupScheduler(IValiSchedule schedule)
{
public Schedule BuildStandup(DateTime startDate)
{
return schedule
.Every(1, RecurrenceType.Weekly)
.On(DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday,
DayOfWeek.Thursday, DayOfWeek.Friday)
.At(9, 30)
.StartingFrom(startDate)
.Build();
}

public void PrintNextStandups(Schedule sched, int count = 5)
{
Console.WriteLine($"Next {count} standups:");
foreach (var occ in sched.Occurrences(count, DateTime.Now))
Console.WriteLine($" {occ:ddd, MMM dd HH:mm}");
}
}

Ejecución mensual de nómina

public class PayrollScheduler(IValiSchedule schedule)
{
public Schedule BuildPayroll(int dayOfMonth = 25)
{
return schedule
.Every(1, RecurrenceType.Monthly)
.OnDayOfMonth(dayOfMonth)
.At(8, 0)
.StartingFrom(new DateTime(2025, 1, 1))
// Omitir meses donde el día 25 cae en fin de semana
.WithCustomPredicate(d =>
d.DayOfWeek != DayOfWeek.Saturday &&
d.DayOfWeek != DayOfWeek.Sunday)
.Build();
}
}

Recordatorio anual con condición de fin

var reminder = schedule
.Every(1, RecurrenceType.Yearly)
.At(10, 0)
.StartingFrom(new DateTime(2025, 3, 23))
.EndsAfter(5) // solo 5 años
.Build();

DateTime? nextReminder = reminder.NextOccurrence(DateTime.Now);
Console.WriteLine($"Next annual reminder: {nextReminder:MMM dd, yyyy}");