Skip to content

BUG - GetPreviousOccurrence returns incorrect value around daylight savings time #92

@DevJasperNL

Description

@DevJasperNL

When running the following:

using Cronos;

// Setup timezone and expression
var dutchTz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
var expression = CronExpression.Parse("30 2 * * *"); // 02:30 AM daily

// Starting point: 2024-03-29 03:35:00 (Two days before the 2AM -> 3AM jump)
var localStart = new DateTime(2024, 3, 29, 3, 35, 0);
var utcStart = TimeZoneInfo.ConvertTimeToUtc(localStart, dutchTz);

Console.WriteLine($"Start (Local): {localStart} | (UTC): {utcStart}\n");
Console.WriteLine($"{"Direction",-15} | {"UTC Occurrence",-25} | {"Local (NL) Equivalent"} ");
Console.WriteLine(new string('-', 75));

// 1. Sample 3 Next Occurrences
var next1 = expression.GetNextOccurrence(utcStart, dutchTz)!;
var next2 = expression.GetNextOccurrence(next1.Value, dutchTz)!;
var next3 = expression.GetNextOccurrence(next2.Value, dutchTz)!;

PrintRow("Next 1", next1, dutchTz);
PrintRow("Next 2", next2, dutchTz);
PrintRow("Next 3", next3, dutchTz);

Console.WriteLine(new string('-', 75));

// 2. Go back using Previous Occurrences
var prev1 = expression.GetPreviousOccurrence(next3.Value, dutchTz)!;
var prev2 = expression.GetPreviousOccurrence(prev1.Value, dutchTz)!;

PrintRow("Prev (from N3)", prev1, dutchTz);
PrintRow("Prev (from P1)", prev2, dutchTz);

void PrintRow(string label, DateTimeOffset? utcTime, TimeZoneInfo tz)
{
    if (utcTime == null) return;
    var local = TimeZoneInfo.ConvertTimeFromUtc(utcTime.Value.UtcDateTime, tz);
    Console.WriteLine($"{label,-15} | {utcTime.Value.UtcDateTime,-25:yyyy-MM-dd HH:mm:ss} | {local:yyyy-MM-dd HH:mm:ss}");
}

It results in:

Direction UTC Occurrence Local (NL) Equivalent
Next 1 2024-03-30 01:30:00 2024-03-30 02:30:00
Next 2 2024-03-31 01:00:00 2024-03-31 03:00:00
Next 3 2024-04-01 00:30:00 2024-04-01 02:30:00
Prev (from N3) 2024-03-31 00:59:59 2024-03-31 01:59:59
Prev (from P1) 2024-03-30 01:30:00 2024-03-30 02:30:00

@odinserj @israellot

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions