As explained by others, there's a time discontinuity there. There are two possible timezone offsets for 1927-12-31 23:54:08
at Asia/Shanghai
, but only one offset for 1927-12-31 23:54:07
. So, depending on which offset is used, there's either a one second difference or a 5 minutes and 53 seconds difference.
This slight shift of offsets, instead of the usual one-hour daylight savings (summer time) we are used to, obscures the problem a bit.
Note that the 2013a update of the timezone database moved this discontinuity a few seconds earlier, but the effect would still be observable.
The new java.time
package on Java 8 let use see this more clearly, and provide tools to handle it. Given:
DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
String str3 = "1927-12-31 23:54:07";
String str4 = "1927-12-31 23:54:08";
ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);
Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());
Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());
Then durationAtEarlierOffset
will be one second, while durationAtLaterOffset
will be five minutes and 53 seconds.
Also, these two offsets are the same:
// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();
But these two are different:
// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();
// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();
You can see the same problem comparing 1927-12-31 23:59:59
with 1928-01-01 00:00:00
, though, in this case, it is the earlier offset that produces the longer divergence, and it is the earlier date that has two possible offsets.
Another way to approach this is to check whether there's a transition going on. We can do this like this:
// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
You can check whether the transition is an overlap where there's more than one valid offset for that date/time or a gap where that date/time is not valid for that zone id - by using the isOverlap()
and isGap()
methods on zot4
.
I hope this helps people handle this sort of issue once Java 8 becomes widely available, or to those using Java 7 who adopt the JSR 310 backport.