The following has been kicking around my "Handling_Leapseconds directory for more than a year. It is a complete replacement for the current "leapseconds.awk". It uses the NTP timestamp *data* rather than the *comments* at the end of each leap-second datum from the leap-seconds.list file. I found it again today and finished the changes needed to use Dennis' sstamp_to_ymdhMs() AWK function. I've incorporated some AT&T UNIX programming style changes (which I learned back in the dark ages), too. I'm sure Paul will re-arrange it whichever way he wants before (or even if) it makes it into the TZ distribution. # -=*< This file is in the public domain !! >*=- # Generate zic LEAP commands from an NIST format 'leap-seconds.list'. BEGIN { print "# This file is in the public domain\n" print "# Allowance for leap-seconds added to each time zone file.\n" print "# This script generates zic \"Leap\" directives from public-domain data" print "# in the NIST format leap-seconds.list file (originally created for the" print "# Network Time Protocol (NTP) daemon by Judah Levine). Find it at:\n" print "# <ftp://ftp.nist.gov/pub/time/leap-seconds.list>" print "# -or- <ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list>\n" print "# For more info about the leap-seconds.list file, please see:\n" print "# The NTP Timescale and Leap Seconds by Prof. David L. Mills." print "# <https://www.eecis.udel.edu/~mills/leap.html>.\n" print "# The rules for leap-seconds are specified in Annex 1 (Time scales) of:\n" print "# \"Standard-frequency and time-signal emissions\"" print "# International Telecommunication Union -" print "# Radiocommunication Sector Recommendation (ITU-R) TF.460-6 (02/2002)\n" print "# <https://www.itu.int/rec/R-REC-TF.460-6-200202-I/>\n" print "# which is incorporated by reference in the [ITU] Radio Regulations.\n" print "# The International Earth Rotation and Reference Systems Service (IERS)" print "# periodically uses leap seconds to keep UTC to within 0.9 s of UT1" print "# (which is a proxy for the earth's angular orientation in space)" print "# and publishes leap-second data in a copyrighted file" print "# <https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat>." print "# USNO also has a comprehensive uncopyrighted list of leap-seconds at:" print "# <ftp://199.211.133.23/ser7/leapsec.dat> (AKA maia.usno.navy.mil)" print "# Also see: Coordinated Universal Time and the leap second by Judah Levine" print "# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995" print "# <https://ieeexplore.ieee.org/document/7909995>.\n" print "# There were no leap-seconds before 1972, as no official mechanism existed" print "# accounting for the discrepancy between atomic time (TAI) and UT1." print "# All leap-seconds are Stationary (S) at the given UTC time.\n" print "# The correction (+ or -) is made at the given time, so lines" print "# will typically look like:\n" print "# Leap YEAR MON DAY 23:59:60 + S" print "# - or -" print "# Leap YEAR MON DAY 23:59:59 - S\n" monthabbr[ 1] = "Jan" ; monthabbr[ 2] = "Feb" ; monthabbr[ 3] = "Mar" monthabbr[ 4] = "Apr" ; monthabbr[ 5] = "May" ; monthabbr[ 6] = "Jun" monthabbr[ 7] = "Jul" ; monthabbr[ 8] = "Aug" ; monthabbr[ 9] = "Sep" monthabbr[10] = "Oct" ; monthabbr[11] = "Nov" ; monthabbr[12] = "Dec" # In case the input has CRLF form a la NIST RS = "\r?\n" old_TAI_minus_UTC = int( 0 ) # This variable should be initialize !! sstamp_init() # Init function } # Note: maintaining case, spacing or punctuation can # not be depended upon when humans are involved... /^#[ \t]*[Uu]pdated through[ \t:]*/ || /^#[ \t]*[Ff]ile expires on[ \t:]*/ { last_lines = last_lines $0 "\n" } /^#[$][ \t]/ { updated = strtonum( $2 ) } # Save update NTP timestamp /^#[@][ \t]/ { expires = strtonum( $2 ) } # Save expiry NTP timestamp /^[ \t]*#/ { next } { NTP_timestamp = int( $1 ) # NTP epoch timestamp TAI_minus_UTC = int( $2 ) # delta T if ( old_TAI_minus_UTC ) { if ( old_TAI_minus_UTC < TAI_minus_UTC ) { sign = "23:59:60\t+" } else { sign = "23:59:59\t-" } sstamp_to_ymdhMs( NTP_timestamp - 1, ss_NTP ) # Note minus one !! printf "Leap\t%d\t%s\t%d\t%s\tS\n", ss_y, monthabbr[ ss_m ], ss_d, sign } old_TAI_minus_UTC = TAI_minus_UTC } END { # The difference between the NTP and POSIX epochs is 70 years # (including 17 leap days), each 24 hours of 60 minutes of 60 # seconds each. epoch_minus_NTP = ((1970 - 1900) * 365 + 17) * 24 * 60 * 60 print "" print "# POSIX timestamps for the data in this file:" sstamp_to_ymdhMs( updated, ss_NTP ) printf "# Updated: %s (%02d %s %d)\n", ( updated - epoch_minus_NTP ), ss_d, monthabbr[ ss_m ], ss_y sstamp_to_ymdhMs( expires, ss_NTP ) printf "# Expires: %s (%02d %s %d)\n", ( expires - epoch_minus_NTP ), ss_d, monthabbr[ ss_m ], ss_y printf "\n%s", last_lines } # Contributed by # # Dennis Ferguson <dennis.c.ferguson@gmail.com> on Sun, 14 Oct 2018 21:56:57 -0700 # # sstamp_to_ymdhMs() - convert (NTP, POSIX) seconds stamp to date and time # # Call as: # # sstamp_to_ymdhMs( sstamp, epoch_days ) # # where: # # sstamp - is a seconds timestamp, e.g. POSIX or NTP # epoch_days - is the timestamp epoch in (proleptic Gregorian) days since # 1 Mar 1600. ss_NTP is appropriate for a NTP sstamp, ss_POSIX # for a Unix sstamp. # # On return the following variables are set based on sstamp: # # ss_y - calendar year for the sstamp (e.g. 2016) # ss_m - month of the year (1-January to 12-December) # ss_d - day of the month # ss_h - hour (0-23) # ss_M - minute (0-59) # ss_s - second (0-59) # ss_w - day of week (0-Sunday to 6-Saturday) # # The function # # sstamp_init() # # should be called prior to using sstamp_to_ymdhMs(). # # The calendar computation part of this starts with a count of days since # March 1, 1600 (in a proleptic Gregorian calendar) and computes a ymd # date with days of the month numbered 0-30, months (March-February) numbered # 0-11 and years which run from March-February. The last four lines of code # in the function convert the date to a conventional day of month (1-31), # month (1-12, January-December) and Gregorian year. # # In the alternate calendar a year starts at March 1 and ends after the last # day of February. A quad-year starts on March 1 of a year evenly divisible # by 4 and ends after the last day of February 4 years later. A century # starts on and ends before March 1 in a year evenly divisible by 100 # (i.e. 1600, 1900, 2100). A quad-century starts on and ends before March 1 # in years divisible by 400 (1600, 2000, 2400). Given this the magic constants # used here are: # # days per year = 365 # days per quad-year = 1461 = (365 * 4) + 1 # days per century = 36524 = (1461 * 25) - 1 # days per quad-century = 146097 = (36524 * 4) + 1 # weekday (0-Sunday 6-Saturday) of first day of quad-century = 3 (Wednesday) # # Note that while the number of days in a quad-century is a constant the # number of days in each of the other time periods can vary by 1. In all # these cases, however, the variation is in the last day of the time period # (there might or might not be a February 29) where it is easy to deal with. # # The three standard day_epochs are: # # ss_MJD = 94493 # ss_NTP = 109513 # ss_POSIX = 135080 function sstamp_init(){ ss_mon_days[ 1] = 31 ss_mon_days[ 2] = 61 ss_mon_days[ 3] = 92 ss_mon_days[ 4] = 122 ss_mon_days[ 5] = 153 ss_mon_days[ 6] = 184 ss_mon_days[ 7] = 214 ss_mon_days[ 8] = 245 ss_mon_days[ 9] = 275 ss_mon_days[10] = 306 ss_mon_days[11] = 337 ss_NTP = 109513 ss_POSIX = 135080 } function sstamp_to_ymdhMs( sstamp, epoch_days, ss_qcent, ss_cent, ss_qyr, ss_yr ){ ss_s = sstamp % 86400 # isolate hms ss_h = int( ss_s / 3600 ) ss_s %= 3600 ss_M = int( ss_s / 60 ) ss_s %= 60 ss_d = int( sstamp / 86400 ) + epoch_days # select epoch ss_qcent = int( ss_d / 146097 ) ss_y = 1600 + ss_qcent * 400 ss_d -= ss_qcent * 146097 ss_w = ( ss_d + 3 ) % 7 ss_cent = int( ss_d / 36524 ) if (ss_cent == 4) { ss_cent = 3 } ss_d -= ss_cent * 36524 ss_y += ss_cent * 100 ss_qyr = int( ss_d / 1461 ) ss_y += ss_qyr * 4 ss_d -= ss_qyr * 1461 ss_yr = int(ss_d / 365) if ( ss_yr == 4 ) { ss_yr = 3 } ss_d -= ss_yr * 365 ss_y += ss_yr for ( ss_m = 11 ; ss_m > 0 ; ss_m-- ){ if( ss_d >= ss_mon_days[ss_m] ){ ss_d -= ss_mon_days[ss_m] ; break ; } } # convert to conventional calendar ss_d += 1; if (ss_m > 9) { ss_m -= 9 ; ss_y++ } else { ss_m += 3 } }