The following GNU date command demonstrates converting from local time to Terrestrial Time (TT) used by astronomers. Just for fun, we show conversions across the leap second which occurred at 2015-06-30 18:59:60 local time in Chicago (Central Time). This demonstrates that 2 seconds elapsed between 18:59:59 and 19:00:00 local time. TT does not have leap seconds, so its clock advances 2 seconds.
$ TZ=posix/UTC date --date='TZ="right/America/Chicago" 32.184 second 10 second 2015-06-30T18:59:59-05:00' | perl -plwe 's/ UTC / TT / or die'
Wed Jul 1 00:01:06 TT 2015
$ TZ=posix/UTC date --date='TZ="right/America/Chicago" 32.184 second 10 second 2015-06-30T18:59:60-05:00' | perl -plwe 's/ UTC / TT / or die'
Wed Jul 1 00:01:07 TT 2015
$ TZ=posix/UTC date --date='TZ="right/America/Chicago" 32.184 second 10 second 2015-06-30T19:00:00-05:00' | perl -plwe 's/ UTC / TT / or die'
Wed Jul 1 00:01:08 TT 2015
Note that the double quotes are required when setting TZ inside the --date argument.
The general idea is to use or abuse how the difference between the "right" counter and the "posix" counter increases each time a leap second occurs.
When specifying a local time, we need to specify the time zone both with the TZ variable, e.g., America/Chicago, and with time zone suffix, e.g., -5:00, (future post, parsing time zones) in case we are dealing with time during a daylight saving time change.
The substrings "32.184 second" and "10 second" are relative items which adjust a time by the specified amount.
32.184 seconds is the offset between TAI and TT. 10 seconds is the offset between TAI and a "right" counter, the counter which does not stop for leap seconds. Incidentally, to convert to TAI instead of TT, just omit the 32.184 second offset:
$ TZ=posix/UTC date --date='TZ="right/America/Chicago" 10 second 2015-06-30T18:59:59-05:00' | perl -plwe 's/ UTC / TAI / or die'
Wed Jul 1 00:00:34 TAI 2015
$ TZ=posix/UTC date --date='TZ="right/America/Chicago" 10 second 2015-06-30T18:59:60-05:00' | perl -plwe 's/ UTC / TAI / or die'
Wed Jul 1 00:00:35 TAI 2015
$ TZ=posix/UTC date --date='TZ="right/America/Chicago" 10 second 2015-06-30T19:00:00-05:00' | perl -plwe 's/ UTC / TAI / or die'
Wed Jul 1 00:00:36 TAI 2015
Here is how to get the current TT:
$ date -u ; TZ=posix/UTC date --date='TZ="right/UTC" 32.184 second 10 second '$(date -u --iso=ns) | perl -plwe 's/ UTC / TT / or die'
Tue Jul 7 08:57:47 UTC 2020
Tue Jul 7 08:58:56 TT 2020
Note that "now" does not work:
$ date -u ; TZ=posix/UTC date --date='TZ="right/UTC" 32.184 second 10 second now' | perl -plwe 's/ UTC / wrongTT / or die'
Sun Jul 26 16:22:09 UTC 2020
Sun Jul 26 16:22:51 wrongTT 2020
Here is the correct conversion of "Sun Jul 26 16:22:09 UTC 2020" to TT:
$ TZ=posix/UTC date --date='TZ="right/UTC" 32.184 second 10 second 2020-07-26T16:22:09+00:00' | perl -plwe 's/ UTC / TT / or die'
Sun Jul 26 16:23:18 TT 2020
This post was inspired by https://serverfault.com/questions/405169/easy-way-to-get-the-international-atomic-time-on-linux, but the instructions given on that page, using "now", are incorrect.
The current and historical offsets between TAI and UTC can be looked up in many authoritative places, so these calculations can easily be verified.
Digression: We avoid demonstrating with the America/New_York time zone because of FUD: its time zone file is a symbolic link to a file called /usr/share/zoneinfo/posixrules, so this time zone might have special behavior. What is the purpose of posixrules?
$ ls -l /usr/share/zoneinfo/America/New_York
lrwxrwxrwx 1 root root 13 Sep 20 2019 /usr/share/zoneinfo/America/New_York -> ../posixrules
In newer systems, e.g., Debian Bullseye, the symlink goes the other way:
$ ls -l /usr/share/zoneinfo/posixrules
lrwxrwxrwx 1 root root 16 Apr 24 15:32 /usr/share/zoneinfo/posixrules -> America/New_York
Another digression: This is how relative items work across a leap second boundary. With posix, minutes always have 60 seconds:
$ TZ=posix/UTC date '--date=32.184 second 10 second 2015-06-30T23:59:17+00:00'
Tue Jun 30 23:59:59 UTC 2015
$ TZ=posix/UTC date '--date=32.184 second 10 second 2015-06-30T23:59:18+00:00'
Wed Jul 1 00:00:00 UTC 2015
$ TZ=posix/UTC date '--date=32.184 second 10 second 2015-06-30T23:59:19+00:00'
Wed Jul 1 00:00:01 UTC 2015
The above is what we want if we are doing arithmetic with TT or TAI times, because their minutes always have exactly 60 seconds.
In contrast, "right" does leap seconds, which is not what we want:
$ TZ=right/UTC date '--date=32.184 second 10 second 2015-06-30T23:59:17+00:00'
Tue Jun 30 23:59:59 UTC 2015
$ TZ=right/UTC date '--date=32.184 second 10 second 2015-06-30T23:59:18+00:00'
Tue Jun 30 23:59:60 UTC 2015
$ TZ=right/UTC date '--date=32.184 second 10 second 2015-06-30T23:59:19+00:00'
Wed Jul 1 00:00:00 UTC 2015
Next, we describe how to do the reverse conversion, converting from Terrestrial Time to local time. Although we specify the input time zone as posix/UTC, we really mean TT. We do not need to add a time zone suffix (e.g., +0:00) because UTC does not do Daylight Saving Time. (We hope it never will.)
$ TZ=right/America/Chicago date --date='TZ="posix/UTC" -32.184 second -10 second 2015-07-01T00:01:06.2'
Tue Jun 30 18:59:59 CDT 2015
$ TZ=right/America/Chicago date --date='TZ="posix/UTC" -32.184 second -10 second 2015-07-01T00:01:07.2'
Tue Jun 30 18:59:60 CDT 2015
$ TZ=right/America/Chicago date --date='TZ="posix/UTC" -32.184 second -10 second 2015-07-01T00:01:08.2'
Tue Jun 30 19:00:00 CDT 2015
We add .2 seconds to the input dates to avoid possible confusion caused by clocks always rounding down.
Here is one more example, converting noon Coordinated Universal Time on January 1, 2000 to Terrestrial Time:
$ TZ=right/UTC date --iso=ns --date='TZ="posix/UTC" -32.184 second -10 second 2000-01-01T12:00:00'
2000-01-01T11:58:55,816000000+00:00
and converting that TT back to UTC:
$ TZ=posix/UTC date --iso=ns --date='TZ="right/UTC" 32.184 second 10 second 2000-01-01T11:58:55.816+00:00'
2000-01-01T12:00:00,000000000+00:00
No comments:
Post a Comment