1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
# Generate zic format 'leapseconds' from NIST/IERS format 'leap-seconds.list'.
# This file is in the public domain.
# This program uses awk arithmetic. POSIX requires awk to support
# exact integer arithmetic only through 10**10, which means for NTP
# timestamps this program works only to the year 2216, which is the
# year 1900 plus 10**10 seconds. However, in practice
# POSIX-conforming awk implementations invariably use IEEE-754 double
# and so support exact integers through 2**53. By the year 2216,
# POSIX will almost surely require at least 2**53 for awk, so for NTP
# timestamps this program should be good until the year 285,428,681
# (the year 1900 plus 2**53 seconds). By then leap seconds will be
# long obsolete, as the Earth will likely slow down so much that
# there will be more than 25 hours per day and so some other scheme
# will be needed.
BEGIN {
print "# Allowance for leap seconds added to each time zone file."
print ""
print "# This file is in the public domain."
print ""
print "# This file is generated automatically from the data in the public-domain"
print "# NIST/IERS format leap-seconds.list file, which can be copied from"
print "# <https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list>"
print "# or via a less-secure protocol and with different comments and"
print "# less volatile last-modified and expiration timestamps, from"
print "# <ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list>."
print "# For more about leap-seconds.list, please see"
print "# The NTP Timescale and Leap Seconds"
print "# <https://www.eecis.udel.edu/~mills/leap.html>."
print ""
print "# The rules for leap seconds are specified in Annex 1 (Time scales) of:"
print "# Standard-frequency and time-signal emissions."
print "# International Telecommunication Union - Radiocommunication Sector"
print "# (ITU-R) Recommendation TF.460-6 (02/2002)"
print "# <https://www.itu.int/rec/R-REC-TF.460-6-200202-I/>."
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 "# (a proxy for Earth's angle in space as measured by astronomers)"
print "# and publishes leap second data in a copyrighted file"
print "# <https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat>."
print "# See: Levine J. Coordinated Universal Time and the leap second."
print "# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995"
print "# <https://ieeexplore.ieee.org/document/7909995>."
print ""
print "# There were no leap seconds before 1972, as no official mechanism"
print "# accounted for the discrepancy between atomic time (TAI) and the earth's"
print "# rotation. The first (\"1 Jan 1972\") data line in leap-seconds.list"
print "# does not denote a leap second; it denotes the start of the current definition"
print "# of UTC."
print ""
print "# All leap-seconds are Stationary (S) at the given UTC time."
print "# The correction (+ or -) is made at the given time, so in the unlikely"
print "# event of a negative leap second, a line would look like this:"
print "# Leap YEAR MON DAY 23:59:59 - S"
print "# Typical lines look like this:"
print "# Leap YEAR MON DAY 23:59:60 + S"
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"
sstamp_init()
}
# In case the input has CRLF form a la NIST.
{ sub(/\r$/, "") }
/^#[ \t]*[Uu]pdated through/ || /^#[ \t]*[Ff]ile expires on/ {
last_lines = last_lines $0 "\n"
}
/^#[$][ \t]/ { updated = $2 }
/^#[@][ \t]/ { expires = $2 }
/^[ \t]*#/ { next }
{
NTP_timestamp = $1
TAI_minus_UTC = $2
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)
printf "Leap\t%d\t%s\t%d\t%s\tS\n", \
ss_year, monthabbr[ss_month], ss_mday, sign
}
old_TAI_minus_UTC = TAI_minus_UTC
}
END {
print ""
if (expires) {
sstamp_to_ymdhMs(expires, ss_NTP)
print "# UTC timestamp when this leap second list expires."
print "# Any additional leap seconds will come after this."
if (! EXPIRES_LINE) {
print "# This Expires line is commented out for now,"
print "# so that pre-2020a zic implementations do not reject this file."
}
printf "%sExpires %.4d\t%s\t%.2d\t%.2d:%.2d:%.2d\n", \
EXPIRES_LINE ? "" : "#", \
ss_year, monthabbr[ss_month], ss_mday, ss_hour, ss_min, ss_sec
} else {
print "# (No Expires line, since the expires time is unknown.)"
}
# 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 "# Here are POSIX timestamps for the data in this file."
print "# \"#updated\" gives the last time the leap seconds data changed"
print "# or, if this file was derived from the IERS leap-seconds.list,"
print "# the last time that file changed in any way."
print "# \"#expires\" gives the first time this file might be wrong;"
print "# if this file was derived from the IERS leap-seconds.list,"
print "# this is typically a bit less than one year after \"updated\"."
if (updated) {
sstamp_to_ymdhMs(updated, ss_NTP)
printf "#updated %d (%.4d-%.2d-%.2d %.2d:%.2d:%.2d UTC)\n", \
updated - epoch_minus_NTP, \
ss_year, ss_month, ss_mday, ss_hour, ss_min, ss_sec
} else {
print "#(updated time unknown)"
}
if (expires) {
sstamp_to_ymdhMs(expires, ss_NTP)
printf "#expires %d (%.4d-%.2d-%.2d %.2d:%.2d:%.2d UTC)\n", \
expires - epoch_minus_NTP, \
ss_year, ss_month, ss_mday, ss_hour, ss_min, ss_sec
} else {
print "#(expires time unknown)"
}
printf "\n%s", last_lines
}
# sstamp_to_ymdhMs - convert seconds timestamp to date and time
#
# Call as:
#
# sstamp_to_ymdhMs(sstamp, epoch_days)
#
# where:
#
# sstamp - is the seconds timestamp.
# epoch_days - is the timestamp epoch in Gregorian days since 1600-03-01.
# ss_NTP is appropriate for an NTP sstamp.
#
# Both arguments should be nonnegative integers.
# On return, the following variables are set based on sstamp:
#
# ss_year - Gregorian calendar year
# ss_month - month of the year (1-January to 12-December)
# ss_mday - day of the month (1-31)
# ss_hour - hour (0-23)
# ss_min - minute (0-59)
# ss_sec - second (0-59)
# ss_wday - day of week (0-Sunday to 6-Saturday)
#
# The function sstamp_init should be called prior to using sstamp_to_ymdhMs.
function sstamp_init()
{
# Days in month N, where March is month 0 and January month 10.
ss_mon_days[ 0] = 31
ss_mon_days[ 1] = 30
ss_mon_days[ 2] = 31
ss_mon_days[ 3] = 30
ss_mon_days[ 4] = 31
ss_mon_days[ 5] = 31
ss_mon_days[ 6] = 30
ss_mon_days[ 7] = 31
ss_mon_days[ 8] = 30
ss_mon_days[ 9] = 31
ss_mon_days[10] = 31
# Counts of days in a Gregorian year, quad-year, century, and quad-century.
ss_year_days = 365
ss_quadyear_days = ss_year_days * 4 + 1
ss_century_days = ss_quadyear_days * 25 - 1
ss_quadcentury_days = ss_century_days * 4 + 1
# Standard day epochs, suitable for epoch_days.
# ss_MJD = 94493
# ss_POSIX = 135080
ss_NTP = 109513
}
function sstamp_to_ymdhMs(sstamp, epoch_days, \
quadcentury, century, quadyear, year, month, day)
{
ss_hour = int(sstamp / 3600) % 24
ss_min = int(sstamp / 60) % 60
ss_sec = sstamp % 60
# Start with a count of days since 1600-03-01 Gregorian.
day = epoch_days + int(sstamp / (24 * 60 * 60))
# Compute a year-month-day date with days of the month numbered
# 0-30, months (March-February) numbered 0-11, and years that start
# start March 1 and end 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 years evenly divisible by 100.
# A quad-century starts on and ends before March 1 in years divisible
# by 400. While the number of days in a quad-century is a constant,
# the number of days in each other time period can vary by 1.
# Any 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.
quadcentury = int(day / ss_quadcentury_days)
day -= quadcentury * ss_quadcentury_days
ss_wday = (day + 3) % 7
century = int(day / ss_century_days)
century -= century == 4
day -= century * ss_century_days
quadyear = int(day / ss_quadyear_days)
day -= quadyear * ss_quadyear_days
year = int(day / ss_year_days)
year -= year == 4
day -= year * ss_year_days
for (month = 0; month < 11; month++) {
if (day < ss_mon_days[month])
break
day -= ss_mon_days[month]
}
# Convert the date to a conventional day of month (1-31),
# month (1-12, January-December) and Gregorian year.
ss_mday = day + 1
if (month <= 9) {
ss_month = month + 3
} else {
ss_month = month - 9
year++
}
ss_year = 1600 + quadcentury * 400 + century * 100 + quadyear * 4 + year
}
|