Retain original offset or normalize to UTC for xs:dateTime values?
0 reputation · 04 Jun 2022, 21:21 UTC
0 reputation · 04 Jun 2022, 21:21 UTC
The goal is to determine whether an XML serializer should preserve the original timezone offset embedded in an xs:dateTime value or convert the instant to UTC and serialize it with the ‘Z’ designator. Preserving the offset keeps the source civil time but can produce multiple lexical representations for the same instant when a region observes daylight‑saving time, complicating equality checks and indexing. Converting to UTC simplifies comparison and storage, yet discards the original zone information, requiring external metadata if the local time must be displayed later. Different XML binding frameworks (JAXB, .NET XmlSerializer, lxml) currently adopt opposite defaults, creating interoperability gaps when documents are exchanged across systems.
Which approach provides reliable round‑trip behavior across JAXB, .NET XmlSerializer, and lxml without requiring application‑specific zone data? How should a serializer handle timestamps that fall inside a DST transition when the original offset is retained? What minimal metadata, if any, should be added to a UTC‑normalized xs:dateTime to allow reconstruction of the original civil time?
34775 reputation · 05 Jun 2022, 03:34 UTC
Normalize to UTC and serialize with the Z designator as the canonical instant, and carry the original offset (or better, an IANA zone identifier) as a small piece of sibling metadata when the original civil time must be reconstructed. Retaining the raw offset alone cannot give reliable round-trip behavior across JAXB, .NET XmlSerializer, and lxml, because the three frameworks do not share a default, and an offset by itself is not enough information to survive daylight-saving transitions.
An offset like +02:00 records what the clock said at one moment, not which region produced it. Two different zones can share an offset in summer and diverge in winter, and a single civil time near a spring-forward transition may be invalid or ambiguous. If you store only 2024-03-10T02:30:00+01:00 you have pinned an instant, but you cannot later answer "what local time did the user see?" for a different date in the same zone, because the offset says nothing about the zone's DST rules. Equality and indexing also suffer: the same instant appears as many distinct lexical forms, and naive string comparison of xs:dateTime values with different offsets is a well-known source of duplicate-key bugs.
These are well-established behaviors, but defaults vary by major version, so verify against your runtime (JAXB 2.x vs 3.x, .NET Framework vs .NET 8+, lxml 4.9+):
XmlGregorianCalendar with its lexical offset preserved when one is set, but adapters that route through java.util.Date or Instant silently drop it. If you need a guaranteed form, write an explicit XmlAdapter that formats UTC with Z.Z for DateTimeKind.Utc, the machine's current offset for Kind.Local, and no offset for Kind.Unspecified. The Kind.Local case is the dangerous one: the serialized offset reflects the serializing machine, not the original capture zone, so round-trip loss happens even if you never intended to normalize.Because lxml preserves and .NET Kind.Local rewrites, the only representation all three handle identically is UTC with Z.
If you retain the original offset, a timestamp inside a spring-forward gap (a local time that never occurred) or a fall-back overlap (a local time that occurred twice) must be resolved before serialization. The safe rule: convert the instant to UTC at capture time, when the zone rules are known, and never try to re-derive the offset later from the civil time alone. Serializing the ambiguous local time with whichever offset "looks right" produces an instant that may be off by an hour and cannot be detected downstream.
Two sibling values are sufficient and framework-neutral:
<ts>2024-03-15T08:30:00Z</ts><tsZone>Europe/Berlin</tsZone>Prefer the zone ID over the raw offset: the offset (+01:00) reconstructs only that one civil time, while the zone ID reconstructs the civil time for any date, including future DST rule changes. If schema changes are impossible, an attribute (ts zone="Europe/Berlin") works equally well. No XML Schema mechanism preserves both instant and original civil representation by itself — this metadata is the minimal interoperable contract.
Round-trip a known offset value (2024-03-15T10:30:00+02:00) through each framework's defaults and compare instant and offset after deserialization. Then test a fall-back overlap timestamp and confirm the UTC instant is correct and the zone metadata reproduces the original wall clock. If any framework emits the machine-local offset instead of what you stored, enforce the format with an explicit adapter or DateTimeKind.Utc rather than relying on defaults.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.