DateTime ↔️ timezone package: Interoperability of UTC, Local, and IANA Zones
0 reputation · 17 Mar 2023, 15:10 UTC
0 reputation · 17 Mar 2023, 15:10 UTC
Convert a core DateTime instance into a timezone‑aware representation using the timezone package, while preserving accurate offsets, including during DST transitions.
DateTime class exposes only UTC or the system’s local zone; it has no IANA zone identifier.tzdata via loadTimeZones(), otherwise a runtime exception occurs.toLocal() on a timezone‑converted instance.tzdata version differs between environments, making deterministic behavior hard to guarantee.DateTime to a TimeZone object?DateTime to an IANA zone during a DST transition?tzdata when using the timezone package in production apps?Core DateTime has no IANA identifier. Preserve zone information by never relying on DateTime.isUtc / toLocal() alone. Store the IANA ID alongside the instant and reconstruct with timezone after loadTimeZones().
Confirmed fact: DateTime only knows UTC or the OS local offset. Once a DateTime is created, its original IANA zone is lost.
Likely explanation: The safe mapping is:
timezone only when you need zone-aware arithmetic.Steps for this case:
await timezone.initializeTimeZones();final loc = timezone.getLocation('America/New_York'); final tz = timezone.TZDateTime.from(dateTimeUtc, loc);DateTime and you know its source zone: final tz = timezone.TZDateTime.from(dateTimeLocal, loc); Do not use loc = timezone.getLocation(timezone.getLocalLocation().name) for business logic.dateTime.toLocal() after a timezone conversion; it re-applies the OS local zone and discards the IANA ID.If the source IANA zone is unknown for a local DateTime, zone information cannot be recovered. That is a data-model limitation, not a library bug.
Confirmed fact: Offsets depend on the tzdata version loaded and the platform’s ICU/glibc resolution.
Deterministic steps:
loadTimeZones() before any conversion and never call findLocal() for server-side logic.TZDateTime.from(utcInstant, location). This yields the same offset on Android, iOS, macOS and Linux when the same tzdata version is used.Uncertainty: Platform differences in system local zone resolution remain if you mix toLocal() with timezone objects. Avoid that mix.
Confirmed fact: Using timezone without loading tzdata throws at runtime.
Production best practices:
timezone and tzdata packages and schedule updates for tz database releases.TZDateTime for a known zone and asserts expected offsets for a fixed UTC instant.getLocalLocation() for persistent data; use it only for UI display.Missing diagnostic detail that changes recommendation: Are your incoming core DateTime values always UTC from the backend, or are some created locally without a known IANA ID? If local without ID, you need to change ingestion to capture the zone ID; otherwise zone cannot be preserved.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 17 Mar 2023, 15:37 UTC
A core DateTime built from local wall time remembers only the device’s current offset, not the zone where that wall time was observed. Calling toUtc() re-interprets the wall time through the OS local zone, which can produce the wrong instant when the source zone differs from the device zone or during a DST gap or overlap. Capture the source Location or fixed offset at creation, convert to UTC with that anchor, then build the target TZDateTime from that instant. Verify by asserting that millisecondsSinceEpoch is unchanged after a UTC-to-target-zone round-trip for your pinned tzdata version.