Skip to content

Add ZonedTimestamp{P,Z}: a UTC Timestamp in a named time zone - #5

Merged
quinnj merged 2 commits into
mainfrom
jq/zoned-timestamp
Sep 25, 2026
Merged

quinnj merged 2 commits into
mainfrom
jq/zoned-timestamp

Conversation

@quinnj

@quinnj quinnj commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Adds Durations.ZonedTimestamp{P,Z} for Arrow timestamps with a timezone. It stores one utc::Timestamp{P} field; Z is the zone name as a Symbol. For each of Arrow's four timestamp units, values are 8-byte isbits and preserve every Int64 count, including unknown zone names.

The storage split follows Arrow's timestamp specification: an absent or empty zone uses Timestamp{P} for local clock time; a nonempty zone, including UTC, uses ZonedTimestamp{P,Z} for a UTC instant. Changing a nonempty zone changes metadata, not the stored count. Nullability remains in Arrow's validity bitmap.

Behavior

  • ZonedTimestamp{P,Z}(ts) interprets local time; (ts, UTC) stores UTC. The corresponding Timestamp(zt) / Timestamp(zt, UTC) conversions are explicit.
  • Comparison, hashing, subtraction, and time-period arithmetic use UTC and work without zone rules. A zoned value compares unequal to a timezone-free Timestamp, DateTime, or Date.
  • UTC and fixed offsets work without TimeZones.jl. The optional extension supplies named-zone rules, DST fold/gap handling, and ZonedDateTime conversions. Calendar operations use local time and can reject ambiguous, nonexistent, or out-of-range times.
  • Text parsing validates offset fields and checks UTC overflow. Time-period rounding reuses Timestamp's wide calculations, preserves repeated-hour offsets, and rejects unrepresentable results. repr round-trips even on older Dates parsers.
  • Custom fixed-zone labels become portable numeric offsets. Custom offsets with seconds cannot be encoded as an Arrow timezone offset and are rejected. Named-zone local operations respect the installed timezone database's cutoff.

Arrow integration

The Arrow 2.x adapter preserves both types through extension metadata. Arrow 3.x can load Durations without that legacy adapter. The native reader/writer mappings belong in apache/arrow-julia#609; this PR does not change that branch or claim that its public facade already returns these types.

test/arrow3.jl checks the actual #609 IPC reader/writer with typed buffers, all four units, absent/empty/UTC/fixed/named/unknown zones, file and stream formats, nulls, and extreme counts. It runs instead of the Arrow 2.x adapter tests when the test environment has Arrow 3.x.

Validation

Rebased on the released Durations 1.3.0 implementation. No version bump or Timestamp implementation changes are included.

  • Julia 1.10.12 with Arrow 2.x: 7,952 checks passed.
  • Julia 1.13.0 with Arrow 2.x: 7,960 checks passed.
  • Updated Julia Dates backend: 7,963 checks passed.
  • Julia 1.13.0 with Arrow #609 at 51ccbcc: 8,321 checks passed, including 482 Arrow 3 IPC/storage checks.
  • TimeZones tests compare every installed zone near its transitions, with separate known zero-length transition cases, plus fold/gap, precision, hashing, and range-limit regressions.
  • All nine CI checks passed on 1c64bd1: eight platform/version jobs plus trim compilation and execution.

Original implementation prepared with Claude Code.

Co-authored by Codex

quinnj and others added 2 commits September 24, 2026 22:52
- `ZonedTimestamp{P,Z}` stores one `utc::Timestamp{P}` and names its zone with
  the Symbol type parameter `Z`, so a column has the 8-byte layout of an Arrow
  timestamp with a time zone and reinterprets to and from Arrow buffers in place.
- Comparison, hashing, subtraction, and time-period arithmetic use the UTC time
  and work for every zone name. Local fields, date-period arithmetic, rounding,
  and adjusters use zone rules: "UTC" and "+HH:MM"/"-HH:MM" offsets are built
  in, and the new DurationsTimeZonesExt supplies every name TimeZones.jl knows,
  plus conversions and comparisons with ZonedDateTime.
- A repeated local time throws AmbiguousTimeError unless `occurrence` picks one;
  a skipped one throws NonExistentTimeError. Text is RFC 9557 style:
  "2026-11-01T01:30:00-07:00[America/Denver]".
- DurationsArrowExt writes ZonedTimestamp columns as Arrow timestamps with time
  zone String(Z), tagged JuliaLang.Durations.ZonedTimestamp.
- Tests compare the zone rules of all 597 TimeZones.jl zones with ZonedDateTime.
  Next to a zero-length transition (5 zones) TimeZones.jl reports existing local
  times as nonexistent, so those cases are pinned to Python zoneinfo values.
- The trim workload covers UTC and fixed-offset zones.
- Version 1.3.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@quinnj
quinnj marked this pull request as ready for review September 25, 2026 20:11
@quinnj
quinnj merged commit 6c75273 into main Sep 25, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant