Skip to content

Windows: local DateTime offset ignores the system time zone when TZ is set #64559

Open

Description

@edde746

On Windows, when the TZ environment variable holds a value the MSVC CRT cannot parse (for example an IANA name such as America/Edmonton), the local DateTime offset ignores the system time zone. A user- or machine-scope TZ reaches every process, including GUI apps launched from Explorer.

Reported against a Flutter app (Dart 3.13.1 via Flutter 3.47.1, Windows 11 build 26300): system zone Mountain Standard Time with DST active, user-scope TZ=America/Edmonton. DateTime.now() rendered 17:05 when local time was 10:05 MDT (16:05 UTC). Removing TZ fixed it: edde746/plezy#2637

Cause

OS::GetTimeZoneOffsetInSeconds in runtime/vm/os_win.cc combines two sources:

  • Base offset: -_timezone after tzset(). The CRT parses TZ as tzn[+|-]hh[:mm[:ss]][dzn]. For America/Edmonton it takes Ame as the name and wcstol("rica/Edmonton") = 0, so _timezone = 0.
  • DST flag and amount: LocalTime() via GetTimeZoneInformationForYear / SystemTimeToTzSpecificLocalTime, and GetTimeZoneInformation().DaylightBias. Both ignore TZ.

Result: the offset is +1:00 during the system zone's DST and 0:00 otherwise, whatever the system zone is. timeZoneName is still correct because it comes from GetTimeZoneInformation.

#17031 and #17032 reported the same symptom in 2014 (also TZ=America/Edmonton, set by Cygwin) and were closed as a Cygwin issue. Nothing about it is Cygwin-specific: any process with an unparseable TZ is affected.

Repro

void main() {
  final now = DateTime.now();
  print('$now ${now.timeZoneOffset} ${now.timeZoneName}');
}
set TZ=America/Edmonton
dart run tz.dart

Expected: the offset of the Windows system zone, matching the taskbar clock.
Actual: 1:00:00 during DST, 0:00:00 otherwise. This is derived from the code above and matches the Flutter report; I have not run this exact program.

Suggested fix

Take the base offset from the TIME_ZONE_INFORMATION that LocalTime() already fetches, -(Bias + DaylightBias) * 60 in DST and -(Bias + StandardBias) * 60 otherwise, instead of the CRT's _timezone. The offset, DST flag, and timeZoneName would then share one source. If honouring TZ on Windows is intended, it has to be applied consistently instead, with the DST flag and amount also coming from the CRT.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions