Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Jint.Tests/Runtime/DateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,32 @@ public void DateTimeMaxValueFlag()
jsDate = new JsDate(_engine, date);
Assert.Equal(DateFlags.None, jsDate._dateValue.Flags);
}

[Fact]
public void DstTransitionShouldUseCorrectOffset()
{
TimeZoneInfo nztz;
try
{
nztz = TimeZoneInfo.FindSystemTimeZoneById("New Zealand Standard Time");
}
catch (TimeZoneNotFoundException)
{
nztz = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Auckland");
}

var engine = new Engine(options => options.LocalTimeZone(nztz));

// NZDT (GMT+13) ends at 3:00 AM on the first Sunday of April 2025.
// At midnight April 6, we are still in NZDT (GMT+13).
var result1 = engine.Evaluate("new Date(2025, 3, 6, 0, 0, 0).toString()").AsString();
Assert.Contains("GMT+1300", result1);
Assert.Contains("Apr 06 2025 00:00:00", result1);

// NZDT (GMT+13) begins at 2:00 AM on the last Sunday of September 2025.
// At midnight Sep 28, we are still in NZST (GMT+12).
var result2 = engine.Evaluate("new Date(2025, 8, 28, 0, 0, 0).toString()").AsString();
Assert.Contains("GMT+1200", result2);
Assert.Contains("Sep 28 2025 00:00:00", result2);
}
}
8 changes: 7 additions & 1 deletion Jint/Native/Date/DatePrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,8 +1295,14 @@ private DatePresentation LocalTime(DatePresentation t)

internal DatePresentation Utc(DatePresentation t)
{
// t represents local time encoded as epoch milliseconds. GetUtcOffset treats
// its argument as a UTC instant, so a single-pass conversion uses the wrong
// DST offset near transitions. Use a two-pass approach matching the ES spec's
// UTC(t): first estimate UTC, then get the correct offset at that UTC instant.
var offset = _timeSystem.GetUtcOffset(t.Value).TotalMilliseconds;
return t - offset;
var estimatedUtc = (t - offset).Value;
var refinedOffset = _timeSystem.GetUtcOffset(estimatedUtc).TotalMilliseconds;
return t - refinedOffset;
}

private static int HourFromTime(DatePresentation t)
Expand Down
6 changes: 5 additions & 1 deletion Jint/Runtime/DefaultTimeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,12 @@ public virtual bool TryParse(string date, out long epochMilliseconds)

if (convertToUtcAfter)
{
// datePresentation is local time encoded as epoch milliseconds.
// Two-pass conversion to get correct DST offset near transitions.
var offset = GetUtcOffset(datePresentation.Value).TotalMilliseconds;
datePresentation -= offset;
var estimatedUtc = (datePresentation - offset).Value;
var refinedOffset = GetUtcOffset(estimatedUtc).TotalMilliseconds;
datePresentation -= refinedOffset;
}

epochMilliseconds = datePresentation.Value;
Expand Down