[browser] Fix DateTime marshalling to be timezone-independent#127340
Merged
pavelsavara merged 2 commits intodotnet:mainfrom Apr 24, 2026
Merged
[browser] Fix DateTime marshalling to be timezone-independent#127340pavelsavara merged 2 commits intodotnet:mainfrom
pavelsavara merged 2 commits intodotnet:mainfrom
Conversation
Contributor
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes DateTime marshalling between .NET and JavaScript in browser/WASM so roundtrips are timezone-independent, avoiding tick shifts when the local timezone is non-UTC (notably exposed with newer Emscripten behavior around TZ).
Changes:
- Marshal
DateTime/DateTime?to JS using a zero-offsetDateTimeOffset(value.Ticks, TimeSpan.Zero)to avoid applying the local timezone offset. - Configure the WASM unit test run to use a non-UTC timezone (
TZ=Europe/Berlin) so CI exercises the previously-masked code paths.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.DateTime.cs | Makes DateTime and nullable DateTime marshalling interpret ticks as UTC (offset 0) when converting to Unix epoch milliseconds. |
| src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj | Sets TZ=Europe/Berlin for WASM test execution to validate behavior in a non-UTC timezone in CI. |
maraf
approved these changes
Apr 24, 2026
Member
maraf
left a comment
There was a problem hiding this comment.
Looks good to me and my copilot 👍
Member
Author
|
/ba-g known CI badexit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ToJS(DateTime)usednew DateTimeOffset(value)which applies the local timezone offset whenDateTime.KindisUnspecifiedorLocal. TheToManagedside always returns.UtcDateTime(offset=0). This asymmetry causes the roundtrippedDateTimeto shift by the local UTC offset.With Emscripten 3.1.56, the WASM environment effectively ignored the
TZenvironment variable, so local time always equaled UTC — masking the bug. Emscripten 5.0.6 correctly processesTZ, exposing the issue on any machine with a non-UTC timezone.Failing tests (CET/CEST, +02:00)
DateTimeMaxValueBoundaryCondition— 1h shift (CET for Dec 31)DateTimeMarshallingLosesMicrosecondComponentPrecisionLoss— 2h shift (CEST for Apr 1)DateTimeMinValueBoundaryCondition—ArgumentOutOfRangeException(MinValue + offset underflows year 0)JsExportFuncOfDateTime_Argument_OverflowNETDateTime— MaxValue+60s no longer overflowsJsExportFunctionDateTimeDateTime—DateTime.Nowroundtrip loses offset hoursFix
Replace
new DateTimeOffset(value)withnew DateTimeOffset(value.Ticks, TimeSpan.Zero)in bothToJS(DateTime)andToJS(DateTime?). This reinterprets ticks as UTC, matching theToManagedside which already returns.UtcDateTime.The
DateTimeOffsetoverloads are unaffected —ToUnixTimeMilliseconds()already accounts for the stored offset.Before (broken in non-UTC timezone)
After (timezone-independent)
Test coverage
Added
TZ=Europe/BerlintoWasmXHarnessMonoArgsin the test csproj so CI (which runs in UTC) exercises the same non-UTC code paths as local development.