I need to schedule a task in my code at a fixed datetime. For that I'm using a ScheduledExecutorService with the method schedule(Runnable command, long delay, TimeUnit unit);
How can I compute this delay according to leap seconds ?
For the moment I use Duration.between()
but it doesn't seem aware of leap seconds:
ZonedDateTime date1 = ZonedDateTime.of(2015, 06, 30, 23, 59, 59, 000000, ZoneOffset.UTC);
ZonedDateTime date2 = ZonedDateTime.of(2015, 07, 01, 00, 00, 00, 000000, ZoneOffset.UTC);
ZonedDateTime date3 = ZonedDateTime.of(2015, 07, 01, 00, 00, 01, 000000, ZoneOffset.UTC);
System.out.println(Duration.between(date1, date2)); // PT1S
System.out.println(Duration.between(date2, date3)); // PT1S
To support leap seconds you need the ThreeTen-Extra extension jar file. It has dedicated classes for UTC and TAI plus the table of leap seconds.
To calculate the duration, use the dedicated durationUntil() method.
Using my library Time4J (v3.x works on Java 7 - or Time4A on Android) will give you a complete solution including extensive support for formatting and parsing in different timezones. Examples:
// Time4J - transformation from old world
Moment m1 = TemporalType.JAVA_UTIL_DATE.translate(d1);
Moment m2 = TemporalType.JAVA_UTIL_DATE.translate(d2);
System.out.println("Time4J - from j.u.Date: " + SI.SECONDS.between(m1, m2)); // 601
// Time4J - programmatical construction of timestamps (like in your example)
m1 = PlainTimestamp.of(2012, 6, 30, 23, 50, 0).atUTC();
m2 = PlainTimestamp.of(2012, 7, 1, 0, 0, 0).atUTC();
System.out.println("Time4J - from utc-timestamps: " + SI.SECONDS.between(m1, m2)); // 601
// Time4J - parsing zoned inputs
ChronoFormatter<Moment> cf =
ChronoFormatter.ofMomentPattern(
"d. MMM uuuu HH:mm:ss[XXX]", // optional offset
PatternType.CLDR,
Locale.ENGLISH,
EUROPE.PARIS); // only used if the offset is missing in input
m1 = cf.parse("1. Jul 2015 01:50:00");
m2 = cf.parse("1. Jul 2015 09:00:00+09:00");
System.out.println("Time4J - from offsets or zones: " + SI.SECONDS.between(m1, m2)); // 601
// the leap second itself can also be parsed
Moment ls = cf.parse("1. Jul 2015 08:59:60+09:00"); // offset of Tokyo
System.out.println("leap second in 2015: " + ls);
// 2015-06-30T23:59:60Z
There is even more, namely support for clocks being aware of leap seconds and transfer of leap second data from IANA-TZDB. Examples see my article on DZone. Note that Time4J can fully replace ThreetenBP, but is also interoperable with Java-8 (just change the version to v4.x) and offers many more features which are out of scope of the question here.
Side note: Most people obviously choose ThreetenBP in order to make a future migration to Java-8 easier (just changing some import statements). But the problem in your case is that
Java-standard (any version) itself does not know anything about leap seconds (not even the necessary data).
The proposed other 3rd-party library Threeten-Extra is not working on Java-7. It requires Java-8.
After migration to Java-8, Threeten-Extra seems to present a solution. However, I have now done my own tests and would advise against using that library, see following code:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Date d1 = sdf.parse("2012-06-30T23:50:00Z");
Date d2 = sdf.parse("2012-07-01T00:00:00Z");
System.out.println(
"Old world in Posix - ignorant of leap seconds: "
+ (d2.getTime() - d1.getTime()) / 1000);
// 600 (OK, SI-seconds without counting leap second, a limitation of POSIX)
Instant instant1 = d1.toInstant();
Instant instant2 = d2.toInstant();
System.out.println(
"Java 8 without Threeten-Extra: "
+ (instant2.getEpochSecond() - instant1.getEpochSecond()));
// 600 (obviously using more or less fuzzy "rubber seconds", not SI-seconds)
// internally a simple 1:1-mapping of POSIX is applied within 'toInstant()'
UtcInstant utc1 = UtcInstant.of(instant1);
UtcInstant utc2 = UtcInstant.of(instant2);
System.out.println(
"Threeten-Extra-impl of UTC-SLS: "
+ utc1.durationUntil(utc2).getSeconds()); // pitfall, see next output!
// 600 (??? - where is the leap second, should be 601 because of original POSIX-input?!)
System.out.println(
"Threeten-Extra-impl of UTC-SLS: "
+ utc1.durationUntil(utc2)); // <= printing the duration object
// PT10M0.600600601S (OK, here the UTC-SLS-specific fraction of second appears
// Reason: Smoothing starts 1000s before leap second)
// only offset "Z=UTC+00:00" can be parsed
utc1 = UtcInstant.parse("2012-06-30T23:50:00Z");
utc2 = UtcInstant.parse("2012-07-01T00:00:00Z");
System.out.println(
"Threeten-Extra-impl of UTC-SLS: " + utc1.durationUntil(utc2).getSeconds());
// 601 (expected, the only way where seconds are really SI-seconds)
Leaving aside some obvious limitations in formatting and parsing, the main problem seems to be the confusing handling of time scale definitions. The definition of a second depends here on the context. For example the result "PT10M0.600600601S" is okay if you only consider the transformation using UTC-SLS, but not okay if you consider the whole transformation from POSIX via UTC-SLS to UTC. And as said before, POSIX time is well defined 10 minutes before leap second so any fuzzyness of POSIX during the leap second is no excuse.
Keep in mind that really NOBODY outside of java.time
-world talks about UTC-SLS which has been an (officially expired) proposal of Markus Kuhn addressing the internal implementation of NTP time servers or OS-kernels. Instead other people and enterprises like Google introduce their own different leap smear implementations. I don't see any future for UTC-SLS.
And since java.util.Date
has nothing to do with UTC-SLS (is much older) but is also well defined for normal timestamps (following UTC-definition of Si-second with the limitation of the leap seconds which are not handled at all), we see here an interoperability problem between outer IT-world (which does not want to know anything about leap seconds or UTC-SLS) and Threeten-Extra which can cause unexpected differences in calculated durations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With