Ir al contenido principal

Vali-Range

IValiRange proporciona un conjunto completo de operaciones con rangos de fechas construido sobre el struct DateRange. Admite creación, consultas, operaciones de conjuntos (unión, intersección), enumeración, división e iteración de días laborables con conciencia del calendario.

Instalación

dotnet add package Vali-Range

Registro

builder.Services.AddValiRange();
// o mediante el meta-paquete:
builder.Services.AddValiTempo();

Struct DateRange

DateRange es un tipo de valor que representa un intervalo de fechas cerrado [Start, End].

var range = new DateRange(new DateTime(2025, 1, 1), new DateTime(2025, 3, 31));
Console.WriteLine(range.Start); // 2025-01-01
Console.WriteLine(range.End); // 2025-03-31
Console.WriteLine(range.Duration); // TimeSpan de 90 días

Propiedades:

PropiedadTipoDescripción
StartDateTimeFecha de inicio inclusiva
EndDateTimeFecha de fin inclusiva
DurationTimeSpanEnd - Start
TotalDaysintNúmero de días calendario
IsEmptyboolVerdadero cuando Start == End o el rango es inválido

API de IValiRange

Create

Crea un DateRange a partir de fechas de inicio y fin:

DateRange r = range.Create(new DateTime(2025, 1, 1), new DateTime(2025, 12, 31));

Firma: DateRange Create(DateTime start, DateTime end)

LastUnits

Crea un rango que cubre las últimas N unidades antes de una fecha de referencia:

DateRange last30Days = range.LastUnits(30, TimeUnit.Days);
DateRange last3Months = range.LastUnits(3, TimeUnit.Months, referenceDate: DateTime.Today);

Firma: DateRange LastUnits(int count, TimeUnit unit, DateTime? referenceDate = null)

Nota: amount debe ser mayor que 0. Pasar 0 o un valor negativo lanza ArgumentException.

NextUnits

Crea un rango que cubre las próximas N unidades después de una fecha de referencia:

DateRange next7Days   = range.NextUnits(7, TimeUnit.Days);
DateRange nextQuarter = range.NextUnits(1, TimeUnit.Months, count: 3);

Firma: DateRange NextUnits(int count, TimeUnit unit, DateTime? referenceDate = null)

Nota: amount debe ser mayor que 0. Pasar 0 o un valor negativo lanza ArgumentException.

ThisMonth / ThisWeek / ThisQuarter / ThisYear

Métodos de fábrica convenientes para rangos comunes:

DateRange month   = range.ThisMonth();
DateRange week = range.ThisWeek();
DateRange quarter = range.ThisQuarter();
DateRange year = range.ThisYear();

Contains

Comprueba si una fecha u otro rango está completamente contenido:

bool has = range.Contains(myRange, new DateTime(2025, 6, 15));
bool sub = range.Contains(myRange, subRange);

Firma:

  • bool Contains(DateRange range, DateTime date)
  • bool Contains(DateRange range, DateRange other)

Overlaps

Comprueba si dos rangos se superponen (comparten al menos un día):

bool overlapping = range.Overlaps(rangeA, rangeB);

Firma: bool Overlaps(DateRange a, DateRange b)

IsContainedBy

Comprueba si el primer rango está completamente dentro del segundo:

bool inside = range.IsContainedBy(inner, outer);

Firma: bool IsContainedBy(DateRange range, DateRange container)

Intersection

Devuelve la parte superpuesta de dos rangos (o un rango vacío si no se superponen):

DateRange overlap = range.Intersection(rangeA, rangeB);
// → DateRange que cubre los días compartidos

Firma: DateRange Intersection(DateRange a, DateRange b)

Union

Fusiona dos rangos en el rango más pequeño que cubre ambos (no necesitan superponerse):

DateRange merged = range.Union(rangeA, rangeB);

Firma: DateRange Union(DateRange a, DateRange b)

Expand

Extiende un rango N unidades en uno o ambos extremos:

DateRange expanded = range.Expand(myRange, 7, TimeUnit.Days);
// Agrega 7 días en cada extremo

DateRange expandedStart = range.Expand(myRange, 7, TimeUnit.Days, expandStart: true, expandEnd: false);

Firma: DateRange Expand(DateRange range, int amount, TimeUnit unit, bool expandStart = true, bool expandEnd = true)

Shrink

Contrae un rango N unidades desde uno o ambos extremos:

DateRange shrunk = range.Shrink(myRange, 1, TimeUnit.Weeks);

Firma: DateRange Shrink(DateRange range, int amount, TimeUnit unit, bool shrinkStart = true, bool shrinkEnd = true)

Shift

Mueve un rango hacia adelante o hacia atrás en el tiempo:

DateRange shifted = range.Shift(myRange, 1, TimeUnit.Months); // hacia adelante
DateRange back = range.Shift(myRange, -2, TimeUnit.Weeks); // hacia atrás

Firma: DateRange Shift(DateRange range, int amount, TimeUnit unit)

IsAdjacent

Comprueba si dos rangos son adyacentes (el fin de uno es el día anterior al inicio del otro):

bool adj = range.IsAdjacent(rangeA, rangeB);

Firma: bool IsAdjacent(DateRange a, DateRange b)

Merge

Fusiona una colección de rangos superpuestos o adyacentes en rangos mínimos no superpuestos:

IEnumerable<DateRange> merged = range.Merge(listOfRanges);

Firma: IEnumerable<DateRange> Merge(IEnumerable<DateRange> ranges)

Gaps

Devuelve rangos que representan brechas entre una lista ordenada de rangos, delimitados por un rango contenedor:

var container = range.Create(new DateTime(2025, 1, 1), new DateTime(2025, 12, 31));
IEnumerable<DateRange> gaps = range.Gaps(listOfRanges, container);
// → Rangos para períodos dentro del contenedor no cubiertos por ningún rango de entrada

Firma: IEnumerable<DateRange> Gaps(IEnumerable<DateRange> ranges, DateRange container)

El parámetro container define el rango delimitador dentro del cual se calculan las brechas. Solo se devuelven los períodos no cubiertos que caen dentro de container.

EachDay / EachWeek / EachMonth

Enumera fechas dentro del rango a un paso dado:

foreach (DateTime day in range.EachDay(myRange))
Console.WriteLine(day);

foreach (DateTime weekStart in range.EachWeek(myRange))
Console.WriteLine(weekStart);

foreach (DateTime monthStart in range.EachMonth(myRange))
Console.WriteLine(monthStart);

Firmas:

  • IEnumerable<DateTime> EachDay(DateRange range)
  • IEnumerable<DateTime> EachWeek(DateRange range)
  • IEnumerable<DateTime> EachMonth(DateRange range)

EachWorkday

Enumera solo los días laborables (lun–vie) dentro del rango:

foreach (DateTime workday in range.EachWorkday(myRange))
Console.WriteLine(workday);

Firma: IEnumerable<DateTime> EachWorkday(DateRange range)

SplitByDay / SplitByWeek / SplitByMonth / SplitByQuarter

Divide un rango en sub-rangos:

IEnumerable<DateRange> days     = range.SplitByDay(myRange);
IEnumerable<DateRange> weeks = range.SplitByWeek(myRange);
IEnumerable<DateRange> months = range.SplitByMonth(myRange);
IEnumerable<DateRange> quarters = range.SplitByQuarter(myRange);

Ejemplo completo

public class ProjectAnalyzer(IValiRange range)
{
public void AnalyzeProject(DateTime projectStart, DateTime projectEnd)
{
var project = range.Create(projectStart, projectEnd);

Console.WriteLine($"Duration: {project.TotalDays} days");

// Dividir en sprints mensuales
var sprints = range.SplitByMonth(project).ToList();
Console.WriteLine($"Sprints: {sprints.Count}");

// Encontrar brechas de días festivos (si el servicio de feriados está disponible)
var workdays = range.EachWorkday(project).Count();
Console.WriteLine($"Workdays: {workdays}");

// Desglose trimestre por trimestre
foreach (var quarter in range.SplitByQuarter(project))
{
Console.WriteLine($" {quarter.Start:MMM dd}{quarter.End:MMM dd}: {quarter.TotalDays} days");
}
}
}