I am not able to understand one specific part of doc provided for the plusDays()
method in the ZonedDateTime
class. Doc states:
public ZonedDateTime plusDays(long days)
Returns a copy of this
ZonedDateTime
with the specified number of days added.This operates on the local time-line,
adding days
to the local date-time. This is then converted back to aZonedDateTime
, using the zone ID to obtain the offset.When converting back to
ZonedDateTime
, if the local date-time is in an overlap, then the offset will be retained if possible, otherwise the earlier offset will be used. If in a gap, the local date-time will be adjusted forward by the length of the gap.This instance is immutable and unaffected by this method call.
Parameters:
days
- the days to add, may be negativeReturns:
a
ZonedDateTime
based on this date-time with the days added, not nullThrows:
DateTimeException
- if the result exceeds the supported date range
How I understand this: Assume we have ZonedDateTime
object representing September 4, 2022 6 PM in America/New_York TimeZone
. So this method will first convert it to LocalDateTime, that is, it will lose timezone information and just retain September 4, 2022 6 PM
. It will add some number of days to it, let's say 7, so that the result is September 11, 2022 6 PM
, and now it will convert it back to ZonedDateTime
object by providing back the information related to timezone.
However, I am not able to understand the latter part of documentation, that is,
When converting back to
ZonedDateTime
, if the local date-time is in an overlap, then the offset will be retained if possible, otherwise the earlier offset will be used. If in a gap, the local date-time will be adjusted forward by the length of the gap.
What do they mean by "local date-time is in an overlap"? "...then the offset will be retained if possible, otherwise the earlier offset will be used." - what are these two different offsets? "If in a gap..." - what is this gap?
A "gap" is the period of local (wall clock) time that is skipped over when advancing the clock at the start of daylight saving time. An "overlap" is the period of local time that is repeated when turning the clocks back at the end of daylight saving time.
The "gap" and "overlap" terms are defined in the class-level Javadoc of the ZonedDateTime
class:
This class handles conversion from the local time-line of
LocalDateTime
to the instant time-line ofInstant
. The difference between the two time-lines is the offset fromUTC/Greenwich
, represented by aZoneOffset
.Converting between the two time-lines involves calculating the offset using the rules accessed from the ZoneId. Obtaining the offset for an instant is simple, as there is exactly one valid offset for each instant. By contrast, obtaining the offset for a local date-time is not straightforward. There are three cases:
- Normal, with one valid offset. For the vast majority of the year, the normal case applies, where there is a single valid offset for the local date-time.
- Gap, with zero valid offsets. This is when clocks jump forward typically due to the spring daylight savings change from "winter" to "summer". In a gap there are local date-time values with no valid offset.
- Overlap, with two valid offsets. This is when clocks are set back typically due to the autumn daylight savings change from "summer" to "winter". In an overlap there are local date-time values with two valid offsets.
Let's use your specific example of the America/New_York
time zone. Per timeanddate.com, the daylight saving time changes in New York for 2022 are:
Mar 13, 2022 - Daylight Saving Time Started
When local standard time was about to reach
Sunday, March 13, 2022, 2:00:00 am clocks were turned forward 1 hour to
Sunday, March 13, 2022, 3:00:00 am local daylight time instead.Nov 6, 2022 - Daylight Saving Time Ends
When local daylight time was about to reach
Sunday, November 6, 2022, 2:00:00 am clocks were turned backward 1 hour to
Sunday, November 6, 2022, 1:00:00 am local standard time instead.
Therefore, there are no times between 2:00 and 2:59 on March 13 in the New York time zone. 1:59 occurs in standard time. When that minute ends, no 2:00 hour occurs, and instead the local time jumps to 3:00 daylight time.
Additionally, the times between 1:00 and 1:59 occur twice on November 6: one in daylight time and then one in standard time.
ZonedDateTime
exampleZoneId zone = ZoneId.of("America/New_York");
// 2022-03-13T03:15:30-04:00[America/New_York] (no 2:15)
System.out.println(
ZonedDateTime.of(LocalDateTime.parse("2022-03-12T02:15:30"), zone)
.plusDays(1));
Since 2:15 AM on March 12 doesn't exist and is within a 1-hour gap, the following logic you quoted applies, adding 1 hour to the local time:
If in a gap, the local date-time will be adjusted forward by the length of the gap.
Therefore, 3:15 AM is used when adding 1 day to March 11 at 2:15 AM.
ZoneId zone = ZoneId.of("America/New_York");
// 2022-11-06T01:15:30-04:00[America/New_York] (First 1:15)
System.out.println(
ZonedDateTime.of(LocalDateTime.parse("2022-11-05T01:15:30"), zone)
.plusDays(1));
// 2022-11-06T01:15:30-05:00[America/New_York] (Second 1:15)
System.out.println(
ZonedDateTime.of(LocalDateTime.parse("2022-11-05T01:15:30"), zone)
.plusDays(1).plusHours(1));
Since 1:15 AM is during an overlap — 1:15 AM occurs twice on November 6 — the following logic you quoted applies, using the same -04:00 zone offset as 1:15 AM on November 5:
if the local date-time is in an overlap, then the offset will be retained if possible
Therefore, adding 1 day to November 5 at 1:15 uses the first 1:15 on November 6. This is made more evident by the second call, which shows that adding an hour to this timestamp returns the second 1:15 of November 6. The fact that these are different points on the timeline despite both being 1:15 local time is evident by their differing zone offsets: -04:00 & -05:00.