If I run the following program, which parses two date strings referencing times 1 second apart and compares them:
public static void main(String[] args) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str3 = "1927-12-31 23:54:07";
String str4 = "1927-12-31 23:54:08";
Date sDt3 = sf.parse(str3);
Date sDt4 = sf.parse(str4);
long ld3 = sDt3.getTime() /1000;
long ld4 = sDt4.getTime() /1000;
System.out.println(ld4-ld3);
}
The output is:
353
Why is ld4-ld3
not 1
(as I would expect from the one-second difference in the times), but 353
?
If I change the dates to times 1 second later:
String str3 = "1927-12-31 23:54:08";
String str4 = "1927-12-31 23:54:09";
Then ld4-ld3
will be 1
.
Java version:
java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)
Timezone(`TimeZone.getDefault()`):
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]
Locale(Locale.getDefault()): zh_CN
As explained by others, there's a time discontinuity there. There are two possible timezone offsets for
1927-12-31 23:54:08
atAsia/Shanghai
, but only one offset for1927-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:Then
durationAtEarlierOffset
will be one second, whiledurationAtLaterOffset
will be five minutes and 53 seconds.Also, these two offsets are the same:
But these two are different:
You can see the same problem comparing
1927-12-31 23:59:59
with1928-01-01 00:00:00
, though, in this case, it is the earlier offset that produce 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:
You can check whether the transition is an overlap - in which case there's more than one valid offset for that date/time - or a gap - in which case that date/time is not valid for that zone id - by using the
isOverlap()
andisGap()
methods onzot4
.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.
When incrementing time you should convert back to UTC and then add or subtract. Use the local time only for display.
This way you will be able to walk through any periods where hours or minutes happen twice.
If you converted to UTC, add each second, and convert to local time for display. You would go through 11:54:08 p.m. LMT - 11:59:59 p.m. LMT and then 11:54:08 p.m. CST - 11:59:59 p.m. CST.