Skip to content

[BUG][DART] format: date fields shift one day back in timezones east of UTC #24703

Description

@eirikb

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

The dart generator calls .toUtc() on format: date fields before formatting them, and that shifts the date one day back in any UTC+X timezone.

_dateFormatter is DateFormat('yyyy-MM-dd'), which just formats whatever y/m/d the DateTime already carries. It does no timezone conversion. So the .toUtc() there does nothing except roll the clock back past midnight before the date is read.

Going the other way, mapDateTime parses the bare wire value "2026-09-12" with DateTime.tryParse, which gives you local midnight. Round trip in Oslo:

"2026-09-12" -> DateTime 2026-09-12 00:00 local -> toUtc() 2026-09-11 22:00Z -> "2026-09-11"

It is stable in UTC, which I bet is why nobody noticed. We only found it because a payment deadline got printed one day early on a physical parking fine, and a read-modify-write on the same field walked the stored date one day earlier on every save.

FWIW dart-dio already handles this correctly with its own Date class (plain year/month/day ints, no DateTime), so the two dart generators disagree today.

openapi-generator version

Reproduced on master (66f6973, 2026-08-13). Also present in 7.19 and 7.24, so not a regression, looks like it has been there a long time.

OpenAPI declaration file content or url
openapi: 3.0.0
info:
  title: Date only
  version: 1.0.0
paths: {}
components:
  schemas:
    DateOnlyModel:
      type: object
      required:
        - requiredDate
      properties:
        requiredDate:
          type: string
          format: date
Generation Details
openapi-generator-cli generate \
  -i spec.yaml \
  -g dart \
  -o out
Steps to reproduce
  1. Generate with the spec above.
  2. Look at out/lib/model/date_only_model.dart:
json[r'requiredDate'] = _dateFormatter.format(this.requiredDate.toUtc());
  1. Round trip a value with TZ=Europe/Oslo:
final parsed = mapDateTime({'requiredDate': '2026-09-12'}, 'requiredDate', '');
print(DateOnlyModel(requiredDate: parsed!).toJson()['requiredDate']);

Actual: 2026-09-11
Expected: 2026-09-12

With TZ=UTC you get 2026-09-12, so it only shows up east of UTC. West of UTC it is fine at midnight, but a non-midnight value shifts forward a day.

Related issues/PRs

Could not find one for dart. Same bug class has been reported for other generators, at least #24635 and #12490 (both typescript-fetch).

Suggest a fix

Drop the .toUtc() from the four {{#isDate}} sites in dart2/serialization/native/native_class.mustache (lines 85, 88, 123, 126 on master). The {{#isDateTime}} branch right next to it should keep it, an instant genuinely needs UTC, a calendar date does not.

Should be safe both ways: toUtc() returns this when the DateTime is already UTC, so for UTC callers removing it is a no-op, and for local callers it removes a wrong shift.

I have a fix with a regression test and regenerated samples ready, will open a PR shortly.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions