Paul Warnick
Paul Warnick

Reputation: 933

Finding the number of TAI seconds since 00:00:00 UTC, 1 January, 2004 in Java

As the title states, I'm required to find the number of TAI seconds since 00:00:00 UTC, January 1st, 2004 (in Java). I've just recently learned what TAI is and my attempts at working out the above have left me a bit confused.

What I've tried:

I know in Java you can use System.currentTimeMillis() to get the number of milliseconds since January 1st, 1970 UTC (Javadocs).

Additionally, from my brief research of atomic time I understand that currently TAI is exactly 37 (leap) seconds ahead of UTC.

Therefore, my thought process was to:

  1. Find the number of seconds between 1970 and 2004 (34 years)
  2. Subtract that from the current UTC time to get the number of since 2004
  3. Add 37 to get the actual number of seconds in TAI

I wasn't certain of the math here (1 day = 86400 seconds):

At this point I started questioning whether the 37 leap seconds added to TAI were to account for leap years when comparing to UTC and thus I should use Option 2. Unfortunately, I'm not certain whether my thought process is correct and I figured it'd be better to ask here to make certain.

Also, I found this cite claiming that 1,072,915,200 (seconds) is equivalent to 01/01/2004 @ 12:00am (UTC). Which kind of threw me off because it's not equal to either of my calculations.

Upvotes: 3

Views: 1289

Answers (2)

Meno Hochschild
Meno Hochschild

Reputation: 44061

Tai-seconds are essentially atomic SI-seconds including leap seconds. My library Time4J supports this feature out of the box. For more details about TAI-support see also the javadoc of class Moment:

Moment m2004 = PlainTimestamp.of(2004, 1, 1, 0, 0).atUTC();
Moment now = SystemClock.currentMoment(); // other clocks based on NTP are possible
long seconds = SI.SECONDS.between(m2004, now);

System.out.println(seconds); // 425222084L
System.out.println(now); // 2017-06-22T13:15:24,570000000Z

Update after Ricolas comment:

There was one wrong value in my old answer displayed probably due to executing the test code twice and effectively looked at the results at two different points in time for "now" (45 secs distance). Proof with new test code that my library is correct:

Moment m2004 = PlainTimestamp.of(2004, 1, 1, 0, 0).atUTC();
Moment m2017 = PlainTimestamp.of(2017, 6, 22, 13, 15, 24).atUTC();
long seconds = SI.SECONDS.between(m2004, m2017);
System.out.println(seconds); // 425222129L instead of 425222084L

Thanks to Ricola for having seen the wrong displayed value in the old answer of 2017. So yes, Time4J really shows the same value for what Ricola has manually calculated.

However, Ricola seems to suggest that TAI seconds and UTC seconds are somehow different. No, they have the same constant length and effectively are both SI-seconds by definition. The only thing different is just the representation of seconds in the space of year-month-day-hour-minute-second terms for TAI and UTC. Example using Time4J for that difference in representation:

System.out.println("UTC: " + m2017.toString(TimeScale.UTC));
System.out.println("TAI: " + m2017.toString(TimeScale.TAI));
// Output:
// UTC: UTC-2017-06-22T13:15:24Z
// TAI: TAI-2017-06-22T13:16:01Z

And another tricky caveat, I would not talk about UTC before its official introduction in year 1972 (two years after Unix epoch!). Handling inofficial UTC seconds before 1972 are far more complex (even using decimal parts in seconds or UT based on astronomical seconds with varying length). Therefore it was a wise decision in Time4J to handle UTC only after 1972 (and before just using fuzzy UT seconds).

Upvotes: 1

Ricola
Ricola

Reputation: 2922

I see three ways to solve this. To reuse the same values as @Meno Hochschild, I'll assume that "now" is 2017-06-22T13:15:24Z .

1. Easiest: compute difference in UTC and then add the leap seconds that were inserted between both dates

  1. Get the number of UTC (= non-leap) seconds between 1970 and 2004
  2. Subtract that from the current UTC time to get the number of UTC seconds since 2004
  3. Add the number of leap seconds that happened between 2004 and now.

This gives

  1. Instant.now() = 1498137324 (Unix timestamp, which is the same as System.currentTimeMillis()/1000)
  2. Instant.parse("2004-01-01T00:00:00Z").getEpochSecond() = 1072915200
    1498137324 - 1072915200 = 425222124
  3. According to https://en.wikipedia.org/wiki/Leap_second, there was 5 leap seconds between 2017 and 2004 so we need to add 5 => 425,222,129 TAI seconds elapsed.

2. Convert both dates to TAI and subtract

  1. Get current time in TAI
  2. Get 00:00:00 UTC, 1 January, 2004 in TAI
  3. Subtract

I don't think there is a native way to get time in TAI without using external libraries so we'll just compute it manually. But you can still create a constant table of leap seconds to get it manually.

  1. 2017-06-22T13:15:24Z UTC is 2017-06-22T13:16:01Z TAI (you can either use an online converter like https://astroconverter.com/clocks.html or add 37 seconds)
  2. 2004-01-01T00:00:00Z UTC is 2004-01-01T00:00:32Z TAI (in this case if you don't use the converter you need to add 22 seconds)
  3. ChronoUnit.SECONDS.between( Instant.parse("2004-01-01T00:00:32Z"), Instant.parse("2017-06-22T13:16:01Z") gives 425,222,129 TAI seconds elapsed

3. Harder: Your approach

You wrote

  1. Find the number of seconds between 1970 and 2004 (34 years)
  2. Subtract that from the current UTC time to get the number of since 2004
  3. Add 37 to get the actual number of seconds in TAI

In 1. it's not clear if you mean the number of UTC seconds or TAI (actual) seconds. But I guess you meant TAI seconds as it wouldn't work otherwise.

However the way you compute the TAI seconds is off for three reasons:

  1. Leap year calculation is incorrect
  2. You need to add leap seconds.
  3. There is already a 10 seconds offset between TAI and UTC in 1970

Problem for 1. is that you can't use 365.25 days/year as the number of days is discrete, so this would only give an estimation (Also it's not exactly once every four years as the rules of leap years are: Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400, but luckily between 1970 and 2004 it was indeed once every four year).

For 2. the fix is straightforward

For 3. you have two options.
Either you compute all timestamps as the seconds since Jan 1st 1958, which is when UTC and TAI were the same. Then you can add 37 at the end.
Or you keep the timestamps as the seconds since Jan 1st 1970 UTC (Unix Epoch) and need to only add 27 to take into account the 10 seconds offset.

  1. Between 1970 and 2004 there were 8 leap years (1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000), which is close to what you got with 34/4 = 8.5 but as you can see you are off by half a day. (this is the result of Math.floor(34.0 / 4), but as mentioned above, this only works if there is no centurial year not divisible 400 during the time period).
  2. There were 22 leap seconds between 2004 and 1970.
  3. Let's take the second option to add 27.

So your approach become

  1. Find the number of TAI (real) seconds between 1970 and 2004 (34 years)
  2. Subtract that from the current UTC timestamp to get the number of seconds since 2004
  3. Add 27 to get the actual number of seconds in TAI

This works because {UTC seconds since Unix Epoch} = {TAI seconds since Unix Epoch} - 27. So we have
{UTC seconds now since Unix Epoch} - {TAI seconds in 2004 since Unix Epoch} + 27 =
{TAI seconds now since Unix Epoch} - 27 - {TAI seconds in 2004 since Unix Epoch} + 27 =
{TAI seconds now since Unix Epoch} - {TAI seconds in 2004 since Unix Epoch} =
{TAI seconds between 2004 and now}

  1. (365 (days) x 34 (years)) + 8 (days) * 86400 (seconds) + 22 (seconds) = 1,072,915,222 seconds. There is a difference of 22 seconds with https://www.unixtimestamp.com/index.php because it gives the UTC (non-leap) seconds.
  2. 1498137324 (current UTC timestamp) - 1072555222 = 425582102
  3. 425582102 + 27 = 425,222,129 TAI seconds elapsed

Somehow I don't have the same result as @Meno Hochschild

Upvotes: 2

Related Questions