Repository navigation
Keep the later offset of a ZonedDateTime in an overlap - #1532
Merged
theigl merged 1 commit intoOct 20, 2026
Merged
Conversation
ZonedDateTimeSerializer wrote the date, time and zone of a ZonedDateTime and read it with ZonedDateTime.of, which takes the earlier offset in an overlap, when clocks are set back. A ZonedDateTime with the later offset, eg 2026-11-01T01:30-05:00[America/New_York], was read with the earlier one, -04:00, an instant one hour earlier. The offset of such a ZonedDateTime is written now too, after a null zone ID, which Kryo 5 never wrote, and read with ZonedDateTime.ofLocal, which uses it if it is valid. Other ZonedDateTimes are written as before, so Kryo 6 reads all ZonedDateTimes written by Kryo 5. With the Kryo 5 format, they are written like Kryo 5 wrote them.
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved blocking issues were identified.
No open issues 🎉
What changed in this PR
Preserves the later offset when serializing ZonedDateTime values in overlaps while maintaining Kryo 5 compatibility.
Changes:
- Adds overlap-offset preservation for Kryo 6.
- Adds round-trip and compatibility tests.
- Documents the format change.
| File | Description |
|---|---|
test/com/esotericsoftware/kryo/serializers/TimeSerializersTest.java |
Tests overlap round trips and invalid offsets. |
test/com/esotericsoftware/kryo/Kryo5RoundTripTest.java |
Verifies Kryo 5 format compatibility. |
src/com/esotericsoftware/kryo/serializers/TimeSerializers.java |
Preserves later overlap offsets. |
MIGRATION.md |
Documents the format and behavior change. |
Review effort: Lite
Collaborator
|
Thanks again @CCCLL! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1531.
ZonedDateTimeSerializerwrote the date, time and zone of aZonedDateTimeand read it withZonedDateTime.of, which takes the earlier offset in an overlap, when clocks are set back. AZonedDateTimewith the later offset, eg2026-11-01T01:30-05:00[America/New_York], was read as2026-11-01T01:30-04:00[America/New_York], an instant one hour earlier, without an exception.Like for locales with a script or extensions (#1334, #1525), a null zone ID, which Kryo 5 never wrote, now marks an offset. It is written before the zone ID only for a
ZonedDateTimewith the later offset in an overlap. The offset is read withZonedDateTime.ofLocal, which uses it if it is valid for the date and time in the zone, and else the earlier offset as before, eg if the zone's rules changed. OtherZonedDateTimes are written as before, so Kryo 6 reads allZonedDateTimes written by Kryo 5 and the golden files are unchanged. WithFormat.KRYO5, they are written like Kryo 5 wrote them, without the offset. MIGRATION.md has an entry.To find out if a
ZonedDateTimehas the later offset, writing it now looks up the zone's transition for its date and time. In a rough loop on JDK 25, writing aZonedDateTimewith a region zone took about 31 ns instead of 21 ns. Reading is unchanged. Always writing the offset would avoid the lookup, but it would make everyZonedDateTimelonger and still need a marker to read the golden files.Tests:
TimeSerializersTest.testZonedDateTimeOverlapround trips both offsets of an overlap and checks that an offset that is not valid for the date and time is not used.Kryo5RoundTripTest.testZonedDateTimeLaterOffsetAtOverlapchecks that the Kryo 5 format writes the same bytes as Kryo 5 and that Kryo 5 reads them. The tests fail without the fix, and also when the format check, the read of the offset or the check for the later offset is removed.I also checked all zones, around every transition from 1900 to 2100 and at random instants, 838,235 values with
OutputandByteBufferOutput, 117,381 of them with the later offset in an overlap: Kryo 6 round trips each value, writes the same bytes as Kryo 5 except for the later offsets, and writes the same bytes as Kryo 5 with the Kryo 5 format.mvn -pl main testpasses on JDK 17, 21 and 25 (built with JDK 25), including the Android API check.mvn -pl main formatter:validatepasses.master has the same bug, but the fix changes the format, so this is for kryo-6 only.