Skip to content

feat(client): map date-time-local and require an offset for zoned date-time - #135

Open
jd-lara wants to merge 1 commit into
JuliaComputing:mainfrom
QXT-Energy:jd/date-time-local
Open

jd-lara wants to merge 1 commit into
JuliaComputing:mainfrom
QXT-Energy:jd/date-time-local

Conversation

@jd-lara

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

Copy link
Copy Markdown
Contributor

Summary

The OpenAPI Format Registry
defines date-time-local as an RFC 3339 date-time without a time zone. OpenAPI.jl did not
map it, so the field became a String. Under datetime = :zoned, a date-time with no
offset decoded as UTC, which RFC 3339 does not allow.

This PR replaces #134 (closed), which proposed a custom datetime = :as_written mode.
It uses the registry format instead, so the schema states the form of each timestamp.

Behavior

Format datetime = :utc (default) datetime = :zoned
date-time Dates.DateTime, offset normalized to UTC (unchanged) ZonedDateTime; Z or an offset is now required
date-time-local String (unchanged) Dates.DateTime, a wall clock with no offset
anyOf of both wrapper struct, as before wrapper whose value is either type; the branch is chosen by schema

Under :utc, Dates.DateTime already means a UTC instant, so date-time-local stays a
String there. Under :zoned, date-time is a ZonedDateTime, so a Dates.DateTime
can only mean the wall clock.

Changes

  • src/planning.jl: format: date-time-local maps through _local_datetime_type!,
    dispatched on Val(datetime).
  • src/client.jl, src/servergen.jl: _emit_model takes an encode keyword. A :zoned
    module encodes model fields, aliases, union values and additional properties through
    _encode_zoned. :utc output does not change.
  • src/runtime.jl: _encode_zoned(::Dates.DateTime) writes yyyy-mm-ddTHH:MM:SS.sss
    with no offset, and recurses into vectors, tuples, dicts and named tuples. Any other
    value goes to _encode.
  • ext/OpenAPITimeZonesExt.jl: the ZonedDateTime decoder requires Z or an offset.
    Its regex ends with \z, so a trailing newline is rejected. PCRE $ matches before
    one.
  • CONTRACT_VERSION is 5, because generated code imports _encode_zoned. Contract 3 and
    4 modules still load.
  • Docs: clients.md, models.md, artifacts.md, servers.md, and the client
    docstring.

Breaking change for :zoned users

A :zoned module rejects a date-time value with no offset. Before, it read the value as
UTC. The existing test that pinned that behavior now asserts a DecodeError. A producer
that writes zoneless values should declare them date-time-local.

Limits

A date-time-local value sent directly as a query, header or cookie parameter, or as a
top-level request body, still goes through _encode / _scalar, which add Z. Only
model values use _encode_zoned.

The release that carries contract 5 needs a version bump. I left that to you.

Tests

test/runtime.jl has a new test set, "date-time-local is a wall clock under datetime =
:zoned". It covers:

  • :utc keeps String;
  • :zoned field types for a required, an array and an anyOf field;
  • decode and encode of each form;
  • anyOf branch selection by schema pattern;
  • rejection of an offset on a wall-clock field.

The :zoned test set now asserts that a zoneless value and a trailing newline are
rejected. test/generated_contract.jl moves its tripwire to contract 5 and checks that
contracts 3, 4 and 5 load.

julia --project=. -e 'using Pkg; Pkg.test()': "OpenAPI tests passed", 1442 passes, 0
failures, 0 errors. generated runtime units: 392 of 392.

…e-time

The OpenAPI Format Registry defines date-time-local as an RFC 3339
date-time without a time zone. It fell through to String, and under
datetime = :zoned a date-time with no offset decoded as UTC.

- Under :zoned, date-time-local maps to Dates.DateTime, a wall clock.
  Generated :zoned models encode through _encode_zoned, which writes a
  DateTime with no offset. Under :utc, date-time-local stays String,
  because DateTime already means a UTC instant there.
- The ZonedDateTime decoder requires Z or an offset, as RFC 3339
  date-time does. The regex ends with \z, so a trailing newline fails.
- CONTRACT_VERSION is 5: generated code imports _encode_zoned.

This branch has not been deployed

No deployments
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