Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a 1 year old (java) lib correctly perform an UTC Time formatting, considering a newly introduced leap second

Tags:

java

date

time

gps

A timestamp expressed in milliseconds since 1.1.1970 UTC is a common way to store timestamps, e.g in Java.

e.g:

long timestampUtc = System.currentTimeMillis();

Such a timestamp can be formated in human readle time format, e.g using this code

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
    df.setTimeZone(TimeZone.getTimeZone("UTC"));
    String humanTimeUtc = df.format(new Date(timestampUtc));
    System.out.println(humanTimeUtc);

which gives output: 2014-02-14 14:58:05

Now imagine that today at midnight the time administration introduces a new UTC leap second. If I run the code above tommorow, the java JRE on my system cannot know that leap second introduction, and would format the time wrongly (by one second).

Is my asumption correct?
How to correctly format the time (e.g in a log file) in systems that cannot always use an up to date JRE?.

Background info:
This is used in an embedded device, which synchronizes its system clock via GPS, having the GPS number of leap seconds offset to UTC.

like image 996
AlexWien Avatar asked Feb 14 '14 15:02

AlexWien


4 Answers

Java and the Unix "epoch" (number of seconds since Jan 1, 1970 00:00:00 UTC) both ignore leap seconds entirely. They both assume every day (measured in UTC) has had exactly 86400 seconds. A simple block of code to verify:

    Calendar c = Calendar.getInstance();
    c.setTimeZone(TimeZone.getTimeZone("UTC"));
    c.set(2014, 0, 1, 0, 0, 0);
    c.set(Calendar.MILLISECOND, 0);
    System.out.println(c.getTimeInMillis());

You will see that the number of seconds from 1/1/1970 to 1/1/2014 is an exact multiple of 86400 (it's actually exactly 44 years * 365.25 days/year * 86400 seconds/day); it shouldn't be, because there have been 25 leap seconds introduced in that interval.

If you need to take leap seconds into account, you need to find a library that will do so, or come up with your own adjustment.

like image 166
dcsohl Avatar answered Nov 01 '22 14:11

dcsohl


Leap second handling is hard and Java is not particularly bad in this respect.

The NTP Timescale and Leap Seconds explains just some of the weirdness:

The insertion of leap seconds in UTC and subsequently into NTP and POSIX affect the system clock, and thus the conversion between system clock time and conventional civil time in hours, minutes and seconds. However, since the only institutional memory available to determine the conversion are the UTC national broadcast services, the conversion is in effect reset to UTC as each broadcast timecode is received. Thus, when a leap second is inserted in UTC and subsequently in NTP or POSIX, knowledge of all previous leap seconds is lost.

Another way to describe this is to say there are as many NTP or POSIX timescales as historic leap seconds. In effect, a new timescale is reestablished after each new leap second. Thus, all previous leap seconds, not to mention the apparent origin of the timescale itself, lurch backward one second as each new timescale is established. For instance, if a clock synchronized to UTC in 2005 was used to establish the UTC epoch of an event that occurred in early 1972 without correction, the event would appear 22 seconds late. As a result, for the most precise determination of epoch relative to the historic Gregorian calendar and UTC timescale, the user must subtract from the apparent NTP or POSIX epoch the relevant offset provided by the IERS. This is a feature of almost all present day time distribution mechanisms.

Leap seconds are almost never relevant unless you're doing astronomical calculations or something else that depends on the position of the Earth and Sun.

Usually people who think they need leap seconds really need either

  1. a stable (for the foreseeable future) way of converting from a tuple representation like (year, month, day, ...) to a scalar representation like millis-since-epoch or
  2. deal with one representation instead of mixing and matching.

I prefer (2). The scalar representation is better for scientific calculations, and the tuple representation is better for business and casual applications.

like image 29
Mike Samuel Avatar answered Nov 01 '22 13:11

Mike Samuel


The answer by dcsohl and the answer by Mike Samuel are correct.

The other comments about the time database of Java being updated are incorrect. Yes, the database is updated, but not for leap seconds. Leap seconds are entirely ignored by Java, by Joda-Time, and by other Unix-oriented timekeeping systems.

Your question assumes a problem where none exists.

Clocks Drift

The physical clock on nearly all computers are not very precise. Drifting a second or more per month is common. That's why virtually all Operating Systems default to connecting to time servers either locally or over the internet. So, in practical terms, your computer is regularly tweaking its own current time. Because of this you may already find anomalies in the time sequences recorded in your logs if you looked carefully.

Inserting a leap second has the same effect as your clock’s imprecision. Your computer is off by a second, and will soon be corrected by a check-in with a time server.

Ignoring Leap Second

In terms of your data, both the bundled java.util.Date class and the popular replacement, Joda-Time, ignore the leap second. So think of a leap second as just stretching out that 59th second on the last hour of the day of a leap-second event. In terms of our calendar and call clocks, nothing happened. For all business-related apps and most practical purposes of most software, ignoring leap seconds has no detrimental effects.

Yes, technically speaking, the milliseconds-since-epoch used by java.util.Date and Joda-Time are incorrect. Right now is reported 1392442622998 since the beginning of the year 1970, while actually with 25 leap-seconds having been inserted since 1972, that number "should" be 1,392,442,647,998 (25,000 more). Yes, calculating the elapsed time between two timepoints over years will be short by a few seconds. But for most purposes, who cares? We have adjusted our calendars to act as if there was no extra second.

If you have a precision-oriented mind as I do, it takes a while to wrap your head around the fact that ignoring leap seconds has no practical effect on tracking the calendar. The basic problem:

  • Time-of-day is the way we read the spinning of the planet (which is slowing), while date/calendar is how we read the orbit of the planet around the Sun.
  • Those two, time-of-day & date/calendar, have nothing to do with each other. They are independent realities. But we humans smoosh them together to make sense of time the way we smoosh peanut butter and banana together to make a sandwich. And just like we might drizzle honey between the pb and banana to bring them together, leap-seconds bring the clock+calendar together in a unified manner for our mental consumption.

One Possible Problem

The only real practical problem is that reportedly some systems generate an actual 60th second, a time of 23:59:60. That time value wreaks havoc with some software written while ignorant of leap seconds. Such software incorrectly assumes that value is impossible, and may throw errors or otherwise fail. Properly informed software should know that (a) we can have extra seconds so 23:59:60 and 23:59:61 are legal values, and (b) the leap can be negative. (While so far we've had only single positive leap seconds, I recall reading that more than one is possible but not expected. I cannot find a source on that.) Remember, this problem only occurs if an OS or provider of time values is actually tracking those leap second events – few do, so they never see a second numbered 60.

More Info

Wikipedia page has more info on Leap Second.

like image 2
Basil Bourque Avatar answered Nov 01 '22 12:11

Basil Bourque


My new library Time4J is able to handle leap seconds so this is one of several unique features of this library. I don't know any other library which can do formatting of leap seconds. Concretely about your question in detail:

Your example code using standard Java

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String humanTimeUtc = df.format(new Date(timestampUtc));
System.out.println(humanTimeUtc);

looks in Time4J like:

ChronoFormatter<Moment> formatter =
  ChronoFormatter.setUp(Moment.class, Locale.US)
                 .addPattern("uuuu-MM-dd HH:mm:ss", PatternType.CLDR)
                 .build();
Moment timestampUTC = SystemClock.INSTANCE.currentTime();
System.out.println(formatter.format(timestampUTC));
// output: 2014-02-20 14:16:25

a) The time source SystemClock is based on System.currentTimeMillis(). This source never counts leap seconds and can also never yield a leap second timestamp - assuming that the underlying OS is leapsecond-unaware. So the output in this example will never display a leap second value of 60.

b) Internally an object of type Moment both holds a posix-timestamp AND a leapsecond-bit in its state. So by help of an external leapsecond table (which is actually hold in a small file in classpath) every Moment will correctly display the exact same time even when a system administrator will later update the leapsecond file and insert a new one. This does not affect any Moment outside of leap seconds hence there is no one-second-off-error. => If you rerun the code after insertion of new leap second then the timestamp of stored moment is still the same. The formatted output does not change which is a good thing.

c) You can construct a Moment which represent a leap second either by choosing a specialized time source (in the future I deliver a SNTP-client which might be able to track a leap second), or by applying a suitable number of SI-seconds added to a normal Moment. The formatted output for such a timestamp will indeed display a second value of 60, provided that the leap second table is up-to-date. If you transfer this leapsecond-moment to another JVM by serialization where the leapsecond-table is not up-to-date then it will be handled there as one second off (and if you serialize it back to a properly updated JVM or if the receiver-JVM is properly updated later then the leapsecond will be shown again).

d) Time4J also supports GPS time scale. You can construct a Moment by giving the elapsed seconds since GPS epoch (1980-01-06 midnight at start) and specifying the GPS time scale. Internally the Moment converts the data to UTC state which is not lossy provided the leapsecond-table is up-to-date. Of course, if your configuration is not up-to-date and the GPS source emits a number of elapsed seconds representing a leapsecond event then there will be an error one-second-off. In order to avoid such tiny and rare errors because of not properly managed leapsecond tables in client JVMs it might be a good idea to install another mechanism for configuration. Time4J defines a SPI-interface for this purpose.

like image 1
Meno Hochschild Avatar answered Nov 01 '22 14:11

Meno Hochschild