Skip to content

feat(client): add datetime = :as_written for zoneless timestamps - #134

Closed
jd-lara wants to merge 1 commit into
JuliaComputing:mainfrom
QXT-Energy:jd/as-written-datetime
Closed

jd-lara wants to merge 1 commit into
JuliaComputing:mainfrom
QXT-Energy:jd/as-written-datetime

Conversation

@jd-lara

@jd-lara jd-lara commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a third value for the datetime option, :as_written. With :utc and
:zoned, a date-time with no offset decodes as UTC. Some producers write a wall
clock with no offset, and that value names no instant. Reading it as UTC gives a
wrong instant without an error.

With datetime = :as_written:

Input Decoded value Encoded output
2024-01-01T00:00:00 Dates.DateTime 2024-01-01T00:00:00.000
2024-01-01T00:00:00Z ZonedDateTime, UTC 2024-01-01T00:00:00.000+00:00
2024-01-01T02:00:00+02:00 ZonedDateTime, FixedTimeZone +02:00 2024-01-01T02:00:00.000+02:00

The field type is Union{Dates.DateTime,TimeZones.ZonedDateTime}. The generated
module has using TimeZones, as with :zoned.

Changes

  • src/planning.jl: the option check accepts :as_written. The field type comes
    from methods on Val(datetime).
  • src/runtime.jl: _decode_union sends a DateTime/ZonedDateTime union to
    _decode_as_written, after the nothing check. Trial decoding would convert an
    offset to UTC. _encode_as_written(::Dates.DateTime) writes no offset, and recurses
    into vectors and dicts.
  • ext/OpenAPITimeZonesExt.jl: _decode_as_written uses the same regex, millisecond
    truncation and DecodeError messages as the other decoders. It dispatches on the
    Z and offset captures. _encode_as_written(::ZonedDateTime) uses _encode.
  • src/client.jl, src/servergen.jl: _emit_model takes an encode keyword. Under
    :as_written, model fields, aliases, union values and additional properties encode
    through _encode_as_written. _encode(::Dates.DateTime) does not change, so :utc
    output still has Z.
  • docs/src/clients.md, docs/src/models.md: the new mode.

Limits

A date-time sent directly as a query, header or cookie parameter, or as a top-level
request body, still goes through _encode. Only model values use
_encode_as_written.

Tests

The new test set in test/runtime.jl covers:

  • decode of a zoneless value, Z, +02:00, -07:00, fractional seconds, and invalid
    text;
  • encode and round trip of each form;
  • a generated model with required, optional, nullable and array date-time fields;
  • that :utc and :zoned output does not change.

julia --project=. -e 'using Pkg; Pkg.test()': all test sets pass (1467 passes, 0
failures, 0 errors). generated runtime units: 418 of 418.

With :utc and :zoned, a date-time with no offset decodes as UTC. Some
producers write a wall clock with no offset, which names no instant.

The :as_written mode types the fields as
Union{Dates.DateTime,TimeZones.ZonedDateTime}. A value with no offset
decodes to a DateTime and encodes without an offset. A value with Z or
an offset decodes to a ZonedDateTime with that offset and encodes with
it. The generated models encode through _encode_as_written, so the
:utc behavior of _encode(::DateTime) does not change.
@jd-lara

jd-lara commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Closed. A new PR will add support for the registry format date-time-local instead of a custom datetime = :as_written mode.

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