#endif /* !HAVE_STDINT_H */
#ifndef HAVE_INTTYPES_H
@@ -358,14 +358,14 @@ typedef unsigned long uintmax_t;
#endif
#ifndef INT32_MAX
-#define INT32_MAX 0x7fffffff
+# define INT32_MAX 0x7fffffff
#endif /* !defined INT32_MAX */
#ifndef INT32_MIN
-#define INT32_MIN (-1 - INT32_MAX)
+# define INT32_MIN (-1 - INT32_MAX)
#endif /* !defined INT32_MIN */
#ifndef SIZE_MAX
-#define SIZE_MAX ((size_t) -1)
+# define SIZE_MAX ((size_t) -1)
#endif
#if 3 <= __GNUC__
@@ -632,6 +632,11 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
#define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+/* Minimum and maximum of two values. Use lower case to avoid
+ naming clashes with standard include files. */
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
/* Max and min values of the integer type T, of which only the bottom
B bits are used, and where the highest-order used bit is considered
to be a sign bit if T is signed. */
@@ -737,47 +742,55 @@ char *ctime_r(time_t const *, char *);
/* Handy macros that are independent of tzfile implementation. */
-#define SECSPERMIN 60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
-#define DAYSPERNYEAR 365
-#define DAYSPERLYEAR 366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+enum {
+ SECSPERMIN = 60,
+ MINSPERHOUR = 60,
+ SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
+ HOURSPERDAY = 24,
+ DAYSPERWEEK = 7,
+ DAYSPERNYEAR = 365,
+ DAYSPERLYEAR = DAYSPERNYEAR + 1,
+ MONSPERYEAR = 12,
+ YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
+};
+
#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
-#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-#define TM_TUESDAY 2
-#define TM_WEDNESDAY 3
-#define TM_THURSDAY 4
-#define TM_FRIDAY 5
-#define TM_SATURDAY 6
-
-#define TM_JANUARY 0
-#define TM_FEBRUARY 1
-#define TM_MARCH 2
-#define TM_APRIL 3
-#define TM_MAY 4
-#define TM_JUNE 5
-#define TM_JULY 6
-#define TM_AUGUST 7
-#define TM_SEPTEMBER 8
-#define TM_OCTOBER 9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
-
-#define TM_YEAR_BASE 1900
-#define TM_WDAY_BASE TM_MONDAY
-
-#define EPOCH_YEAR 1970
-#define EPOCH_WDAY TM_THURSDAY
+enum {
+ TM_SUNDAY,
+ TM_MONDAY,
+ TM_TUESDAY,
+ TM_WEDNESDAY,
+ TM_THURSDAY,
+ TM_FRIDAY,
+ TM_SATURDAY
+};
+
+enum {
+ TM_JANUARY,
+ TM_FEBRUARY,
+ TM_MARCH,
+ TM_APRIL,
+ TM_MAY,
+ TM_JUNE,
+ TM_JULY,
+ TM_AUGUST,
+ TM_SEPTEMBER,
+ TM_OCTOBER,
+ TM_NOVEMBER,
+ TM_DECEMBER
+};
+
+enum {
+ TM_YEAR_BASE = 1900,
+ TM_WDAY_BASE = TM_MONDAY,
+ EPOCH_YEAR = 1970,
+ EPOCH_WDAY = TM_THURSDAY
+};
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
diff --git a/southamerica b/southamerica
index c6249cd7..2f2c8733 100644
--- a/southamerica
+++ b/southamerica
@@ -400,6 +400,7 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 -
#
# Buenos Aires (BA), Capital Federal (CF),
Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May # Córdoba Mean Time
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -417,6 +418,7 @@ Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31
# - Santiago del Estero switched to -4:00 on 1991-04-01,
# then to -3:00 on 1991-04-26.
#
+ #STDOFF -4:16:48.25
Zone America/Argentina/Cordoba -4:16:48 - LMT 1894 Oct 31
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
@@ -429,6 +431,7 @@ Zone America/Argentina/Cordoba -4:16:48 - LMT 1894 Oct 31
#
# Salta (SA), La Pampa (LP), Neuquén (NQ), Rio Negro (RN)
Zone America/Argentina/Salta -4:21:40 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -441,6 +444,7 @@ Zone America/Argentina/Salta -4:21:40 - LMT 1894 Oct 31
#
# Tucumán (TM)
Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -454,6 +458,7 @@ Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31
#
# La Rioja (LR)
Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -468,6 +473,7 @@ Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31
#
# San Juan (SJ)
Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -482,6 +488,7 @@ Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31
#
# Jujuy (JY)
Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -497,6 +504,7 @@ Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31
#
# Catamarca (CT), Chubut (CH)
Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -511,6 +519,7 @@ Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31
#
# Mendoza (MZ)
Zone America/Argentina/Mendoza -4:35:16 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -533,6 +542,7 @@ Rule SanLuis 2008 2009 - Mar Sun>=8 0:00 0 -
Rule SanLuis 2007 2008 - Oct Sun>=8 0:00 1:00 -
Zone America/Argentina/San_Luis -4:25:24 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -551,6 +561,7 @@ Zone America/Argentina/San_Luis -4:25:24 - LMT 1894 Oct 31
#
# Santa Cruz (SC)
Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -563,6 +574,7 @@ Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31
#
# Tierra del Fuego, Antártida e Islas del Atlántico Sur (TF)
Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31
+ #STDOFF -4:16:48.25
-4:16:48 - CMT 1920 May
-4:00 - -04 1930 Dec
-4:00 Arg -04/-03 1969 Oct 5
@@ -645,7 +657,7 @@ Zone America/La_Paz -4:32:36 - LMT 1890
# From Rodrigo Severo (2004-10-04):
# It's just the biannual change made necessary by the much hyped, supposedly
-# modern Brazilian eletronic voting machines which, apparently, can't deal
+# modern Brazilian ... voting machines which, apparently, can't deal
# with a time change between the first and the second rounds of the elections.
# From Steffen Thorsen (2007-09-20):
@@ -1141,7 +1153,7 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914
# this is known to work for DST transitions starting in 2008 and
# may well be true for earlier transitions.
-# From Tim Parenti (2022-03-15):
+# From Tim Parenti (2022-07-06):
# For a brief period of roughly six weeks in 1946, DST was only observed on an
# emergency basis in specific regions of central Chile; namely, "the national
# territory between the provinces of Coquimbo and Concepción, inclusive".
@@ -1159,7 +1171,14 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914
# Law Number 8,522, promulgated 1946-08-27, reunified Chilean clocks at their
# new "Summer Time" of -04, reckoned as that of "the meridian of the
# Astronomical Observatory of Lo Espejo, advanced by 42 minutes and 45
-# seconds".
+# seconds". Although this law specified the new Summer Time to start on 1
+# September each year, a special "transitional article" started it a few days
+# early, as soon as the law took effect. As the law was to take force "from
+# the date of its publication in the 'Diario Oficial', which happened the
+# following day, presume the change took place in Santiago and its environs
+# from 24:00 -03 to 23:00 -04 on Wednesday 1946-08-28. Although this was a
+# no-op for wall clocks in the north and south of the country, put their formal
+# start to DST an hour later when they reached 24:00 -04.
# https://www.diariooficial.interior.gob.cl/versiones-anteriores/do-h/19460828/#page/1
# After a brief "Winter Time" stint at -05 beginning 1947-04-01, Law Number
# 8,777, promulgated 1947-05-17, established year-round -04 "from 23:00 on the
@@ -1279,11 +1298,19 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914
# So we extend the new rules on Saturdays at 24:00 mainland time indefinitely.
# From Juan Correa (2019-02-04):
# http://www.diariooficial.interior.gob.cl/publicaciones/2018/11/23/42212/01/1498738.pdf
-# From Paul Eggert (2019-09-01):
-# The above says the Magallanes exception expires 2022-04-02 at 24:00,
-# so in theory, they will revert to -04/-03 after that.
-# For now, assume that they will not revert,
-# since they have extended the expiration date once already.
+
+# From Juan Correa (2022-04-02):
+# I found there was a decree published last Thursday that will keep
+# Magallanes region to UTC -3 "indefinitely". The decree is available at
+# https://www.diariooficial.interior.gob.cl/publicaciones/2022/03/31/43217-B/01/2108910.pdf
+
+# From Juan Correa (2022-08-09):
+# the Internal Affairs Ministry (Ministerio del Interior) informed DST
+# for America/Santiago will start on midnight of September 11th;
+# and will end on April 1st, 2023. Magallanes region (America/Punta_Arenas)
+# will keep UTC -3 "indefinitely"... This is because on September 4th
+# we will have a voting whether to approve a new Constitution....
+# https://www.interior.gob.cl/noticias/2022/08/09/comunicado-el-proximo-sabado-10-de-septiembre-los-relojes-se-deben-adelantar-una-hora/
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule Chile 1927 1931 - Sep 1 0:00 1:00 -
@@ -1321,7 +1348,9 @@ Rule Chile 2012 2014 - Sep Sun>=2 4:00u 1:00 -
Rule Chile 2016 2018 - May Sun>=9 3:00u 0 -
Rule Chile 2016 2018 - Aug Sun>=9 4:00u 1:00 -
Rule Chile 2019 max - Apr Sun>=2 3:00u 0 -
-Rule Chile 2019 max - Sep Sun>=2 4:00u 1:00 -
+Rule Chile 2019 2021 - Sep Sun>=2 4:00u 1:00 -
+Rule Chile 2022 only - Sep Sun>=9 4:00u 1:00 -
+Rule Chile 2023 max - Sep Sun>=2 4:00u 1:00 -
# IATA SSIM anomalies: (1992-02) says 1992-03-14;
# (1996-09) says 1998-03-08. Ignore these.
# Zone NAME STDOFF RULES FORMAT [UNTIL]
@@ -1334,9 +1363,9 @@ Zone America/Santiago -4:42:45 - LMT 1890
-5:00 Chile -05/-04 1932 Sep 1
-4:00 - -04 1942 Jun 1
-5:00 - -05 1942 Aug 1
- -4:00 - -04 1946 Jul 15
- -4:00 1:00 -03 1946 Sep 1 # central Chile
- -4:00 - -04 1947 Apr 1
+ -4:00 - -04 1946 Jul 14 24:00
+ -4:00 1:00 -03 1946 Aug 28 24:00 # central CL
+ -5:00 1:00 -04 1947 Mar 31 24:00
-5:00 - -05 1947 May 21 23:00
-4:00 Chile -04/-03
Zone America/Punta_Arenas -4:43:40 - LMT 1890
@@ -1348,7 +1377,8 @@ Zone America/Punta_Arenas -4:43:40 - LMT 1890
-5:00 Chile -05/-04 1932 Sep 1
-4:00 - -04 1942 Jun 1
-5:00 - -05 1942 Aug 1
- -4:00 - -04 1947 Apr 1
+ -4:00 - -04 1946 Aug 28 24:00
+ -5:00 1:00 -04 1947 Mar 31 24:00
-5:00 - -05 1947 May 21 23:00
-4:00 Chile -04/-03 2016 Dec 4
-3:00 - -03
@@ -1382,13 +1412,14 @@ Zone Antarctica/Palmer 0 - -00 1965
# Colombia
-# Milne gives 4:56:16.4 for Bogotá time in 1899; round to nearest. He writes,
+# Milne gives 4:56:16.4 for Bogotá time in 1899. He writes,
# "A variation of fifteen minutes in the public clocks of Bogota is not rare."
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule CO 1992 only - May 3 0:00 1:00 -
Rule CO 1993 only - Apr 4 0:00 0 -
# Zone NAME STDOFF RULES FORMAT [UNTIL]
+ #STDOFF -4:56:16.4
Zone America/Bogota -4:56:16 - LMT 1884 Mar 13
-4:56:16 - BMT 1914 Nov 23 # Bogotá Mean Time
-5:00 CO -05/-04
diff --git a/strftime.c b/strftime.c
index 52731551..deba2b5e 100644
--- a/strftime.c
+++ b/strftime.c
@@ -56,8 +56,6 @@ struct lc_time_T {
const char * date_fmt;
};
-#define Locale (&C_time_locale)
-
static const struct lc_time_T C_time_locale = {
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -113,7 +111,7 @@ static char * _fmt(const char *, const struct tm *, char *, const char *,
static char * _yconv(int, int, bool, bool, char *, char const *);
#ifndef YEAR_2000_NAME
-#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+# define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
#endif /* !defined YEAR_2000_NAME */
#if HAVE_STRFTIME_L
@@ -164,6 +162,8 @@ static char *
_fmt(const char *format, const struct tm *t, char *pt,
const char *ptlim, enum warn *warnp)
{
+ struct lc_time_T const *Locale = &C_time_locale;
+
for ( ; *format; ++format) {
if (*format == '%') {
label:
@@ -320,21 +320,11 @@ _fmt(const char *format, const struct tm *t, char *pt,
time_t mkt;
tm = *t;
- tm.tm_yday = -1;
mkt = mktime(&tm);
- if (mkt == (time_t) -1) {
- /* Fail unless this -1 represents
- a valid time. */
- struct tm tm_1;
- if (!localtime_r(&mkt, &tm_1))
- return NULL;
- if (!(tm.tm_year == tm_1.tm_year
- && tm.tm_yday == tm_1.tm_yday
- && tm.tm_hour == tm_1.tm_hour
- && tm.tm_min == tm_1.tm_min
- && tm.tm_sec == tm_1.tm_sec))
- return NULL;
- }
+ /* There is no portable, definitive
+ test for whether whether mktime
+ succeeded, so treat (time_t) -1 as
+ the success that it might be. */
if (TYPE_SIGNED(time_t)) {
intmax_t n = mkt;
sprintf(buf, "%"PRIdMAX, n);
@@ -561,15 +551,15 @@ _fmt(const char *format, const struct tm *t, char *pt,
# endif
negative = diff < 0;
if (diff == 0) {
-#ifdef TM_ZONE
+# ifdef TM_ZONE
negative = t->TM_ZONE[0] == '-';
-#else
+# else
negative = t->tm_isdst < 0;
-# if HAVE_TZNAME
+# if HAVE_TZNAME
if (tzname[t->tm_isdst != 0][0] == '-')
negative = true;
+# endif
# endif
-#endif
}
if (negative) {
sign = "-";
@@ -636,7 +626,7 @@ _yconv(int a, int b, bool convert_top, bool convert_yy,
register int lead;
register int trail;
-#define DIVISOR 100
+ int DIVISOR = 100;
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR;
diff --git a/theory.html b/theory.html
index 28cc88ea..2b14c511 100644
--- a/theory.html
+++ b/theory.html
@@ -376,9 +376,11 @@ Timezone identifiers
on platforms that do not support POSIX-style TZ
strings;
no other source file other than backward
contains links to its zones.
-One of etcetera
's names is GMT
,
+One of etcetera
's names is Etc/UTC
,
used by functions like gmtime
to obtain leap
second information on platforms that support leap seconds.
+Another etcetera
name, GMT
,
+is used by older code releases.
@@ -468,6 +470,7 @@ Time zone abbreviations
PST/PDT Philippine,
SAST South Africa,
SST Samoa,
+ UTC Universal,
WAT/WAST West Africa,
WET/WEST/WEMT Western European,
WIB Waktu Indonesia Barat,
@@ -502,7 +505,7 @@ Time zone abbreviations
HMT Havana, Helsinki, Horta, Howrah;
IMT Irkutsk, Istanbul;
JMT Jerusalem;
- KMT Kaunas, Kiev, Kingston;
+ KMT Kaunas, Kyiv, Kingston;
LMT Lima, Lisbon, local, Luanda;
MMT Macassar, Madras, Malé, Managua, Minsk, Monrovia, Montevideo,
Moratuwa, Moscow;
@@ -720,8 +723,8 @@ Accuracy of the tz
database
than what the tz
code can handle.
For example, from 1880 to 1916 clocks in Ireland observed Dublin Mean
Time (estimated to be UT
- −00:25:21.1), but the tz
- code cannot represent the fractional second.
+ −00:25:21.1); although the tz
+ source data can represent the .1 second, TZif files and the code cannot.
In practice these old specifications were rarely if ever
implemented to subsecond precision.
@@ -1295,7 +1298,8 @@ Leap seconds
if no time zone correction is desired,
calls to gmtime
-like functions
also need to consult a TZif file,
-conventionally named GMT,
+conventionally named Etc/UTC
+(GMT in previous versions),
to see whether leap second corrections are needed.
To convert an application's time_t
timestamps to or from
POSIX time_t
timestamps (for use when, say,
diff --git a/tz-how-to.html b/tz-how-to.html
index bf3e86f8..e1e28f2e 100644
--- a/tz-how-to.html
+++ b/tz-how-to.html
@@ -17,7 +17,7 @@
How to Read the tz
Database Source Files
by Bill Seymour
-This page uses the America/Chicago
and
+
This guide uses the America/Chicago
and
Pacific/Honolulu
zones as examples of how to infer
times of day from the tz database
source files. It might be helpful, but not absolutely necessary,
@@ -25,7 +25,10 @@
by Bill Seymour
latest release of the database and become familiar with the basic layout
of the data files. The format is explained in the “man
page” for the zic compiler, zic.8.txt
, in
-the code
subdirectory.
+the code
subdirectory.
+Although this guide covers many of the common cases, it is not a
+complete summary of what zic accepts; the man page is the
+authoritative reference.
We’ll begin by talking about the rules for changing between standard
and daylight saving time since we’ll need that information when we talk
@@ -111,7 +114,8 @@
by Bill Seymour
The next column, -
, is reserved; for compatibility with earlier
releases, it always contains a hyphen, which acts as a kind of null value.
Prior to the 2020b release, it was called the TYPE
field, though
-it was never used in the main data. An obsolescent supplementary file used the
+it had not been used in the main data since the 2000e release.
+An obsolescent supplementary file used the
field as a proof-of-concept to allow zic
to apply a given Rule
line only to certain “types” of years within the specified range as
dictated by the output of a separate script, such as: only years which would
@@ -490,21 +494,26 @@
by Bill Seymour
10:30 + 1:00 = GMT − 9:30.
The FORMAT
column specifies the usual abbreviation of
-the time zone name. It can have one of three forms:
+the time zone name. It should have one of four forms:
-- a string of three or more characters that are either ASCII alphanumerics,
-“
+
”, or “-
”,
-in which case that’s the abbreviation
+- a time zone abbreviation that is a string of three or more
+characters that are either ASCII alphanumerics,
+“
+
”, or “-
”
-- a pair of strings separated by a slash
+
- the string “%z”, in which case the
+“
%z
” will be replaced by a numeric time zone
+abbreviation
+
+- a pair of time zone abbreviations separated by a slash
(‘
/
’), in which case the first string is the
abbreviation for the standard time name and the second string is the
abbreviation for the daylight saving time name
-- a string containing “
%s
,” in which case
+ - a string containing “
%s
”, in which case
the “%s
” will be replaced by the text in the
-appropriate Rule’s LETTER
column
+appropriate Rule’s LETTER
column, and the resulting
+string should be a time zone abbreviation
The last two make sense only if there’s a named rule in effect.
diff --git a/tz-link.html b/tz-link.html
index 92da48f7..25320694 100644
--- a/tz-link.html
+++ b/tz-link.html
@@ -255,7 +255,8 @@ Changes to the tz
database
Noda Time, and OpenJDK/Oracle JDK.
-Sources for the tz
database are
+
Since version 2013a,
+sources for the tz
database have been
UTF-8
text files
@@ -1060,7 +1061,9 @@
Precision timekeeping
sixty seconds. This approach works with the default tz
"posix
" configuration, is supported by
-the NTP reference implementation, and is used by major
+the NTP reference implementation, supports conversion between
+UTC and smeared POSIX timestamps, and is used by major
cloud service providers. However, according to
§3.7.1 of
Network Time Protocol Best Current Practices
diff --git a/tzfile.h b/tzfile.h
index 9c3ea4ef..6b18d6b4 100644
--- a/tzfile.h
+++ b/tzfile.h
@@ -22,15 +22,15 @@
*/
#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+# define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
+# define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
-#define TZDEFRULES "posixrules"
+# define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
@@ -103,21 +103,21 @@ struct tzhead {
*/
#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 2000
+# define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+# define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+# define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+# define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#endif /* !defined TZFILE_H */
diff --git a/tzselect.8 b/tzselect.8
index 69514e24..85496593 100644
--- a/tzselect.8
+++ b/tzselect.8
@@ -4,6 +4,16 @@ tzselect \- select a timezone
.SH SYNOPSIS
.ie \n(.g .ds - \f(CW-\fP
.el .ds - \-
+.ds d " degrees
+.ds m " minutes
+.ds s " seconds
+.ds _ " \"
+.if t .if \n(.g .if c \(de .if c \(fm .if c \(sd \{\
+. ds d \(de
+. ds m \(fm
+. ds s \(sd
+. ds _ \|
+.\}
.B tzselect
[
.B \*-c
@@ -57,11 +67,11 @@ seconds, with any trailing fractions represent fractional minutes or
is present) seconds. The decimal point is that of the current locale.
For example, in the (default) C locale,
.B "\*-c\ +40.689\*-074.045"
-specifies 40.689\(de\|N, 74.045\(de\|W,
+specifies 40.689\*d\*_N, 74.045\*d\*_W,
.B "\*-c\ +4041.4\*-07402.7"
-specifies 40\(de\|41.4\(fm\|N, 74\(de\|2.7\(fm\|W, and
+specifies 40\*d\*_41.4\*m\*_N, 74\*d\*_2.7\*m\*_W, and
.B "\*-c\ +404121\*-0740240"
-specifies 40\(de\|41\(fm\|21\(sd\|N, 74\(de\|2\(fm\|40\(sd\|W.
+specifies 40\*d\*_41\*m\*_21\*s\*_N, 74\*d\*_2\*m\*_40\*s\*_W.
If
.I coord
is not one of the documented forms, the resulting behavior is unspecified.
diff --git a/zdump.c b/zdump.c
index 8b6788a4..a63a3438 100644
--- a/zdump.c
+++ b/zdump.c
@@ -35,11 +35,11 @@
#endif
#ifndef ZDUMP_LO_YEAR
-#define ZDUMP_LO_YEAR (-500)
+# define ZDUMP_LO_YEAR (-500)
#endif /* !defined ZDUMP_LO_YEAR */
#ifndef ZDUMP_HI_YEAR
-#define ZDUMP_HI_YEAR 2500
+# define ZDUMP_HI_YEAR 2500
#endif /* !defined ZDUMP_HI_YEAR */
#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
@@ -72,7 +72,7 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
#endif
#if HAVE_GETTEXT
-#include /* for setlocale */
+# include /* for setlocale */
#endif /* HAVE_GETTEXT */
#if ! HAVE_LOCALTIME_RZ
@@ -450,9 +450,9 @@ main(int argc, char *argv[])
cuthitime = absolute_max_time;
#if HAVE_GETTEXT
setlocale(LC_ALL, "");
-#ifdef TZ_DOMAINDIR
+# ifdef TZ_DOMAINDIR
bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* defined TEXTDOMAINDIR */
+# endif /* defined TEXTDOMAINDIR */
textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */
progname = argv[0];
@@ -548,7 +548,7 @@ main(int argc, char *argv[])
for (i = optind; i < argc; i++) {
size_t arglen = strlen(argv[i]);
if (longest < arglen)
- longest = arglen < INT_MAX ? arglen : INT_MAX;
+ longest = min(arglen, INT_MAX);
}
for (i = optind; i < argc; ++i) {
@@ -1193,6 +1193,7 @@ dumptime(register const struct tm *timeptr)
};
register int lead;
register int trail;
+ int DIVISOR = 10;
/*
** The packaged localtime_rz and gmtime_r never put out-of-range
@@ -1208,7 +1209,6 @@ dumptime(register const struct tm *timeptr)
? mon_name[timeptr->tm_mon] : "???"),
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec);
-#define DIVISOR 10
trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
trail / DIVISOR;
diff --git a/zic.8 b/zic.8
index a0a9743b..772ee89c 100644
--- a/zic.8
+++ b/zic.8
@@ -28,6 +28,16 @@ zic \- timezone compiler
. ds :
. ds - \-
.\}
+.ds d " degrees
+.ds m " minutes
+.ds s " seconds
+.ds _ " \"
+.if t .if \n(.g .if c \(de .if c \(fm .if c \(sd \{\
+. ds d \(de
+. ds m \(fm
+. ds s \(sd
+. ds _ \|
+.\}
The
.B zic
program reads text from the file(s) named on the command line
@@ -136,8 +146,7 @@ are possibly-signed decimal counts of seconds since the Epoch
Omitted counts default to extreme values.
The output files use UT offset 0 and abbreviation
.q "\*-00"
-in place of the omitted timestamp data;
-this typically reduces the files' sizes.
+in place of the omitted timestamp data.
For example,
.q "zic \*-r @0"
omits data intended for negative timestamps (i.e., before the Epoch), and
@@ -146,12 +155,31 @@ outputs data intended only for nonnegative timestamps that fit into
31-bit signed integers.
On platforms with GNU
.BR date ,
-.q "zic \-r @$(date +%s)"
+.q "zic \*-r @$(date +%s)"
omits data intended for past timestamps.
+Although this option typically reduces the output file's size,
+the size can increase due to the need to represent the timestamp range
+boundaries, particularly if
+.I hi
+causes a TZif file to contain explicit entries for
+.RI pre- hi
+transitions rather than concisely representing them
+with an extended POSIX TZ string.
Also see the
.B "\*-b slim"
option for another way to shrink output size.
.TP
+.BI "\*-R @" hi
+Generate redundant trailing explicit transitions for timestamps
+that occur less than
+.I hi
+seconds since the Epoch, even though the transitions could be
+more concisely represented via the extended POSIX TZ string.
+This option does not affect the represented timestamps.
+Although it accommodates nonstandard TZif readers
+that ignore the extended POSIX TZ string,
+it increases the size of the altered output files.
+.TP
.BI "\*-t " file
When creating local time information, put the configuration link in
the named file rather than in the standard location.
@@ -253,7 +281,8 @@ format.
.PP
Input files should be text files, that is, they should be a series of
zero or more lines, each ending in a newline byte and containing at
-most 511 bytes, and without any NUL bytes. The input text's encoding
+most 2048 bytes counting the newline, and without any NUL bytes.
+The input text's encoding
is typically UTF-8 or ASCII; it should have a unibyte representation
for the POSIX Portable Character Set (PPCS)
\*
@@ -521,7 +550,7 @@ This field has the same format as the
.B AT
and
.B SAVE
-fields of rule lines;
+fields of rule lines, except without suffix letters;
begin the field with a minus sign if time must be subtracted from UT.
.TP
.B RULES
@@ -749,15 +778,13 @@ The
.BR DAY ,
and
.B HH:MM:SS
-fields give the expiration timestamp in UTC for the leap second table;
+fields give the expiration timestamp in UTC for the leap second table.
+.br
+.ne 22
.SH "EXTENDED EXAMPLE"
Here is an extended example of
.B zic
input, intended to illustrate many of its features.
-In this example, the EU rules are for the European Union
-and for its predecessor organization, the European Communities.
-.br
-.ne 22
.nf
.in +2m
.ta \w'# Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'\*-\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
@@ -784,13 +811,13 @@ Link Europe/Zurich Europe/Vaduz
.sp
.in
.fi
-In this example, the timezone is named Europe/Zurich but it has an alias
-as Europe/Vaduz. This example says that Zurich was 34 minutes and 8
+In this example, the EU rules are for the European Union
+and for its predecessor organization, the European Communities.
+The timezone is named Europe/Zurich and it has the alias Europe/Vaduz.
+This example says that Zurich was 34 minutes and 8
seconds east of UT until 1853-07-16 at 00:00, when the legal offset
was changed to
-.ds o 7 degrees 26 minutes 22.50 seconds
-.if \n(.g .if c \(de .if c \(fm .if c \(sd .ds o 7\(de\|26\(fm\|22.50\(sd
-\*o,
+7\*d\*_26\*m\*_22.50\*s,
which works out to 0:29:45.50;
.B zic
treats this by rounding it to 0:29:46.
diff --git a/zic.c b/zic.c
index 2d1a1879..a29f6792 100644
--- a/zic.c
+++ b/zic.c
@@ -29,7 +29,7 @@ typedef int_fast64_t zic_t;
#define SCNdZIC SCNdFAST64
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
-#define ZIC_MAX_ABBR_LEN_WO_WARN 6
+# define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifdef HAVE_DIRECT_H
@@ -40,12 +40,12 @@ typedef int_fast64_t zic_t;
#endif
#if HAVE_SYS_STAT_H
-#include
+# include
#endif
#ifdef S_IRUSR
-#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+# define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
#else
-#define MKDIR_UMASK 0755
+# define MKDIR_UMASK 0755
#endif
/* The maximum ptrdiff_t value, for pre-C99 platforms. */
@@ -58,6 +58,11 @@ static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
# define _Alignof(type) offsetof(struct { char a; type b; }, b)
#endif
+/* The maximum length of a text line, including the trailing newline. */
+#ifndef _POSIX2_LINE_MAX
+# define _POSIX2_LINE_MAX 2048
+#endif
+
/* The type for line numbers. Use PRIdMAX to format them; formerly
there was also "#define PRIdLINENO PRIdMAX" and formats used
PRIdLINENO, but xgettext cannot grok that. */
@@ -91,12 +96,13 @@ struct rule {
};
/*
-** r_dycode r_dayofmonth r_wday
+** r_dycode r_dayofmonth r_wday
*/
-
-#define DC_DOM 0 /* 1..31 */ /* unused */
-#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
-#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
+enum {
+ DC_DOM, /* 1..31 */ /* unused */
+ DC_DOWGEQ, /* 1..31 */ /* 0..6 (Sun..Sat) */
+ DC_DOWLEQ /* 1..31 */ /* 0..6 (Sun..Sat) */
+};
struct zone {
const char * z_filename;
@@ -145,7 +151,7 @@ static void leapadd(zic_t, int, int);
static void adjleap(void);
static void associate(void);
static void dolink(const char *, const char *, bool);
-static char ** getfields(char * buf);
+static int getfields(char *, char **, int);
static zic_t gethms(const char * string, const char * errstring);
static zic_t getsave(char *, bool *);
static void inexpires(char **, int);
@@ -208,86 +214,107 @@ static int unspecifiedtype;
** Line codes.
*/
-#define LC_RULE 0
-#define LC_ZONE 1
-#define LC_LINK 2
-#define LC_LEAP 3
-#define LC_EXPIRES 4
+enum {
+ LC_RULE,
+ LC_ZONE,
+ LC_LINK,
+ LC_LEAP,
+ LC_EXPIRES
+};
/*
** Which fields are which on a Zone line.
*/
-#define ZF_NAME 1
-#define ZF_STDOFF 2
-#define ZF_RULE 3
-#define ZF_FORMAT 4
-#define ZF_TILYEAR 5
-#define ZF_TILMONTH 6
-#define ZF_TILDAY 7
-#define ZF_TILTIME 8
-#define ZONE_MINFIELDS 5
-#define ZONE_MAXFIELDS 9
+enum {
+ ZF_NAME = 1,
+ ZF_STDOFF,
+ ZF_RULE,
+ ZF_FORMAT,
+ ZF_TILYEAR,
+ ZF_TILMONTH,
+ ZF_TILDAY,
+ ZF_TILTIME,
+ ZONE_MAXFIELDS,
+ ZONE_MINFIELDS = ZF_TILYEAR
+};
/*
** Which fields are which on a Zone continuation line.
*/
-#define ZFC_STDOFF 0
-#define ZFC_RULE 1
-#define ZFC_FORMAT 2
-#define ZFC_TILYEAR 3
-#define ZFC_TILMONTH 4
-#define ZFC_TILDAY 5
-#define ZFC_TILTIME 6
-#define ZONEC_MINFIELDS 3
-#define ZONEC_MAXFIELDS 7
+enum {
+ ZFC_STDOFF,
+ ZFC_RULE,
+ ZFC_FORMAT,
+ ZFC_TILYEAR,
+ ZFC_TILMONTH,
+ ZFC_TILDAY,
+ ZFC_TILTIME,
+ ZONEC_MAXFIELDS,
+ ZONEC_MINFIELDS = ZFC_TILYEAR
+};
/*
** Which files are which on a Rule line.
*/
-#define RF_NAME 1
-#define RF_LOYEAR 2
-#define RF_HIYEAR 3
-#define RF_COMMAND 4
-#define RF_MONTH 5
-#define RF_DAY 6
-#define RF_TOD 7
-#define RF_SAVE 8
-#define RF_ABBRVAR 9
-#define RULE_FIELDS 10
+enum {
+ RF_NAME = 1,
+ RF_LOYEAR,
+ RF_HIYEAR,
+ RF_COMMAND,
+ RF_MONTH,
+ RF_DAY,
+ RF_TOD,
+ RF_SAVE,
+ RF_ABBRVAR,
+ RULE_FIELDS
+};
/*
** Which fields are which on a Link line.
*/
-#define LF_TARGET 1
-#define LF_LINKNAME 2
-#define LINK_FIELDS 3
+enum {
+ LF_TARGET = 1,
+ LF_LINKNAME,
+ LINK_FIELDS
+};
/*
** Which fields are which on a Leap line.
*/
-#define LP_YEAR 1
-#define LP_MONTH 2
-#define LP_DAY 3
-#define LP_TIME 4
-#define LP_CORR 5
-#define LP_ROLL 6
-#define LEAP_FIELDS 7
+enum {
+ LP_YEAR = 1,
+ LP_MONTH,
+ LP_DAY,
+ LP_TIME,
+ LP_CORR,
+ LP_ROLL,
+ LEAP_FIELDS,
+
+ /* Expires lines are like Leap lines, except without CORR and ROLL fields. */
+ EXPIRES_FIELDS = LP_TIME + 1
+};
-/* Expires lines are like Leap lines, except without CORR and ROLL fields. */
-#define EXPIRES_FIELDS 5
+/* The maximum number of fields on any of the above lines.
+ (The "+"s pacify gcc -Wenum-compare.) */
+enum {
+ MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
+ max(+LEAP_FIELDS, +EXPIRES_FIELDS))
+};
/*
** Year synonyms.
*/
-#define YR_MINIMUM 0
-#define YR_MAXIMUM 1
-#define YR_ONLY 2
+enum {
+ YR_MINIMUM,
+ YR_MAXIMUM,
+ YR_ONLY
+};
static struct rule * rules;
static ptrdiff_t nrules; /* number of rules */
@@ -480,7 +507,7 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
return ptr;
else {
ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
- ptrdiff_t amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
+ ptrdiff_t amax = min(nitems_max, SIZE_MAX);
if ((amax - 1) / 3 * 2 < *nitems_alloc)
memory_exhausted(_("integer overflow"));
*nitems_alloc += (*nitems_alloc >> 1) + 1;
@@ -571,7 +598,8 @@ usage(FILE *stream, int status)
_("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
"\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
" [ -L leapseconds ] \\\n"
- "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -t localtime-link ] \\\n"
+ "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
+ "\t[ -t localtime-link ] \\\n"
"\t[ filename ... ]\n\n"
"Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO);
@@ -662,7 +690,7 @@ check_for_signal(void)
}
}
-#define TIME_T_BITS_IN_FILE 64
+enum { TIME_T_BITS_IN_FILE = 64 };
/* The minimum and maximum values representable in a TZif file. */
static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
@@ -673,6 +701,9 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+/* The time specified by the -R option, defaulting to MIN_TIME. */
+static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+
/* The time specified by an Expires line, or negative if no such line. */
static zic_t leapexpires = -1;
@@ -699,11 +730,27 @@ timerange_option(char *timerange)
}
if (*hi_end || hi < lo || max_time < lo || hi < min_time)
return false;
- lo_time = lo < min_time ? min_time : lo;
- hi_time = max_time < hi ? max_time : hi;
+ lo_time = max(lo, min_time);
+ hi_time = min(hi, max_time);
return true;
}
+/* Generate redundant time stamps up to OPT. Return true if successful. */
+static bool
+redundant_time_option(char *opt)
+{
+ if (*opt == '@') {
+ intmax_t redundant;
+ char *opt_end;
+ redundant = strtoimax(opt + 1, &opt_end, 10);
+ if (opt_end != opt + 1 && !*opt_end) {
+ redundant_time = max(redundant_time, redundant);
+ return true;
+ }
+ }
+ return false;
+}
+
static const char * psxrules;
static const char * lcltime;
static const char * directory;
@@ -737,9 +784,9 @@ main(int argc, char **argv)
#endif
#if HAVE_GETTEXT
setlocale(LC_ALL, "");
-#ifdef TZ_DOMAINDIR
+# ifdef TZ_DOMAINDIR
bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* defined TEXTDOMAINDIR */
+# endif /* defined TEXTDOMAINDIR */
textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */
progname = argv[0];
@@ -756,7 +803,8 @@ main(int argc, char **argv)
} else if (strcmp(argv[k], "--help") == 0) {
usage(stdout, EXIT_SUCCESS);
}
- while ((c = getopt(argc, argv, "b:d:l:L:p:r:st:vy:")) != EOF && c != -1)
+ while ((c = getopt(argc, argv, "b:d:l:L:p:r:R:st:vy:")) != EOF
+ && c != -1)
switch (c) {
default:
usage(stderr, EXIT_FAILURE);
@@ -843,12 +891,23 @@ _("%s: invalid time range: %s\n"),
}
timerange_given = true;
break;
+ case 'R':
+ if (! redundant_time_option(optarg)) {
+ fprintf(stderr, _("%s: invalid time: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ break;
case 's':
warning(_("-s ignored"));
break;
}
if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
usage(stderr, EXIT_FAILURE); /* usage message by request */
+ if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
+ fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
+ return EXIT_FAILURE;
+ }
if (bloat == 0) {
static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
if (strcmp(bloat_default, "slim") == 0)
@@ -1304,17 +1363,47 @@ associate(void)
exit(EXIT_FAILURE);
}
+/* Read a text line from FP into BUF, which is of size BUFSIZE.
+ Terminate it with a NUL byte instead of a newline.
+ Return the line's length, not counting the NUL byte.
+ On EOF, return a negative number.
+ On error, report the error and exit. */
+static ptrdiff_t
+inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
+{
+ ptrdiff_t linelen = 0, ch;
+ while ((ch = getc(fp)) != '\n') {
+ if (ch < 0) {
+ if (ferror(fp)) {
+ error(_("input error"));
+ exit(EXIT_FAILURE);
+ }
+ if (linelen == 0)
+ return -1;
+ error(_("unterminated line"));
+ exit(EXIT_FAILURE);
+ }
+ if (!ch) {
+ error(_("NUL input byte"));
+ exit(EXIT_FAILURE);
+ }
+ buf[linelen++] = ch;
+ if (linelen == bufsize) {
+ error(_("line too long"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ buf[linelen] = '\0';
+ return linelen;
+}
+
static void
infile(const char *name)
{
register FILE * fp;
- register char ** fields;
- register char * cp;
register const struct lookup * lp;
- register int nfields;
register bool wantcont;
register lineno num;
- char buf[BUFSIZ];
if (strcmp(name, "-") == 0) {
name = _("standard input");
@@ -1328,24 +1417,16 @@ infile(const char *name)
}
wantcont = false;
for (num = 1; ; ++num) {
+ ptrdiff_t linelen;
+ char buf[_POSIX2_LINE_MAX];
+ int nfields;
+ char *fields[MAX_FIELDS];
eat(name, num);
- if (fgets(buf, sizeof buf, fp) != buf)
- break;
- cp = strchr(buf, '\n');
- if (cp == NULL) {
- error(_("line too long"));
- exit(EXIT_FAILURE);
- }
- *cp = '\0';
- fields = getfields(buf);
- nfields = 0;
- while (fields[nfields] != NULL) {
- static char nada;
-
- if (strcmp(fields[nfields], "-") == 0)
- fields[nfields] = &nada;
- ++nfields;
- }
+ linelen = inputline(fp, buf, sizeof buf);
+ if (linelen < 0)
+ break;
+ nfields = getfields(buf, fields,
+ sizeof fields / sizeof *fields);
if (nfields == 0) {
/* nothing to do */
} else if (wantcont) {
@@ -1379,7 +1460,6 @@ infile(const char *name)
default: UNREACHABLE();
}
}
- free(fields);
}
close_file(fp, NULL, filename, NULL);
if (wantcont)
@@ -1960,11 +2040,11 @@ struct timerange {
int defaulttype;
ptrdiff_t base, count;
int leapbase, leapcount;
- bool pretrans, leapexpiry;
+ bool leapexpiry;
};
static struct timerange
-limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
+limitrange(struct timerange r, zic_t lo, zic_t hi,
zic_t const *ats, unsigned char const *types)
{
/* Omit ordinary transitions < LO. */
@@ -1974,10 +2054,6 @@ limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
r.base++;
}
- /* "-00" before any -r low cutoff. */
- if (min_time < lo_time)
- r.defaulttype = unspecifiedtype;
-
/* Omit as many initial leap seconds as possible, such that the
first leap second in the truncated list is <= LO, and is a
positive leap second if and only if it has a positive correction.
@@ -2003,14 +2079,6 @@ limitrange(struct timerange r, bool locut, zic_t lo, zic_t hi,
r.leapcount--;
}
- /* Determine whether to keep the last too-low transition if no
- transition is exactly at LO. The kept transition will be output
- as a LO "transition"; see "Output a LO_TIME transition" below.
- This is needed when the output is truncated at the start, and is
- also useful when catering to buggy 32-bit clients that do not use
- time type 0 for timestamps before the first transition. */
- r.pretrans = locut && r.base && ! (r.count && ats[r.base] == lo);
-
/* Determine whether to append an expiration to the leap second table. */
r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
@@ -2036,7 +2104,7 @@ writezone(const char *const name, const char *const string, char version,
_Alignof(zic_t)));
void *typesptr = ats + nats;
unsigned char *types = typesptr;
- struct timerange rangeall, range32, range64;
+ struct timerange rangeall = {0}, range32, range64;
/*
** Sort.
@@ -2120,14 +2188,13 @@ writezone(const char *const name, const char *const string, char version,
}
rangeall.defaulttype = defaulttype;
- rangeall.base = rangeall.leapbase = 0;
rangeall.count = timecnt;
rangeall.leapcount = leapcnt;
- rangeall.pretrans = rangeall.leapexpiry = false;
- range64 = limitrange(rangeall, min_time < lo_time,
- lo_time, hi_time, ats, types);
- range32 = limitrange(range64, true,
- INT32_MIN, INT32_MAX, ats, types);
+ range64 = limitrange(rangeall, lo_time,
+ max(hi_time,
+ redundant_time - (ZIC_MIN < redundant_time)),
+ ats, types);
+ range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
/* TZif version 4 is needed if a no-op transition is appended to
indicate the expiration of the leap second table, or if the first
@@ -2161,9 +2228,9 @@ writezone(const char *const name, const char *const string, char version,
register ptrdiff_t thistimei, thistimecnt, thistimelim;
register int thisleapi, thisleapcnt, thisleaplim;
struct tzhead tzh;
- int thisdefaulttype;
- bool hicut, pretrans, thisleapexpiry;
- zic_t lo;
+ int pretranstype = -1, thisdefaulttype;
+ bool locut, hicut, thisleapexpiry;
+ zic_t lo, thismin, thismax;
int old0;
char omittype[TZ_MAX_TYPES];
int typemap[TZ_MAX_TYPES];
@@ -2174,28 +2241,15 @@ writezone(const char *const name, const char *const string, char version,
int indmap[TZ_MAX_CHARS];
if (pass == 1) {
- /* Arguably the default time type in the 32-bit data
- should be range32.defaulttype, which is suited for
- timestamps just before INT32_MIN. However, zic
- traditionally used the time type of the indefinite
- past instead. Internet RFC 8532 says readers should
- ignore 32-bit data, so this discrepancy matters only
- to obsolete readers where the traditional type might
- be more appropriate even if it's "wrong". So, use
- the historical zic value, unless -r specifies a low
- cutoff that excludes some 32-bit timestamps. */
- thisdefaulttype = (lo_time <= INT32_MIN
- ? range64.defaulttype
- : range32.defaulttype);
-
+ thisdefaulttype = range32.defaulttype;
thistimei = range32.base;
thistimecnt = range32.count;
toomanytimes = thistimecnt >> 31 >> 1 != 0;
thisleapi = range32.leapbase;
thisleapcnt = range32.leapcount;
- pretrans = range32.pretrans;
thisleapexpiry = range32.leapexpiry;
- hicut = hi_time < INT32_MAX;
+ thismin = INT32_MIN;
+ thismax = INT32_MAX;
} else {
thisdefaulttype = range64.defaulttype;
thistimei = range64.base;
@@ -2203,17 +2257,46 @@ writezone(const char *const name, const char *const string, char version,
toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
thisleapi = range64.leapbase;
thisleapcnt = range64.leapcount;
- pretrans = range64.pretrans;
thisleapexpiry = range64.leapexpiry;
- hicut = hi_time < max_time;
+ thismin = min_time;
+ thismax = max_time;
}
if (toomanytimes)
error(_("too many transition times"));
+ locut = thismin < lo_time && lo_time <= thismax;
+ hicut = thismin <= hi_time && hi_time < thismax;
thistimelim = thistimei + thistimecnt;
memset(omittype, true, typecnt);
+
+ /* Determine whether to output a transition before the first
+ transition in range. This is needed when the output is
+ truncated at the start, and is also useful when catering to
+ buggy 32-bit clients that do not use time type 0 for
+ timestamps before the first transition. */
+ if ((locut || (pass == 1 && thistimei))
+ && ! (thistimecnt && ats[thistimei] == lo_time)) {
+ pretranstype = thisdefaulttype;
+ omittype[pretranstype] = false;
+ }
+
+ /* Arguably the default time type in the 32-bit data
+ should be range32.defaulttype, which is suited for
+ timestamps just before INT32_MIN. However, zic
+ traditionally used the time type of the indefinite
+ past instead. Internet RFC 8532 says readers should
+ ignore 32-bit data, so this discrepancy matters only
+ to obsolete readers where the traditional type might
+ be more appropriate even if it's "wrong". So, use
+ the historical zic value, unless -r specifies a low
+ cutoff that excludes some 32-bit timestamps. */
+ if (pass == 1 && lo_time <= thismin)
+ thisdefaulttype = range64.defaulttype;
+
+ if (locut)
+ thisdefaulttype = unspecifiedtype;
omittype[thisdefaulttype] = false;
- for (i = thistimei - pretrans; i < thistimelim; i++)
+ for (i = thistimei; i < thistimelim; i++)
omittype[types[i]] = false;
if (hicut)
omittype[unspecifiedtype] = false;
@@ -2237,7 +2320,13 @@ writezone(const char *const name, const char *const string, char version,
register int mrudst, mrustd, hidst, histd, type;
hidst = histd = mrudst = mrustd = -1;
- for (i = thistimei - pretrans; i < thistimelim; ++i)
+ if (0 <= pretranstype) {
+ if (isdsts[pretranstype])
+ mrudst = pretranstype;
+ else
+ mrustd = pretranstype;
+ }
+ for (i = thistimei; i < thistimelim; i++)
if (isdsts[types[i]])
mrudst = types[i];
else mrustd = types[i];
@@ -2307,7 +2396,8 @@ writezone(const char *const name, const char *const string, char version,
indmap[desigidx[i]] = j;
}
if (pass == 1 && !want_bloat()) {
- pretrans = hicut = thisleapexpiry = false;
+ hicut = thisleapexpiry = false;
+ pretranstype = -1;
thistimecnt = thisleapcnt = 0;
thistypecnt = thischarcnt = 1;
}
@@ -2318,7 +2408,8 @@ writezone(const char *const name, const char *const string, char version,
convert(utcnt, tzh.tzh_ttisutcnt);
convert(stdcnt, tzh.tzh_ttisstdcnt);
convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
- convert(pretrans + thistimecnt + hicut, tzh.tzh_timecnt);
+ convert((0 <= pretranstype) + thistimecnt + hicut,
+ tzh.tzh_timecnt);
convert(thistypecnt, tzh.tzh_typecnt);
convert(thischarcnt, tzh.tzh_charcnt);
DO(tzh_magic);
@@ -2345,14 +2436,16 @@ writezone(const char *const name, const char *const string, char version,
for this pass. */
lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time;
- if (pretrans)
+ if (0 <= pretranstype)
puttzcodepass(lo, fp, pass);
for (i = thistimei; i < thistimelim; ++i) {
puttzcodepass(ats[i], fp, pass);
}
if (hicut)
puttzcodepass(hi_time + 1, fp, pass);
- for (i = thistimei - pretrans; i < thistimelim; ++i)
+ if (0 <= pretranstype)
+ putc(typemap[pretranstype], fp);
+ for (i = thistimei; i < thistimelim; i++)
putc(typemap[types[i]], fp);
if (hicut)
putc(typemap[unspecifiedtype], fp);
@@ -2453,6 +2546,8 @@ abbroffset(char *buf, zic_t offset)
}
}
+static char const disable_percent_s[] = "";
+
static size_t
doabbr(char *abbr, struct zone const *zp, char const *letters,
bool isdst, zic_t save, bool doquotes)
@@ -2469,6 +2564,8 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
letters = abbroffset(letterbuf, zp->z_stdoff + save);
else if (!letters)
letters = "%s";
+ else if (letters == disable_percent_s)
+ return 0;
sprintf(abbr, format, letters);
} else if (isdst) {
strcpy(abbr, slashp + 1);
@@ -2739,18 +2836,10 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
static void
outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
{
- register const struct zone * zp;
- register struct rule * rp;
register ptrdiff_t i, j;
- register bool usestart, useuntil;
register zic_t starttime, untiltime;
- register zic_t stdoff;
- register zic_t save;
- register zic_t year;
- register zic_t startoff;
register bool startttisstd;
register bool startttisut;
- register int type;
register char * startbuf;
register char * ab;
register char * envvar;
@@ -2761,8 +2850,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
register bool do_extend;
register char version;
ptrdiff_t lastatmax = -1;
- zic_t one = 1;
- zic_t y2038_boundary = one << 31;
zic_t max_year0;
int defaulttype = -1;
@@ -2794,11 +2881,11 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
}
for (i = 0; i < zonecount; ++i) {
- zp = &zpfirst[i];
+ struct zone const *zp = &zpfirst[i];
if (i < zonecount - 1)
updateminmax(zp->z_untilrule.r_loyear);
for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
+ struct rule *rp = &zp->z_rules[j];
if (rp->r_lowasnum)
updateminmax(rp->r_loyear);
if (rp->r_hiwasnum)
@@ -2860,6 +2947,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
max_year = min_year + years_of_observations;
}
}
+ max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
+ + EPOCH_YEAR + 1));
max_year0 = max_year;
if (want_bloat()) {
/* For the benefit of older systems,
@@ -2875,22 +2964,23 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
for (i = 0; i < zonecount; ++i) {
struct rule *prevrp = NULL;
- zic_t prevktime;
- INITIALIZE(prevktime);
/*
** A guess that may well be corrected later.
*/
- save = 0;
- zp = &zpfirst[i];
- usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
- useuntil = i < (zonecount - 1);
+ zic_t save = 0;
+ struct zone const *zp = &zpfirst[i];
+ bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ bool useuntil = i < (zonecount - 1);
+ zic_t stdoff = zp->z_stdoff;
+ zic_t startoff = stdoff;
+ zic_t prevktime;
+ INITIALIZE(prevktime);
if (useuntil && zp->z_untiltime <= min_time)
continue;
- stdoff = zp->z_stdoff;
eat(zp->z_filename, zp->z_linenum);
*startbuf = '\0';
- startoff = zp->z_stdoff;
if (zp->z_nrules == 0) {
+ int type;
save = zp->z_save;
doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
type = addtype(oadd(zp->z_stdoff, save),
@@ -2901,7 +2991,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
usestart = false;
} else
defaulttype = type;
- } else for (year = min_year; year <= max_year; ++year) {
+ } else {
+ zic_t year;
+ for (year = min_year; year <= max_year; ++year) {
if (useuntil && year > zp->z_untilrule.r_hiyear)
break;
/*
@@ -2910,7 +3002,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
** The former TYPE field was also considered here.
*/
for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
+ zic_t one = 1;
+ zic_t y2038_boundary = one << 31;
+ struct rule *rp = &zp->z_rules[j];
eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum);
rp->r_todo = year >= rp->r_loyear &&
@@ -2926,6 +3020,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
register ptrdiff_t k;
register zic_t jtime, ktime;
register zic_t offset;
+ struct rule *rp;
+ int type;
INITIALIZE(ktime);
if (useuntil) {
@@ -2948,15 +3044,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
*/
k = -1;
for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
- if (!rp->r_todo)
+ struct rule *r = &zp->z_rules[j];
+ if (!r->r_todo)
continue;
eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
- offset = rp->r_todisut ? 0 : stdoff;
- if (!rp->r_todisstd)
+ r->r_filename, r->r_linenum);
+ offset = r->r_todisut ? 0 : stdoff;
+ if (!r->r_todisstd)
offset = oadd(offset, save);
- jtime = rp->r_temp;
+ jtime = r->r_temp;
if (jtime == min_time ||
jtime == max_time)
continue;
@@ -2968,11 +3064,11 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
char const *dup_rules_msg =
_("two rules for same instant");
eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
+ r->r_filename, r->r_linenum);
warning("%s", dup_rules_msg);
- rp = &zp->z_rules[k];
+ r = &zp->z_rules[k];
eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
+ r->r_filename, r->r_linenum);
error("%s", dup_rules_msg);
}
}
@@ -2980,8 +3076,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
break; /* go on to next year */
rp = &zp->z_rules[k];
rp->r_todo = false;
- if (useuntil && ktime >= untiltime)
+ if (useuntil && ktime >= untiltime) {
+ if (!*startbuf
+ && (oadd(zp->z_stdoff, rp->r_save)
+ == startoff))
+ doabbr(startbuf, zp, rp->r_abbrvar,
+ rp->r_isdst, rp->r_save,
+ false);
break;
+ }
save = rp->r_save;
if (usestart && ktime == starttime)
usestart = false;
@@ -3014,6 +3117,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
offset = oadd(zp->z_stdoff, rp->r_save);
if (!want_bloat() && !useuntil && !do_extend
&& prevrp && lo_time <= prevktime
+ && redundant_time <= ktime
&& rp->r_hiyear == ZIC_MAX
&& prevrp->r_hiyear == ZIC_MAX)
break;
@@ -3029,20 +3133,19 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
prevrp = rp;
prevktime = ktime;
}
+ }
}
if (usestart) {
- if (*startbuf == '\0' &&
- zp->z_format != NULL &&
- strchr(zp->z_format, '%') == NULL &&
- strchr(zp->z_format, '/') == NULL)
- strcpy(startbuf, zp->z_format);
+ bool isdst = startoff != zp->z_stdoff;
+ if (*startbuf == '\0' && zp->z_format)
+ doabbr(startbuf, zp, disable_percent_s,
+ isdst, save, false);
eat(zp->z_filename, zp->z_linenum);
if (*startbuf == '\0')
error(_("can't determine time zone abbreviation to use just after until time"));
else {
- bool isdst = startoff != zp->z_stdoff;
- type = addtype(startoff, startbuf, isdst,
- startttisstd, startttisut);
+ int type = addtype(startoff, startbuf, isdst,
+ startttisstd, startttisut);
if (defaulttype < 0 && !isdst)
defaulttype = type;
addtt(starttime, type);
@@ -3344,23 +3447,20 @@ byword(const char *word, const struct lookup *table)
return foundlp;
}
-static char **
-getfields(register char *cp)
+static int
+getfields(char *cp, char **array, int arrayelts)
{
register char * dp;
- register char ** array;
register int nsubs;
- if (cp == NULL)
- return NULL;
- array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
nsubs = 0;
for ( ; ; ) {
+ char *dstart;
while (is_space(*cp))
++cp;
if (*cp == '\0' || *cp == '#')
break;
- array[nsubs++] = dp = cp;
+ dstart = dp = cp;
do {
if ((*dp = *cp++) != '"')
++dp;
@@ -3375,9 +3475,13 @@ getfields(register char *cp)
if (is_space(*cp))
++cp;
*dp = '\0';
+ if (nsubs == arrayelts) {
+ error(_("Too many input fields"));
+ exit(EXIT_FAILURE);
+ }
+ array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
}
- array[nsubs] = NULL;
- return array;
+ return nsubs;
}
static _Noreturn void
diff --git a/ziguard.awk b/ziguard.awk
index 2be6d52b..0728baa5 100644
--- a/ziguard.awk
+++ b/ziguard.awk
@@ -9,7 +9,11 @@
# it does not do these nonessential tasks now.
#
# Although main and vanguard forms are currently equivalent,
-# this need not always be the case.
+# this need not always be the case. When the two forms differ,
+# this script can convert either from main to vanguard form (needed then),
+# or from vanguard to main form (this conversion would be needed later,
+# after main became rearguard and vanguard became main).
+# There is no need to convert rearguard to other forms.
#
# When converting to vanguard form, the output can use negative SAVE
# values.
@@ -19,14 +23,69 @@
# of the input data as best it can within the constraints of the
# rearguard format.
+# Given a FIELD like "-0:30", return a minute count like -30.
+function get_minutes(field, \
+ sign, hours, minutes)
+{
+ sign = field ~ /^-/ ? -1 : 1
+ hours = +field
+ if (field ~ /:/) {
+ minutes = field
+ sub(/[^:]*:/, "", minutes)
+ }
+ return 60 * hours + sign * minutes
+}
+
+# Given an OFFSET, which is a minute count like 300 or 330,
+# return a %z-style abbreviation like "+05" or "+0530".
+function offset_abbr(offset, \
+ hours, minutes, sign)
+{
+ hours = int(offset / 60)
+ minutes = offset % 60
+ if (minutes) {
+ return sprintf("%+.4d", hours * 100 + minutes);
+ } else {
+ return sprintf("%+.2d", hours)
+ }
+}
+
+# Round TIMESTAMP (a +-hh:mm:ss.dddd string) to the nearest second.
+function round_to_second(timestamp, \
+ hh, mm, ss, seconds, dot_dddd, subseconds)
+{
+ dot_dddd = timestamp
+ if (!sub(/^[+-]?[0-9]+:[0-9]+:[0-9]+\./, ".", dot_dddd))
+ return timestamp
+ hh = mm = ss = timestamp
+ sub(/^[-+]?[0-9]+:[0-9]+:/, "", ss)
+ sub(/^[-+]?[0-9]+:/, "", mm)
+ sub(/^[-+]?/, "", hh)
+ seconds = 3600 * hh + 60 * mm + ss
+ subseconds = +dot_dddd
+ seconds += 0.5 < subseconds || ((subseconds == 0.5) && (seconds % 2));
+ return sprintf("%s%d:%.2d:%.2d", timestamp ~ /^-/ ? "-" : "", \
+ seconds / 3600, seconds / 60 % 60, seconds % 60)
+}
+
BEGIN {
dataform_type["vanguard"] = 1
dataform_type["main"] = 1
dataform_type["rearguard"] = 1
+ if (PACKRATLIST) {
+ while (getline =8 25:00"
- # to "Sun>=9 1:00", to cater to zic before 2007 and to older Java.
- if (!vanguard && $1 == "Rule" && $7 == "Sat>=8" && $8 == "25:00") {
- sub(/Sat>=8/, "Sun>=9")
- sub(/25:00/, " 1:00")
+ # Prefer %z in vanguard form, explicit abbreviations otherwise.
+ if (DATAFORM == "vanguard") {
+ sub(/^(Zone[\t ]+[^\t ]+)?[\t ]+[^\t ]+[\t ]+[^\t ]+[\t ]+[-+][^\t ]+/, \
+ "&CHANGE-TO-%z")
+ sub(/-00CHANGE-TO-%z/, "-00")
+ sub(/[-+][^\t ]+CHANGE-TO-/, "")
+ } else {
+ if (/^[^#]*%z/) {
+ stdoff_column = 2 * /^Zone/ + 1
+ rules_column = stdoff_column + 1
+ stdoff = get_minutes($stdoff_column)
+ rules = $rules_column
+ stdabbr = offset_abbr(stdoff)
+ if (rules == "-") {
+ abbr = stdabbr
+ } else {
+ dstabbr_only = rules ~ /^[+0-9-]/
+ if (dstabbr_only) {
+ dstoff = get_minutes(rules)
+ } else {
+ # The DST offset is normally an hour, but there are special cases.
+ if (rules == "Morocco" && NF == 3) {
+ dstoff = -60
+ } else if (rules == "NBorneo") {
+ dstoff = 20
+ } else if (((rules == "Cook" || rules == "LH") && NF == 3) \
+ || (rules == "Uruguay" \
+ && /[\t ](1942 Dec 14|1960|1970|1974 Dec 22)$/)) {
+ dstoff = 30
+ } else if (rules == "Uruguay" && /[\t ]1974 Mar 10$/) {
+ dstoff = 90
+ } else {
+ dstoff = 60
+ }
+ }
+ dstabbr = offset_abbr(stdoff + dstoff)
+ if (dstabbr_only) {
+ abbr = dstabbr
+ } else {
+ abbr = stdabbr "/" dstabbr
+ }
+ }
+ sub(/%z/, abbr)
+ }
}
- # In rearguard format, change the Morocco lines with negative SAVE values
- # to use positive SAVE values.
- if (!vanguard && $1 == "Rule" && $2 == "Morocco" && $4 == 2018 \
- && $6 == "Oct") {
- sub(/\t2018\t/, "\t2017\t")
+ # Normally, prefer whole seconds. However, prefer subseconds
+ # if generating vanguard form and the otherwise-undocumented
+ # VANGUARD_SUBSECONDS environment variable is set.
+ # This relies on #STDOFF comment lines in the data.
+ # It is for hypothetical clients that support UT offsets that are
+ # not integer multiples of one second (e.g., Europe/Lisbon, 1884 to 1912).
+ # No known clients need this currently, and this experimental
+ # feature may be changed or withdrawn in future releases.
+ if ($1 == "#STDOFF") {
+ stdoff = $2
+ rounded_stdoff = round_to_second(stdoff)
+ if (DATAFORM == "vanguard" && ENVIRON["VANGUARD_SUBSECONDS"]) {
+ stdoff_subst[0] = rounded_stdoff
+ stdoff_subst[1] = stdoff
+ } else {
+ stdoff_subst[0] = stdoff
+ stdoff_subst[1] = rounded_stdoff
+ }
+ } else if (stdoff_subst[0]) {
+ stdoff_column = 2 * /^Zone/ + 1
+ stdoff_column_val = $stdoff_column
+ if (stdoff_column_val == stdoff_subst[0]) {
+ sub(stdoff_subst[0], stdoff_subst[1])
+ } else if (stdoff_column_val != stdoff_subst[1]) {
+ stdoff_subst[0] = 0
+ }
}
- if (!vanguard && $1 == "Rule" && $2 == "Morocco" && 2019 <= $3) {
- if ($9 == "0") {
- last_std_date = $3 " " $6 " " $7 " " $8
- sub(/\t0\t/, "\t1:00\t")
+
+ # In rearguard form, change the Japan rule line with "Sat>=8 25:00"
+ # to "Sun>=9 1:00", to cater to zic before 2007 and to older Java.
+ if (/^Rule/ && $2 == "Japan") {
+ if (DATAFORM == "rearguard") {
+ if ($7 == "Sat>=8" && $8 == "25:00") {
+ sub(/Sat>=8/, "Sun>=9")
+ sub(/25:00/, " 1:00")
+ }
} else {
- sub(/\t-1:00\t/, "\t0\t")
+ if ($7 == "Sun>=9" && $8 == "1:00") {
+ sub(/Sun>=9/, "Sat>=8")
+ sub(/ 1:00/, "25:00")
+ }
}
}
- if (!vanguard && $1 == "1:00" && $2 == "Morocco" && $3 == "+01/+00") {
- # This introduces a transition from 01:59:59 +00 to 03:00:00 +01
- # with both times being standard (i.e., a change to standard UT offset).
- # This is rearguard's way to approximate the actual prediction,
- # which is that of an ordinary transition from DST to standard time.
- sub(/1:00\tMorocco\t\+01\/\+00$/,
- "0:00\tMorocco\t+00/+01\t" last_std_date "\n\t\t\t 1:00\t-\t+01")
+
+ # In rearguard form, change the Morocco lines with negative SAVE values
+ # to use positive SAVE values.
+ if ($2 == "Morocco") {
+ if (/^Rule/) {
+ if ($4 ~ /^201[78]$/ && $6 == "Oct") {
+ if (DATAFORM == "rearguard") {
+ sub(/\t2018\t/, "\t2017\t")
+ } else {
+ sub(/\t2017\t/, "\t2018\t")
+ }
+ }
+
+ if (2019 <= $3) {
+ if ($8 == "2:00") {
+ if (DATAFORM == "rearguard") {
+ sub(/\t0\t/, "\t1:00\t")
+ } else {
+ sub(/\t1:00\t/, "\t0\t")
+ }
+ } else {
+ if (DATAFORM == "rearguard") {
+ sub(/\t-1:00\t/, "\t0\t")
+ } else {
+ sub(/\t0\t/, "\t-1:00\t")
+ }
+ }
+ }
+ }
+ if ($1 ~ /^[+0-9-]/ && NF == 3) {
+ if (DATAFORM == "rearguard") {
+ sub(/1:00\tMorocco/, "0:00\tMorocco")
+ sub(/\t\+01\/\+00$/, "\t+00/+01")
+ } else {
+ sub(/0:00\tMorocco/, "1:00\tMorocco")
+ sub(/\t\+00\/+01$/, "\t+01/+00")
+ }
+ }
}
}
+/^Zone/ {
+ packrat_ignored = FILENAME == PACKRATDATA && PACKRATLIST && !packratlist[$2];
+}
+packrat_ignored && !/^Rule/ {
+ sub(/^/, "#")
+}
+
# If a Link line is followed by a Link or Zone line for the same data, comment
# out the Link line. This can happen if backzone overrides a Link
# with a Zone or a different Link.
diff --git a/zishrink.awk b/zishrink.awk
index 1947c7c5..66968e86 100644
--- a/zishrink.awk
+++ b/zishrink.awk
@@ -23,7 +23,7 @@ function record_hash(n, name)
function gen_rule_name(name, \
n)
{
- # Use a simple memonic: the first two letters.
+ # Use a simple mnemonic: the first two letters.
n = substr(name, 1, 2)
record_hash(n, name)
# printf "# %s = %s\n", n, name
@@ -150,10 +150,19 @@ function prehash_rule_names( \
}
}
+function make_line(n, field, \
+ f, r)
+{
+ r = field[1]
+ for (f = 2; f <= n; f++)
+ r = r " " field[f]
+ return r
+}
+
# Process the input line LINE and save it for later output.
function process_input_line(line, \
- field, end, i, n, startdef, \
+ f, field, end, i, n, r, startdef, \
linkline, ruleline, zoneline)
{
# Remove comments, normalize spaces, and append a space to each line.
@@ -218,12 +227,11 @@ function process_input_line(line, \
n = split(line, field)
- # Abbreviate rule names.
- i = zoneline ? 4 : linkline ? 0 : 2
- if (i && field[i] ~ /^[^-+0-9]/) {
- if (!rule[field[i]])
- rule[field[i]] = gen_rule_name(field[i])
- field[i] = rule[field[i]]
+ # Record which rule names are used, and generate their abbreviations.
+ f = zoneline ? 4 : linkline || ruleline ? 0 : 2
+ r = field[f]
+ if (r ~ /^[^-+0-9]/) {
+ rule_used[r] = 1
}
# If this zone supersedes an earlier one, delete the earlier one
@@ -246,10 +254,38 @@ function process_input_line(line, \
zonedef[zonename] = nout + 1
# Save the line for later output.
- line = field[1]
- for (i = 2; i <= n; i++)
- line = line " " field[i]
- output_line[nout++] = line
+ output_line[nout++] = make_line(n, field)
+}
+
+function omit_unused_rules( \
+ i, field)
+{
+ for (i = 0; i < nout; i++) {
+ split(output_line[i], field)
+ if (field[1] == "R" && !rule_used[field[2]]) {
+ output_line[i] = ""
+ }
+ }
+}
+
+function abbreviate_rule_names( \
+ abbr, f, field, i, n, r)
+{
+ for (i = 0; i < nout; i++) {
+ n = split(output_line[i], field)
+ if (n) {
+ f = field[1] == "Z" ? 4 : field[1] == "L" ? 0 : 2
+ r = field[f]
+ if (r ~ /^[^-+0-9]/) {
+ abbr = rule[r]
+ if (!abbr) {
+ rule[r] = abbr = gen_rule_name(r)
+ }
+ field[f] = abbr
+ output_line[i] = make_line(n, field)
+ }
+ }
+ }
}
function output_saved_lines( \
@@ -314,5 +350,7 @@ BEGIN {
}
END {
+ omit_unused_rules()
+ abbreviate_rule_names()
output_saved_lines()
}
diff --git a/zone.tab b/zone.tab
index 086458fb..1f73ddaf 100644
--- a/zone.tab
+++ b/zone.tab
@@ -130,7 +130,7 @@ CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west)
CA +6227-11421 America/Yellowknife Mountain - NT (central)
CA +682059-1334300 America/Inuvik Mountain - NT (west)
CA +4906-11631 America/Creston MST - BC (Creston)
-CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
+CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson)
CA +6043-13503 America/Whitehorse MST - Yukon (east)
CA +6404-13925 America/Dawson MST - Yukon (west)
@@ -400,7 +400,7 @@ TT +1039-06131 America/Port_of_Spain
TV -0831+17913 Pacific/Funafuti
TW +2503+12130 Asia/Taipei
TZ -0648+03917 Africa/Dar_es_Salaam
-UA +5026+03031 Europe/Kiev Ukraine (most areas)
+UA +5026+03031 Europe/Kyiv Ukraine (most areas)
UA +4837+02218 Europe/Uzhgorod Transcarpathia
UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk
UG +0019+03225 Africa/Kampala
diff --git a/zone1970.tab b/zone1970.tab
index c614be81..3e1948cb 100644
--- a/zone1970.tab
+++ b/zone1970.tab
@@ -34,7 +34,7 @@
#country-
#codes coordinates TZ comments
AD +4230+00131 Europe/Andorra
-AE,OM +2518+05518 Asia/Dubai
+AE,OM,RE,SC,TF +2518+05518 Asia/Dubai UAE, Oman, Réunion, Seychelles, Crozet, Scattered Is
AF +3431+06912 Asia/Kabul
AL +4120+01950 Europe/Tirane
AM +4011+04430 Asia/Yerevan
@@ -44,7 +44,6 @@ AQ -6736+06253 Antarctica/Mawson Mawson
AQ -6448-06406 Antarctica/Palmer Palmer
AQ -6734-06808 Antarctica/Rothera Rothera
AQ -720041+0023206 Antarctica/Troll Troll
-AQ -7824+10654 Antarctica/Vostok Vostok
AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF)
AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF)
AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN)
@@ -74,10 +73,9 @@ AU -3143+12852 Australia/Eucla Western Australia (Eucla)
AZ +4023+04951 Asia/Baku
BB +1306-05937 America/Barbados
BD +2343+09025 Asia/Dhaka
-BE +5050+00420 Europe/Brussels
+BE,LU,NL +5050+00420 Europe/Brussels
BG +4241+02319 Europe/Sofia
BM +3217-06446 Atlantic/Bermuda
-BN +0456+11455 Asia/Brunei
BO -1630-06809 America/La_Paz
BR -0351-03225 America/Noronha Atlantic islands
BR -0127-04829 America/Belem Pará (east); Amapá
@@ -118,30 +116,27 @@ CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W)
CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west)
CA +6227-11421 America/Yellowknife Mountain - NT (central)
CA +682059-1334300 America/Inuvik Mountain - NT (west)
-CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
+CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson)
CA +6043-13503 America/Whitehorse MST - Yukon (east)
CA +6404-13925 America/Dawson MST - Yukon (west)
CA +4916-12307 America/Vancouver Pacific - BC (most areas)
-CC -1210+09655 Indian/Cocos
CH,DE,LI +4723+00832 Europe/Zurich Swiss time
-CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan
+CI,BF,GH,GM,GN,IS,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan
CK -2114-15946 Pacific/Rarotonga
CL -3327-07040 America/Santiago Chile (most areas)
CL -5309-07055 America/Punta_Arenas Region of Magallanes
CL -2709-10926 Pacific/Easter Easter Island
CN +3114+12128 Asia/Shanghai Beijing Time
-CN +4348+08735 Asia/Urumqi Xinjiang Time
+CN,AQ +4348+08735 Asia/Urumqi Xinjiang Time, Vostok
CO +0436-07405 America/Bogota
CR +0956-08405 America/Costa_Rica
CU +2308-08222 America/Havana
CV +1455-02331 Atlantic/Cape_Verde
-CX -1025+10543 Indian/Christmas
CY +3510+03322 Asia/Nicosia Cyprus (most areas)
CY +3507+03357 Asia/Famagusta Northern Cyprus
CZ,SK +5005+01426 Europe/Prague
-DE +5230+01322 Europe/Berlin Germany (most areas)
-DK +5540+01235 Europe/Copenhagen
+DE,DK,NO,SE,SJ +5230+01322 Europe/Berlin Germany (most areas), Scandinavia
DO +1828-06954 America/Santo_Domingo
DZ +3647+00303 Africa/Algiers
EC -0210-07950 America/Guayaquil Ecuador (mainland)
@@ -155,11 +150,9 @@ ES +2806-01524 Atlantic/Canary Canary Islands
FI,AX +6010+02458 Europe/Helsinki
FJ -1808+17825 Pacific/Fiji
FK -5142-05751 Atlantic/Stanley
-FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap
-FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape
FM +0519+16259 Pacific/Kosrae Kosrae
FO +6201-00646 Atlantic/Faroe
-FR +4852+00220 Europe/Paris
+FR,MC +4852+00220 Europe/Paris
GB,GG,IM,JE +513030-0000731 Europe/London
GE +4143+04449 Asia/Tbilisi
GF +0456-05220 America/Cayenne
@@ -188,14 +181,13 @@ IN +2232+08822 Asia/Kolkata
IO -0720+07225 Indian/Chagos
IQ +3321+04425 Asia/Baghdad
IR +3540+05126 Asia/Tehran
-IS +6409-02151 Atlantic/Reykjavik
IT,SM,VA +4154+01229 Europe/Rome
JM +175805-0764736 America/Jamaica
JO +3157+03556 Asia/Amman
JP +353916+1394441 Asia/Tokyo
KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi
KG +4254+07436 Asia/Bishkek
-KI +0125+17300 Pacific/Tarawa Gilbert Islands
+KI,MH,TV,UM,WF +0125+17300 Pacific/Tarawa Gilberts, Marshalls, Tuvalu, Wallis & Futuna, Wake
KI -0247-17143 Pacific/Kanton Phoenix Islands
KI +0152-15720 Pacific/Kiritimati Line Islands
KP +3901+12545 Asia/Pyongyang
@@ -211,15 +203,12 @@ LB +3353+03530 Asia/Beirut
LK +0656+07951 Asia/Colombo
LR +0618-01047 Africa/Monrovia
LT +5441+02519 Europe/Vilnius
-LU +4936+00609 Europe/Luxembourg
LV +5657+02406 Europe/Riga
LY +3254+01311 Africa/Tripoli
MA +3339-00735 Africa/Casablanca
-MC +4342+00723 Europe/Monaco
MD +4700+02850 Europe/Chisinau
-MH +0709+17112 Pacific/Majuro Marshall Islands (most areas)
MH +0905+16720 Pacific/Kwajalein Kwajalein
-MM +1647+09610 Asia/Yangon
+MM,CC +1647+09610 Asia/Yangon
MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas)
MN +4801+09139 Asia/Hovd Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan
MN +4804+11430 Asia/Choibalsan Dornod, Sükhbaatar
@@ -227,7 +216,7 @@ MO +221150+1133230 Asia/Macau
MQ +1436-06105 America/Martinique
MT +3554+01431 Europe/Malta
MU -2010+05730 Indian/Mauritius
-MV +0410+07330 Indian/Maldives
+MV,TF +0410+07330 Indian/Maldives Maldives, Kerguelen, St Paul I, Amsterdam I
MX +1924-09909 America/Mexico_City Central Time
MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo
MX +2058-08937 America/Merida Central Time - Campeche, Yucatán
@@ -239,16 +228,13 @@ MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border)
MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora
MX +3232-11701 America/Tijuana Pacific Time US - Baja California
MX +2048-10515 America/Bahia_Banderas Central Time - Bahía de Banderas
-MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula)
-MY +0133+11020 Asia/Kuching Sabah, Sarawak
+MY,BN +0133+11020 Asia/Kuching Sabah, Sarawak, Brunei
MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time
NA -2234+01706 Africa/Windhoek
NC -2216+16627 Pacific/Noumea
NF -2903+16758 Pacific/Norfolk
NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time
NI +1209-08617 America/Managua
-NL +5222+00454 Europe/Amsterdam
-NO,SJ +5955+01045 Europe/Oslo
NP +2743+08519 Asia/Kathmandu
NR -0031+16655 Pacific/Nauru
NU -1901-16955 Pacific/Niue
@@ -259,7 +245,7 @@ PE -1203-07703 America/Lima
PF -1732-14934 Pacific/Tahiti Society Islands
PF -0900-13930 Pacific/Marquesas Marquesas Islands
PF -2308-13457 Pacific/Gambier Gambier Islands
-PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville
+PG,AQ,FM -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Chuuk, Yap, Dumont d'Urville
PG -0613+15534 Pacific/Bougainville Bougainville
PH +1435+12100 Asia/Manila
PK +2452+06703 Asia/Karachi
@@ -275,7 +261,6 @@ PT +3744-02540 Atlantic/Azores Azores
PW +0720+13429 Pacific/Palau
PY -2516-05740 America/Asuncion
QA,BH +2517+05132 Asia/Qatar
-RE,TF -2052+05528 Indian/Reunion Réunion, Crozet, Scattered Islands
RO +4426+02606 Europe/Bucharest
RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade
RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad
@@ -307,10 +292,8 @@ RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is
RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka
RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea
SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa
-SB -0932+16012 Pacific/Guadalcanal
-SC -0440+05528 Indian/Mahe
+SB,FM -0932+16012 Pacific/Guadalcanal Solomons, Pohnpei
SD +1536+03232 Africa/Khartoum
-SE +5920+01803 Europe/Stockholm
SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia
SR +0550-05510 America/Paramaribo
SS +0451+03137 Africa/Juba
@@ -319,8 +302,7 @@ SV +1342-08912 America/El_Salvador
SY +3330+03618 Asia/Damascus
TC +2128-07108 America/Grand_Turk
TD +1207+01503 Africa/Ndjamena
-TF -492110+0701303 Indian/Kerguelen Kerguelen, St Paul Island, Amsterdam Island
-TH,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas)
+TH,CX,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas)
TJ +3835+06848 Asia/Dushanbe
TK -0922-17114 Pacific/Fakaofo
TL -0833+12535 Asia/Dili
@@ -328,12 +310,10 @@ TM +3757+05823 Asia/Ashgabat
TN +3648+01011 Africa/Tunis
TO -210800-1751200 Pacific/Tongatapu
TR +4101+02858 Europe/Istanbul
-TV -0831+17913 Pacific/Funafuti
TW +2503+12130 Asia/Taipei
-UA +5026+03031 Europe/Kiev Ukraine (most areas)
+UA +5026+03031 Europe/Kyiv Ukraine (most areas)
UA +4837+02218 Europe/Uzhgorod Transcarpathia
UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk
-UM +1917+16637 Pacific/Wake Wake Island
US +404251-0740023 America/New_York Eastern (most areas)
US +421953-0830245 America/Detroit Eastern - MI (most areas)
US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area)
@@ -369,6 +349,5 @@ UZ +4120+06918 Asia/Tashkent Uzbekistan (east)
VE +1030-06656 America/Caracas
VN +1045+10640 Asia/Ho_Chi_Minh Vietnam (south)
VU -1740+16825 Pacific/Efate
-WF -1318-17610 Pacific/Wallis
WS -1350-17144 Pacific/Apia
ZA,LS,SZ -2615+02800 Africa/Johannesburg