merge 2019a

Changes to code

    zic now has an -r option to limit the time range of output data.
    For example, 'zic -r @1000000000' limits the output data to
    timestamps starting 1000000000 seconds after the Epoch.
    This helps shrink output size and can be useful for applications
    not needing the full timestamp history, such as TZDIST truncation;
    see Internet RFC 8536 section 5.1.  (Inspired by a feature request
    from Christopher Wong, helped along by bug reports from Wong and
    from Tim Parenti.)

  Changes to documentation

    Mention Internet RFC 8536 (February 2019), which documents TZif.

    tz-link.html now cites tzdata-meta
    <https://tzdata-meta.timtimeonline.com/>.
This commit is contained in:
christos 2019-04-04 18:18:31 +00:00
parent e00a8e01f5
commit 91e14238d8
10 changed files with 500 additions and 261 deletions

View File

@ -12,7 +12,10 @@ VERSION= unknown
# Email address for bug reports.
BUGEMAIL= tz@iana.org
# Choose source data features. To get new features right away, use:
# DATAFORM selects the data format.
# Available formats represent essentially the same data, albeit
# possibly with minor discrepancies that users are not likely to notice.
# To get new features and the best data right away, use:
# DATAFORM= vanguard
# To wait a while before using new features, to give downstream users
# time to upgrade zic (the default), use:
@ -33,11 +36,11 @@ DATAFORM= main
LOCALTIME= GMT
# If you want something other than Eastern United States time as a template
# for handling POSIX-style timezone environment variables,
# for handling ruleless POSIX-style timezone environment variables,
# change the line below (after finding the timezone you want in the
# one of the $(TDATA) source files, or adding it to a source file).
# When a POSIX-style environment variable is handled, the rules in the
# template file are used to determine "spring forward" and "fall back" days and
# A ruleless environment setting like TZ='CST6CDT' uses the rules in the
# template file to determine "spring forward" and "fall back" days and
# times; the environment variable itself specifies UT offsets of standard and
# daylight saving time.
# Alternatively, if you discover you've got the wrong timezone, you can just
@ -46,7 +49,6 @@ LOCALTIME= GMT
# Use the command
# make zonenames
# to get a list of the values you can use for POSIXRULES.
# If you want POSIX compatibility, use "America/New_York".
POSIXRULES= America/New_York
@ -113,8 +115,8 @@ TIME_T_ALTERNATIVES = $(TIME_T_ALTERNATIVES_HEAD) $(TIME_T_ALTERNATIVES_TAIL)
TIME_T_ALTERNATIVES_HEAD = int64_t
TIME_T_ALTERNATIVES_TAIL = int32_t uint32_t uint64_t
# What kind of TZif data files to generate.
# (TZif is the binary time zone data format that zic generates.)
# What kind of TZif data files to generate. (TZif is the binary time
# zone data format that zic generates; see Internet RFC 8536.)
# If you want only POSIX time, with time values interpreted as
# seconds since the epoch (not counting leap seconds), use
# REDO= posix_only
@ -360,6 +362,9 @@ LEAPSECONDS=
zic= ./zic
ZIC= $(zic) $(ZFLAGS)
# To shrink the size of installed TZif files,
# append "-r @N" to omit data before N-seconds-after-the-Epoch.
# See the zic man page for more about -r.
ZFLAGS=
# How to use zic to install TZif files.
@ -491,7 +496,8 @@ MANTXTS= newctime.3.txt newstrftime.3.txt newtzset.3.txt \
COMMON= calendars CONTRIBUTING LICENSE Makefile \
NEWS README theory.html version
WEB_PAGES= tz-art.html tz-how-to.html tz-link.html
CHECK_WEB_PAGES=check_tz-art.html check_tz-how-to.html check_tz-link.html
CHECK_WEB_PAGES=check_theory.html check_tz-art.html \
check_tz-how-to.html check_tz-link.html
DOCS= $(MANS) date.1 $(MANTXTS) $(WEB_PAGES)
PRIMARY_YDATA= africa antarctica asia australasia \
europe northamerica southamerica
@ -804,9 +810,10 @@ check_tzs: $(TZS) $(TZS_NEW)
touch $@
check_web: $(CHECK_WEB_PAGES)
check_theory.html: theory.html
check_tz-art.html: tz-art.html
check_tz-link.html: tz-link.html
check_tz-art.html check_tz-link.html:
check_theory.html check_tz-art.html check_tz-link.html:
$(CURL) -sS --url https://validator.w3.org/nu/ -F out=gnu \
-F file=@$$(expr $@ : 'check_\(.*\)') -o $@.out && \
test ! -s $@.out || { cat $@.out; exit 1; }
@ -840,11 +847,13 @@ check_zishrink_posix check_zishrink_right: \
touch $@
clean_misc:
rm -fr check_*.dir
rm -f *.o *.out $(TIME_T_ALTERNATIVES) \
check_* core typecheck_* \
date tzselect version.h zdump zic yearistype libtz.a
clean: clean_misc
rm -fr *.dir *.zi tzdb-*/ $(TZS_NEW)
rm -fr *.dir tzdb-*/
rm -f *.zi $(TZS_NEW)
maintainer-clean: clean
@echo 'This command is intended for maintainers to use; it'

View File

@ -1,5 +1,53 @@
News for the tz database
Release 20198 - 2019-03-25 22:01:33 -0700
Briefly:
Palestine "springs forward" on 2019-03-30 instead of 2019-03-23.
Metlakatla "fell back" to rejoin Alaska Time on 2019-01-20 at 02:00.
Changes to past and future timestamps
Palestine will not start DST until 2019-03-30, instead of 2019-03-23 as
previously predicted. Adjust our prediction by guessing that spring
transitions will be between 24 and 30 March, which matches recent practice
since 2016. (Thanks to Even Scharning and Tim Parenti.)
Metlakatla ended its observance of Pacific standard time,
rejoining Alaska Time, on 2019-01-20 at 02:00. (Thanks to Ryan
Stanley and Tim Parenti.)
Changes to past timestamps
Israel observed DST in 1980 (08-02/09-13) and 1984 (05-05/08-25).
(Thanks to Alois Treindl and Isaac Starkman.)
Changes to time zone abbreviations
Etc/UCT is now a backward-compatibility link to Etc/UTC, instead
of being a separate zone that generates the abbreviation "UCT",
which nowadays is typically a typo. (Problem reported by Isiah
Meadows.)
Changes to code
zic now has an -r option to limit the time range of output data.
For example, 'zic -r @1000000000' limits the output data to
timestamps starting 1000000000 seconds after the Epoch.
This helps shrink output size and can be useful for applications
not needing the full timestamp history, such as TZDIST truncation;
see Internet RFC 8536 section 5.1. (Inspired by a feature request
from Christopher Wong, helped along by bug reports from Wong and
from Tim Parenti.)
Changes to documentation
Mention Internet RFC 8536 (February 2019), which documents TZif.
tz-link.html now cites tzdata-meta
<https://tzdata-meta.timtimeonline.com/>.
Release 2018i - 2018-12-30 11:05:43 -0800
Briefly:
@ -400,8 +448,9 @@ Release 2018d - 2018-03-22 07:05:46 -0700
downstream parsers do not support it.
* The build procedure constructs three files vanguard.zi, main.zi,
and rearguard.zi, one for each format. The files represent the
same data as closely as the formats allow. These three files
and rearguard.zi, one for each format. Although the files
represent essentially the same data, they may have minor
discrepancies that users are not likely to notice. The files
are intended for downstream data consumers and are not
installed. Zoneinfo parsers that do not support negative SAVE values
should start using rearguard.zi, so that they will be unaffected

View File

@ -1,7 +1,7 @@
README for the tz distribution
"What time is it?" -- Richard Deacon as The King
"Any time you want it to be." -- Frank Baxter as The Scientist
"Where do I set the hands of the clock?" -- Les Tremayne as The King
"Oh that--you can set them any place you want." -- Frank Baxter as The Scientist
(from the Bell System film "About Time")
The Time Zone Database (called tz, tzdb or zoneinfo) contains code and

View File

@ -15,7 +15,7 @@
<ul>
<li><a href="#scope">Scope of the <code><abbr>tz</abbr></code>
database</a></li>
<li><a href="#naming">Names of timezones</a></li>
<li><a href="#naming">Timezone identifiers</a></li>
<li><a href="#abbreviations">Time zone abbreviations</a></li>
<li><a href="#accuracy">Accuracy of the <code><abbr>tz</abbr></code>
database</a></li>
@ -107,9 +107,9 @@ It does not always make sense to talk about a timezone's
</section>
<section>
<h2 id="naming">Names of timezones</h2>
<h2 id="naming">Timezone identifiers</h2>
<p>
Each timezone has a unique name.
Each timezone has a name that uniquely identifies the timezone.
Inexperienced users are not expected to select these names unaided.
Distributors should provide documentation and/or a simple selection
interface that explains each name via a map or via descriptive text like
@ -142,10 +142,12 @@ among the following goals:
</li>
<li>
Be robust in the presence of political changes.
For example, names of countries are ordinarily not used, to avoid
For example, names are typically not tied to countries, to avoid
incompatibilities when countries change their name (e.g.,
Zaire&rarr;Congo) or when locations change countries (e.g., Hong
Swaziland&rarr;Eswatini) or when locations change countries (e.g., Hong
Kong from UK colony to China).
There is no requirement that every country or national
capital must have a timezone name.
</li>
<li>
Be portable to a wide variety of implementations.
@ -214,19 +216,18 @@ in decreasing order of importance:
Uninhabited regions like the North Pole and Bouvet Island
do not need locations, since local time is not defined there.
</li>
<li>
There should typically be at least one name for each <a
href="https://en.wikipedia.org/wiki/ISO_3166-1"><abbr
title="International Organization for Standardization">ISO</abbr>
3166-1</a> officially assigned two-letter code for an inhabited
country or territory.
</li>
<li>
If all the clocks in a timezone have agreed since 1970,
do not bother to include more than one timezone
even if some of the clocks disagreed before 1970.
Otherwise these tables would become annoyingly large.
</li>
<li>
If boundaries between regions are fluid, such as during a war or
insurrection, do not bother to create a new timezone merely
because of yet another boundary change. This helps prevent table
bloat and simplifies maintenance.
</li>
<li>
If a name is ambiguous, use a less ambiguous alternative;
e.g., many cities are named San José and Georgetown, so
@ -299,29 +300,23 @@ in decreasing order of importance:
</ul>
<p>
The file '<code>zone1970.tab</code>' lists geographical locations used
to name timezones.
It is intended to be an exhaustive list of names for geographic
regions as described above; this is a subset of the timezones in the data.
Although a '<code>zone1970.tab</code>' location's
<a href="https://en.wikipedia.org/wiki/Longitude">longitude</a>
corresponds to
its <a href="https://en.wikipedia.org/wiki/Local_mean_time">local mean
time (<abbr>LMT</abbr>)</a> offset with one hour for every 15&deg;
east longitude, this relationship is not exact.
Guidelines have evolved with time, and names following old versions of
this guideline might not follow the current version. When guidelines
have changed, old names continue to be supported. Guideline changes
have included the following:
</p>
<p>
Older versions of this package used a different naming scheme,
and these older names are still supported.
<ul>
<li>
Older versions of this package used a different naming scheme.
See the file '<code>backward</code>' for most of these older names
(e.g., '<code>US/Eastern</code>' instead of '<code>America/New_York</code>').
The other old-fashioned names still supported are
'<code>WET</code>', '<code>CET</code>', '<code>MET</code>', and
'<code>EET</code>' (see the file '<code>europe</code>').
</p>
</li>
<p>
<li>
Older versions of this package defined legacy names that are
incompatible with the first guideline of location names, but which are
still supported.
@ -332,6 +327,31 @@ Also, the file '<code>backward</code>' defines the legacy names
and the file '<code>northamerica</code>' defines the legacy names
'<code>EST5EDT</code>', '<code>CST6CDT</code>',
'<code>MST7MDT</code>', and '<code>PST8PDT</code>'.
</li>
<li>
Older versions of this guideline said that
there should typically be at least one name for each <a
href="https://en.wikipedia.org/wiki/ISO_3166-1"><abbr
title="International Organization for Standardization">ISO</abbr>
3166-1</a> officially assigned two-letter code for an inhabited
country or territory.
This old guideline has been dropped, as it was not needed to handle
timestamps correctly and it increased maintenance burden.
</li>
</ul>
<p>
The file '<code>zone1970.tab</code>' lists geographical locations used
to name timezones.
It is intended to be an exhaustive list of names for geographic
regions as described above; this is a subset of the timezones in the data.
Although a '<code>zone1970.tab</code>' location's
<a href="https://en.wikipedia.org/wiki/Longitude">longitude</a>
corresponds to
its <a href="https://en.wikipedia.org/wiki/Local_mean_time">local mean
time (<abbr>LMT</abbr>)</a> offset with one hour for every 15&deg;
east longitude, this relationship is not exact.
</p>
<p>
@ -983,7 +1003,9 @@ an older <code>zic</code>.
constrained to be a string containing abbreviations
and numeric data as described <a href="#POSIX">above</a>.
The file's format is <dfn><abbr>TZif</abbr></dfn>,
a timezone information format that contains binary data.
a timezone information format that contains binary data; see
<a href="https://tools.ietf.org/html/8536">Internet
<abbr>RFC</abbr> 8536</a>.
The daylight saving time rules to be used for a
particular timezone are encoded in the
<abbr>TZif</abbr> file; the format of the file allows <abbr>US</abbr>,
@ -1166,7 +1188,7 @@ The <code><abbr>tz</abbr></code> code and data supply the following interfaces:
<ul>
<li>
A set of timezone names as per
"<a href="#naming">Names of timezones</a>" above.
"<a href="#naming">Timezone identifiers</a>" above.
</li>
<li>
Library functions described in "<a href="#functions">Time and date
@ -1213,6 +1235,17 @@ For example, users should not rely on particular <abbr>UT</abbr>
offsets or abbreviations for timestamps, as data entries are often
based on guesswork and these guesses may be corrected or improved.
</p>
<p>
Timezone boundaries are not part of the stable interface.
For example, even though the <samp>Asia/Bangkok</samp> timezone
currently includes Chang Mai, Hanoi, and Phnom Penh, this is not part
of the stable interface and the timezone can split at any time.
If a calendar application records a future event in some location other
than Bangkok by putting "<samp>Asia/Bangkok</samp>" in the event's record,
the application should be robust in the presence of timezone splits
between now and the future time.
</p>
</section>
<section>

View File

@ -18,11 +18,11 @@ Arizona's daylight-saving enclaves quite well.</li>
with Time &amp; Timezones &ndash; Computerphile</a>" (2013; 10:12) delves
into problems that programmers have with timekeeping.</li>
<li>
<a href="https://www.rferl.org/a/28375932.html">All The Time In The World:
"<a href="https://www.rferl.org/a/28375932.html">All The Time In The World:
Explaining The Mysteries Of Time Zones</a>" (2017; 2:15)
briefly says why France has more time zones than Russia.
<li>
"About Time" (1962; 53 minutes) is part of the
"About Time" (1962; 59 minutes) is part of the
Bell Science extravaganza, with Frank Baxter, Richard Deacon, and Les Tremayne.
Its advisor was Richard Feynman, and it was voiced by Mel Blanc.
(<a href="http://www.imdb.com/title/tt0154110/">IMDb entry</a>.)</li>

View File

@ -144,7 +144,9 @@ After obtaining the code and data files, see the
<code>README</code> file for what to do next.
The code lets you compile the <code><abbr>tz</abbr></code> source files into
machine-readable binary files, one for each location. The binary files
are in a special timezone information format (<dfn><abbr>TZif</abbr></dfn>).
are in a special timezone information format (<dfn><abbr>TZif</abbr></dfn>)
specified by <a href="https://tools.ietf.org/html/8536">Internet
<abbr>RFC</abbr> 8536</a>.
The code also lets
you read a <abbr>TZif</abbr> file and interpret timestamps for that
location.</p>
@ -157,7 +159,11 @@ the time zone mailing list. You can also <a
href="https://mm.icann.org/mailman/listinfo/tz">subscribe</a> to it
and browse the <a
href="https://mm.icann.org/pipermail/tz/">archive of old
messages</a>.</p>
messages</a>.
<a href="https://tzdata-meta.timtimeonline.com/">Metadata for mailing list
discussions</a> and corresponding data changes can be
generated <a href="https://github.com/timparenti/tzdata-meta">automatically</a>.
</p>
<p>
If your government plans to change its time zone boundaries or
daylight saving rules, inform <code>tz@iana.org</code> well in
@ -278,15 +284,12 @@ along with <a href="https://tools.ietf.org/html/rfc7809">CalDAV</a>
(Internet <abbr>RFC</abbr> 7809), a calendar access protocol for
transferring time zone data by reference.
The <a href="https://www.ietf.org/mailman/listinfo/tzdist-bis">tzdist-bis
mailing list</a> discusses two Internet drafts: <a
mailing list</a> discusses the Internet draft <a
id="TZDIST-Geolocate"
href="https://tools.ietf.org/html/draft-murchison-tzdist-geolocate">TZDIST
Geolocate Extension</a> lets a client determine its timezone
Geolocate Extension</a>, which lets a client determine its timezone
from its geographic location using a <a
href="https://tools.ietf.org/html/rfc5870">'geo' URI</a>, and
<a href="https://tools.ietf.org/html/draft-murchison-tzdist-tzif">The
Time Zone Information Format (<abbr>TZif</abbr>)</a> specifies the format of
<abbr>TZif</abbr> data.</li>
href="https://tools.ietf.org/html/rfc5870">'geo' URI</a>.</li>
<li>The <a href="https://tools.ietf.org/html/rfc5545">
Internet Calendaring and Scheduling Core Object Specification
(iCalendar)</a> (Internet <abbr>RFC</abbr> 5445)

View File

@ -1,8 +1,8 @@
.\" $NetBSD: tzfile.5,v 1.27 2018/10/27 22:29:24 christos Exp $
.\" $NetBSD: tzfile.5,v 1.28 2019/04/04 18:18:31 christos Exp $
.\"
.\" This file is in the public domain, so clarified as of
.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
.Dd October 27, 2018
.Dd April 4, 2019
.Dt TZFILE 5
.Os
.Sh NAME
@ -13,7 +13,10 @@ The timezone information files used by
.Xr tzset 3
are typically found under a directory with a name like
.Pa /usr/share/zoneinfo .
These files beging with a 44-byte header containing the following fields:
These files use the format described in Internet
.Rs
.%R RFC 8536
.Re
.Bl -bullet
.It
The magic four-byte ASCII sequence begin with the magic characters
@ -375,8 +378,16 @@ Future changes to the format may append more data.
.Xr localtime 3 ,
.Xr time 3 ,
.Xr tzset 3 ,
.Xr zdump 8
.Xr zic 8
.Xr zdump 8 ,
.Xr zic 8 .
.Rs
.%A Olson A, Eggert P, Murchison K.
.%T The Time Zone Information Format (TZif).
.%D Feb 2019.
.%U https://www.rfc-editor.org/info/rfc8536
.%U https://doi.org/10.17487/RFC8536
.%R RFC 8536
.Re
.\" @(#)tzfile.5 8.3
.\" This file is in the public domain, so clarified as of
.\" 1996-06-05 by Arthur David Olson.

View File

@ -1 +1 @@
2018i
2019a

View File

@ -1,5 +1,5 @@
.\" $NetBSD: zic.8,v 1.31 2018/10/27 22:29:24 christos Exp $
.Dd October 19, 2018
.\" $NetBSD: zic.8,v 1.32 2019/04/04 18:18:31 christos Exp $
.Dd April 4, 2019
.Dt ZIC 8
.Os
.Sh NAME
@ -52,14 +52,41 @@ will act as if the input contained a link line of the form
Use
.Ar timezone's
rules when handling POSIX-format
timezone environment variables.
TZ strings like
.Dq CST6CDT
that lack transition rules.
.Nm
will act as if the input contained a link line of the form
.Dl Link timezone posixrules
.It Fl s
Limit time values stored in output files to values that are the same
whether they're taken to be signed or unsigned.
You can use this option to generate SVVS-compatible files.
.It Fl r Op Ar @lo / Op Ar @hi
Reduce the size of output files by limiting their applicability
to timestamps in the range from
.Ar lo
(inclusive) to
.Ar hi
(exclusive), where
.Ar lo
and
.Ar hi
are possibly-signed decimal counts of seconds since the Epoch
(1970-01-01 00:00:00 UTC).
Omitted counts default to extreme values.
For example,
.Bd literal
zic -r @0
.Ed
omits data intended for negative timestamps (i.e., before the Epoch), and
.Bd literal
zic -r @0/@2147483648
.Ed
outputs data intended only for nonnegative timestamps that fit into
31-bit signed integers.
Or using
.Xr date 1 ,
.Bd literal
zic -r @$(date +%s)
.Ed
omits data intended for past timestamps.
.It Fl t Ar file
When creating local time information, put the configuration link in
the named file rather than in the standard location.
@ -87,7 +114,7 @@ prohibit this.
The output file does not contain all the information about the
long-term future of a timezone, because the future cannot be summarized as
an extended POSIX TZ string.
For example, as of 2013 this problem
For example, as of 2019 this problem
occurs for Iran's daylight-saving rules for the predicted future, as
these rules are based on the Iranian calendar, which cannot be
represented.
@ -439,6 +466,16 @@ continuation.
.Pp
If a zone changes at the same instant that a rule would otherwise take
effect in the earlier zone or continuation line, the rule is ignored.
A zone or continuation line
.I L
with a named rule set starts with standard time by default:
that is, any of
.IR L 's
timestamps preceding
.IR L 's
earliest rule use the rule in effect after
.IR L 's
first transition into standard time.
In a single zone it is an error if two rules take effect at the same
instant, or if two zone changes take effect at the same instant.
.Pp
@ -568,6 +605,9 @@ Swiss rules and later EU rules were applied, the time zone abbreviation
has been CET for standard time and CEST for daylight saving
time.
.Sh FILES
Input files use the format described in this section; output files use
.Xr tzfile 5
format.
.Bl -tag -width /usr/share/zoneinfo -compact
.It Pa /etc/localtime
Default local timezone file

View File

@ -1,4 +1,4 @@
/* $NetBSD: zic.c,v 1.73 2019/01/01 03:04:56 christos Exp $ */
/* $NetBSD: zic.c,v 1.74 2019/04/04 18:18:31 christos Exp $ */
/*
** This file is in the public domain, so clarified as of
** 2006-07-17 by Arthur David Olson.
@ -10,7 +10,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: zic.c,v 1.73 2019/01/01 03:04:56 christos Exp $");
__RCSID("$NetBSD: zic.c,v 1.74 2019/04/04 18:18:31 christos Exp $");
#endif /* !defined lint */
#include "private.h"
@ -170,8 +170,7 @@ static void adjleap(void);
static void associate(void);
static void dolink(const char *, const char *, bool);
static char ** getfields(char * buf);
static zic_t gethms(const char * string, const char * errstring,
bool);
static zic_t gethms(const char * string, const char * errstring);
static zic_t getstdoff(char *, bool *);
static void infile(const char * filename);
static void inleap(char ** fields, int nfields);
@ -179,9 +178,9 @@ static void inlink(char ** fields, int nfields);
static void inrule(char ** fields, int nfields);
static bool inzcont(char ** fields, int nfields);
static bool inzone(char ** fields, int nfields);
static bool inzsub(char **, int, int);
static bool itsdir(const char *);
static bool itssymlink(const char *);
static bool inzsub(char **, int, bool);
static bool itsdir(char const *);
static bool itssymlink(char const *);
static bool is_alpha(char a);
static char lowerit(char);
static void mkdirs(char const *, bool);
@ -200,12 +199,12 @@ static bool yearistype(zic_t year, const char * type);
enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
/* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles
tz binary files whose POSIX-TZ-style strings contain '<'; see
TZif files whose POSIX-TZ-style strings contain '<'; see
QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>. This
workaround will no longer be needed when Qt 5.6.1 and earlier are
obsolete, say in the year 2021. */
#ifndef WORK_AROUND_QTBUG_53071
enum { WORK_AROUND_QTBUG_53071 = 1 };
enum { WORK_AROUND_QTBUG_53071 = true };
#endif
static int charcnt;
@ -586,7 +585,8 @@ usage(FILE *stream, int status)
fprintf(stream,
_("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
"\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
"\t[ -t localtime-link ] [ -L leapseconds ] [ filename ... ]\n\n"
"\t[ -t localtime-link ] [ -L leapseconds ] [ -r '[@lo][/@hi]' ] \\\n"
"\t[ filename ... ]\n\n"
"Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO);
if (status == EXIT_SUCCESS)
@ -614,6 +614,45 @@ change_directory (char const *dir)
}
}
#define 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);
static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
/* The minimum, and one less than the maximum, values specified by
the -r option. These default to MIN_TIME and MAX_TIME. */
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);
/* Set the time range of the output to TIMERANGE.
Return true if successful. */
static bool
timerange_option(char *timerange)
{
intmax_t lo = min_time, hi = max_time;
char *lo_end = timerange, *hi_end;
if (*timerange == '@') {
errno = 0;
lo = strtoimax (timerange + 1, &lo_end, 10);
if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
return false;
}
hi_end = lo_end;
if (lo_end[0] == '/' && lo_end[1] == '@') {
errno = 0;
hi = strtoimax (lo_end + 2, &hi_end, 10);
if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
return false;
hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
}
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;
return true;
}
static const char * psxrules;
static const char * lcltime;
static const char * directory;
@ -626,6 +665,7 @@ main(int argc, char **argv)
{
int c, k;
ptrdiff_t i, j;
bool timerange_given = false;
#ifdef S_IWGRP
umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
@ -651,7 +691,7 @@ main(int argc, char **argv)
} else if (strcmp(argv[k], "--help") == 0) {
usage(stdout, EXIT_SUCCESS);
}
while ((c = getopt(argc, argv, "d:l:L:p:st:vy:")) != EOF && c != -1)
while ((c = getopt(argc, argv, "d:l:L:p:r:st:vy:")) != EOF && c != -1)
switch (c) {
default:
usage(stderr, EXIT_FAILURE);
@ -719,6 +759,21 @@ _("%s: More than one -L option specified\n"),
case 'v':
noise = true;
break;
case 'r':
if (timerange_given) {
fprintf(stderr,
_("%s: More than one -r option specified\n"),
progname);
return EXIT_FAILURE;
}
if (! timerange_option(optarg)) {
fprintf(stderr,
_("%s: invalid time range: %s\n"),
progname, optarg);
return EXIT_FAILURE;
}
timerange_given = true;
break;
case 's':
warning(_("-s ignored"));
break;
@ -972,52 +1027,9 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
}
}
#define TIME_T_BITS_IN_FILE 64
static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
/* Estimated time of the Big Bang, in seconds since the POSIX epoch.
rounded downward to the negation of a power of two that is
comfortably outside the error bounds.
For the time of the Big Bang, see:
Ade PAR, Aghanim N, Armitage-Caplan C et al. Planck 2013 results.
I. Overview of products and scientific results.
arXiv:1303.5062 2013-03-20 20:10:01 UTC
<https://arxiv.org/pdf/1303.5062v1> [PDF]
Page 36, Table 9, row Age/Gyr, column Planck+WP+highL+BAO 68% limits
gives the value 13.798 plus-or-minus 0.037 billion years.
Multiplying this by 1000000000 and then by 31557600 (the number of
seconds in an astronomical year) gives a value that is comfortably
less than 2**59, so BIG_BANG is - 2**59.
BIG_BANG is approximate, and may change in future versions.
Please do not rely on its exact value. */
#ifndef BIG_BANG
#define BIG_BANG (- (1LL << 59))
#endif
/* If true, work around GNOME bug 730332
<https://bugzilla.gnome.org/show_bug.cgi?id=730332>
by refusing to output time stamps before BIG_BANG.
Such time stamps are physically suspect anyway.
The GNOME bug is scheduled to be fixed in GNOME 3.22, and if so
this workaround will no longer be needed when GNOME 3.21 and
earlier are obsolete, say in the year 2021. */
enum { WORK_AROUND_GNOME_BUG_730332 = true };
static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
? BIG_BANG
: MINVAL(zic_t, TIME_T_BITS_IN_FILE));
/* Return true if NAME is a directory. */
static bool
itsdir(const char *name)
itsdir(char const *name)
{
struct stat st;
int res = stat(name, &st);
@ -1226,7 +1238,7 @@ _("%s: panic: Invalid l_value %d\n"),
*/
static zic_t
gethms(char const *string, char const *errstring, bool signable)
gethms(char const *string, char const *errstring)
{
zic_t hh;
int sign, mm = 0, ss = 0;
@ -1236,9 +1248,7 @@ gethms(char const *string, char const *errstring, bool signable)
if (string == NULL || *string == '\0')
return 0;
if (!signable)
sign = 1;
else if (*string == '-') {
if (*string == '-') {
sign = -1;
++string;
} else sign = 1;
@ -1294,7 +1304,7 @@ getstdoff(char *field, bool *isdst)
case 's': dst = 0; *ep = '\0'; break;
}
}
stdoff = gethms(field, _("invalid saved time"), true);
stdoff = gethms(field, _("invalid saved time"));
*isdst = dst < 0 ? stdoff != 0 : dst;
return stdoff;
}
@ -1308,8 +1318,13 @@ inrule(char **fields, int nfields)
error(_("wrong number of fields on Rule line"));
return;
}
if (*fields[RF_NAME] == '\0') {
error(_("nameless rule"));
switch (*fields[RF_NAME]) {
case '\0':
case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
case '+': case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
return;
}
r.r_filename = filename;
@ -1370,7 +1385,7 @@ inzcont(char **fields, int nfields)
}
static bool
inzsub(char **fields, int nfields, const int iscont)
inzsub(char **fields, int nfields, bool iscont)
{
char * cp;
char * cp1;
@ -1403,7 +1418,7 @@ inzsub(char **fields, int nfields, const int iscont)
}
z.z_filename = filename;
z.z_linenum = linenum;
z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true);
z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"));
if ((cp = strchr(fields[i_format], '%')) != 0) {
if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/')) {
@ -1525,7 +1540,7 @@ inleap(char **fields, int nfields)
return;
}
t = dayoff * SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day"), false);
tod = gethms(fields[LP_TIME], _("invalid time of day"));
cp = fields[LP_CORR];
{
bool positive;
@ -1620,7 +1635,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
break;
}
}
rp->r_tod = gethms(dp, _("invalid time of day"), false);
rp->r_tod = gethms(dp, _("invalid time of day"));
free(dp);
/*
** Year work.
@ -1730,7 +1745,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
}
static void
convert(const zic_t val, char *const buf)
convert(const int_fast32_t val, char *const buf)
{
int i;
int shift;
@ -1752,7 +1767,7 @@ convert64(const zic_t val, char *const buf)
}
static void
puttzcode(const zic_t val, FILE *const fp)
puttzcode(const int_fast32_t val, FILE *const fp)
{
char buf[4];
@ -1761,12 +1776,16 @@ puttzcode(const zic_t val, FILE *const fp)
}
static void
puttzcode64(const zic_t val, FILE *const fp)
puttzcodepass(zic_t val, FILE *fp, int pass)
{
if (pass == 1)
puttzcode(val, fp);
else {
char buf[8];
convert64(val, buf);
fwrite(buf, sizeof buf, (size_t) 1, fp);
}
}
static int
@ -1779,12 +1798,52 @@ atcomp(const void *avp, const void *bvp)
}
static void
writezone(const char *const name, const char *const string, char version)
swaptypes(int i, int j)
{
{ zic_t t = gmtoffs[i]; gmtoffs[i] = gmtoffs[j]; gmtoffs[j] = t; }
{ char t = isdsts[i]; isdsts[i] = isdsts[j]; isdsts[j] = t; }
{ unsigned char t = abbrinds[i]; abbrinds[i] = abbrinds[j];
abbrinds[j] = t; }
{ bool t = ttisstds[i]; ttisstds[i] = ttisstds[j]; ttisstds[j] = t; }
{ bool t = ttisgmts[i]; ttisgmts[i] = ttisgmts[j]; ttisgmts[j] = t; }
}
struct timerange {
int defaulttype;
ptrdiff_t base, count;
int leapbase, leapcount;
};
static struct timerange
limitrange(struct timerange r, zic_t lo, zic_t hi,
zic_t const *ats, unsigned char const *types)
{
while (0 < r.count && ats[r.base] < lo) {
r.defaulttype = types[r.base];
r.count--;
r.base++;
}
while (0 < r.leapcount && trans[r.leapbase] < lo) {
r.leapcount--;
r.leapbase++;
}
if (hi < ZIC_MAX) {
while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
r.count--;
while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
r.leapcount--;
}
return r;
}
static void
writezone(const char *const name, const char *const string, char version,
int defaulttype)
{
FILE * fp;
ptrdiff_t i, j;
int leapcnt32, leapi32;
ptrdiff_t timecnt32, timei32;
int pass;
static const struct tzhead tzh0;
static struct tzhead tzh;
@ -1799,6 +1858,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;
/*
** Sort.
@ -1813,13 +1873,11 @@ writezone(const char *const name, const char *const string, char version)
toi = 0;
fromi = 0;
while (fromi < timecnt && attypes[fromi].at < early_time)
++fromi;
for ( ; fromi < timecnt; ++fromi) {
if (toi > 1 && ((attypes[fromi].at +
if (toi != 0 && ((attypes[fromi].at +
gmtoffs[attypes[toi - 1].type]) <=
(attypes[toi - 1].at +
gmtoffs[attypes[toi - 2].type]))) {
(attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
: attypes[toi - 2].type]))) {
attypes[toi - 1].type =
attypes[fromi].type;
continue;
@ -1876,32 +1934,13 @@ writezone(const char *const name, const char *const string, char version)
timecnt++;
}
/*
** Figure out 32-bit-limited starts and counts.
*/
timecnt32 = timecnt;
timei32 = 0;
leapcnt32 = leapcnt;
leapi32 = 0;
while (0 < timecnt32 && INT32_MAX < ats[timecnt32 - 1])
--timecnt32;
while (1 < timecnt32 && ats[timei32] < INT32_MIN
&& ats[timei32 + 1] <= INT32_MIN) {
/* Discard too-low transitions, except keep any last too-low
transition if no transition is exactly at INT32_MIN.
The kept transition will be output as an INT32_MIN
"transition" appropriate for buggy 32-bit clients that do
not use time type 0 for timestamps before the first
transition; see below. */
--timecnt32;
++timei32;
}
while (0 < leapcnt32 && INT32_MAX < trans[leapcnt32 - 1])
--leapcnt32;
while (0 < leapcnt32 && trans[leapi32] < INT32_MIN) {
--leapcnt32;
++leapi32;
}
rangeall.defaulttype = defaulttype;
rangeall.base = rangeall.leapbase = 0;
rangeall.count = timecnt;
rangeall.leapcount = leapcnt;
range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
/*
** Remove old file, if any, to snap links.
*/
@ -1931,7 +1970,11 @@ writezone(const char *const name, const char *const string, char version)
for (pass = 1; pass <= 2; ++pass) {
ptrdiff_t thistimei, thistimecnt, thistimelim;
int thisleapi, thisleapcnt, thisleaplim;
int writetype[TZ_MAX_TYPES];
int currenttype, thisdefaulttype;
bool locut, hicut;
zic_t lo;
int old0;
char omittype[TZ_MAX_TYPES];
int typemap[TZ_MAX_TYPES];
int thistypecnt;
char thischars[TZ_MAX_CHARS];
@ -1940,41 +1983,73 @@ writezone(const char *const name, const char *const string, char version)
int indmap[TZ_MAX_CHARS];
if (pass == 1) {
thistimei = timei32;
thistimecnt = timecnt32;
/* 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);
thistimei = range32.base;
thistimecnt = range32.count;
toomanytimes = thistimecnt >> 31 >> 1 != 0;
thisleapi = leapi32;
thisleapcnt = leapcnt32;
thisleapi = range32.leapbase;
thisleapcnt = range32.leapcount;
locut = INT32_MIN < lo_time;
hicut = hi_time < INT32_MAX;
} else {
thistimei = 0;
thistimecnt = timecnt;
thisdefaulttype = range64.defaulttype;
thistimei = range64.base;
thistimecnt = range64.count;
toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
thisleapi = 0;
thisleapcnt = leapcnt;
thisleapi = range64.leapbase;
thisleapcnt = range64.leapcount;
locut = min_time < lo_time;
hicut = hi_time < max_time;
}
if (toomanytimes)
error(_("too many transition times"));
/* 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. */
if (0 < thistimei && ats[thistimei] != lo_time) {
thistimei--;
thistimecnt++;
locut = false;
}
thistimelim = thistimei + thistimecnt;
thisleaplim = thisleapi + thisleapcnt;
for (i = 0; i < typecnt; ++i)
writetype[i] = thistimecnt == timecnt;
if (thistimecnt == 0) {
/*
** No transition times fall in the current
** (32- or 64-bit) window.
*/
if (typecnt != 0)
writetype[typecnt - 1] = true;
} else {
for (i = thistimei - 1; i < thistimelim; ++i)
if (i >= 0)
writetype[types[i]] = true;
/*
** For America/Godthab and Antarctica/Palmer
*/
if (thistimei == 0)
writetype[0] = true;
if (thistimecnt != 0) {
if (ats[thistimei] == lo_time)
locut = false;
if (hi_time < ZIC_MAX && ats[thistimelim - 1] == hi_time + 1)
hicut = false;
}
memset(omittype, true, typecnt);
omittype[thisdefaulttype] = false;
for (i = thistimei; i < thistimelim; i++)
omittype[types[i]] = false;
/* Reorder types to make THISDEFAULTTYPE type 0.
Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that
THISDEFAULTTYPE appears as type 0 in the output instead
of OLD0. TYPEMAP also omits unused types. */
old0 = strlen(omittype);
swaptypes(old0, thisdefaulttype);
#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
/*
** For some pre-2011 systems: if the last-to-be-written
@ -1988,15 +2063,12 @@ writezone(const char *const name, const char *const string, char version)
int mrudst, mrustd, hidst, histd, type;
hidst = histd = mrudst = mrustd = -1;
for (i = thistimei; i < thistimelim; ++i) {
if (i < 0)
continue;
for (i = thistimei; i < thistimelim; ++i)
if (isdsts[types[i]])
mrudst = types[i];
else mrustd = types[i];
}
for (i = 0; i < typecnt; ++i)
if (writetype[i]) {
for (i = old0; i < typecnt; i++)
if (!omittype[i]) {
if (isdsts[i])
hidst = i;
else histd = i;
@ -2010,7 +2082,7 @@ writezone(const char *const name, const char *const string, char version)
ttisstds[mrudst],
ttisgmts[mrudst]);
isdsts[mrudst] = 1;
writetype[type] = true;
omittype[type] = false;
}
if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
gmtoffs[histd] != gmtoffs[mrustd]) {
@ -2021,20 +2093,24 @@ writezone(const char *const name, const char *const string, char version)
ttisstds[mrustd],
ttisgmts[mrustd]);
isdsts[mrustd] = 0;
writetype[type] = true;
omittype[type] = true;
}
}
#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
thistypecnt = 0;
for (i = 0; i < typecnt; ++i)
typemap[i] = writetype[i] ? thistypecnt++ : -1;
thistypecnt = 0;
for (i = old0; i < typecnt; i++)
if (!omittype[i])
typemap[i == old0 ? thisdefaulttype
: i == thisdefaulttype ? old0 : i]
= thistypecnt++;
for (i = 0; i < (int)(sizeof indmap / sizeof indmap[0]); ++i)
indmap[i] = -1;
thischarcnt = 0;
for (i = 0; i < typecnt; ++i) {
for (i = old0; i < typecnt; i++) {
char * thisabbr;
if (!writetype[i])
if (omittype[i])
continue;
if (indmap[abbrinds[i]] >= 0)
continue;
@ -2055,7 +2131,7 @@ writezone(const char *const name, const char *const string, char version)
convert(thistypecnt, tzh.tzh_ttisgmtcnt);
convert(thistypecnt, tzh.tzh_ttisstdcnt);
convert(thisleapcnt, tzh.tzh_leapcnt);
convert(thistimecnt, tzh.tzh_timecnt);
convert(locut + thistimecnt + hicut, tzh.tzh_timecnt);
convert(thistypecnt, tzh.tzh_typecnt);
convert(thischarcnt, tzh.tzh_charcnt);
DO(tzh_magic);
@ -2068,23 +2144,31 @@ writezone(const char *const name, const char *const string, char version)
DO(tzh_typecnt);
DO(tzh_charcnt);
#undef DO
for (i = thistimei; i < thistimelim; ++i)
if (pass == 1)
/*
** Output an INT32_MIN "transition"
** if appropriate; see above.
*/
puttzcode(((ats[i] < INT32_MIN) ?
INT32_MIN : ats[i]), fp);
else puttzcode64(ats[i], fp);
for (i = thistimei; i < thistimelim; ++i) {
unsigned char uc;
/* Output a LO_TIME transition if needed; see limitrange.
But do not go below the minimum representable value
for this pass. */
lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time;
uc = typemap[types[i]];
fwrite(&uc, sizeof uc, (size_t) 1, fp);
if (locut)
puttzcodepass(lo, fp, pass);
for (i = thistimei; i < thistimelim; ++i) {
zic_t at = ats[i] < lo ? lo : ats[i];
puttzcodepass(at, fp, pass);
}
for (i = 0; i < typecnt; ++i)
if (writetype[i]) {
if (hicut)
puttzcodepass(hi_time + 1, fp, pass);
currenttype = 0;
if (locut)
putc(currenttype, fp);
for (i = thistimei; i < thistimelim; ++i) {
currenttype = typemap[types[i]];
putc(currenttype, fp);
}
if (hicut)
putc(currenttype, fp);
for (i = old0; i < typecnt; i++)
if (!omittype[i]) {
puttzcode(gmtoffs[i], fp);
putc(isdsts[i], fp);
putc((unsigned char) indmap[abbrinds[i]], fp);
@ -2112,17 +2196,16 @@ writezone(const char *const name, const char *const string, char version)
}
todo = tadd(trans[i], -gmtoffs[j]);
} else todo = trans[i];
if (pass == 1)
puttzcode(todo, fp);
else puttzcode64(todo, fp);
puttzcodepass(todo, fp, pass);
puttzcode(corr[i], fp);
}
for (i = 0; i < typecnt; ++i)
if (writetype[i])
for (i = old0; i < typecnt; i++)
if (!omittype[i])
putc(ttisstds[i], fp);
for (i = 0; i < typecnt; ++i)
if (writetype[i])
for (i = old0; i < typecnt; i++)
if (!omittype[i])
putc(ttisgmts[i], fp);
swaptypes(old0, thisdefaulttype);
}
fprintf(fp, "\n%s\n", string);
close_file(fp, directory, name);
@ -2343,6 +2426,12 @@ stringzone(char *result, const int resultlen, const struct zone *const zpfirst,
struct rule stdr, dstr;
result[0] = '\0';
/* Internet RFC 8536 section 5.1 says to use an empty TZ string if
future timestamps are truncated. */
if (hi_time < max_time)
return -1;
zp = zpfirst + zonecount - 1;
stdrp = dstrp = NULL;
for (i = 0; i < zp->z_nrules; ++i) {
@ -2477,6 +2566,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
zic_t one = 1;
zic_t y2038_boundary = one << 31;
zic_t max_year0;
int defaulttype = -1;
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
@ -2585,9 +2675,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
*/
stdoff = 0;
zp = &zpfirst[i];
usestart = i > 0 && (zp - 1)->z_untiltime > early_time;
usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
useuntil = i < (zonecount - 1);
if (useuntil && zp->z_untiltime <= early_time)
if (useuntil && zp->z_untiltime <= min_time)
continue;
gmtoff = zp->z_gmtoff;
eat(zp->z_filename, zp->z_linenum);
@ -2598,12 +2688,13 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
doabbr(startbuf, max_abbr_len + 1, zp,
NULL, zp->z_isdst, stdoff, false);
type = addtype(oadd(zp->z_gmtoff, stdoff),
startbuf, stdoff != 0, startttisstd,
startbuf, zp->z_isdst, startttisstd,
startttisgmt);
if (usestart) {
addtt(starttime, type);
usestart = false;
} else addtt(early_time, type);
} else
defaulttype = type;
} else for (year = min_year; year <= max_year; ++year) {
if (useuntil && year > zp->z_untilrule.r_hiyear)
break;
@ -2720,6 +2811,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
offset = oadd(zp->z_gmtoff, rp->r_stdoff);
type = addtype(offset, ab, rp->r_isdst,
rp->r_todisstd, rp->r_todisgmt);
if (defaulttype < 0 && !rp->r_isdst)
defaulttype = type;
if (rp->r_hiyear == ZIC_MAX
&& ! (0 <= lastatmax
&& ktime < attypes[lastatmax].at))
@ -2737,11 +2830,14 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
eat(zp->z_filename, zp->z_linenum);
if (*startbuf == '\0')
error(_("can't determine time zone abbreviation to use just after until time"));
else addtt(starttime,
addtype(startoff, startbuf,
startoff != zp->z_gmtoff,
startttisstd,
startttisgmt));
else {
bool isdst = startoff != zp->z_gmtoff;
type = addtype(startoff, startbuf, isdst,
startttisstd, startttisgmt);
if (defaulttype < 0 && !isdst)
defaulttype = type;
addtt(starttime, type);
}
}
/*
** Now we may get to set starttime for the next zone line.
@ -2756,6 +2852,8 @@ error(_("can't determine time zone abbreviation to use just after until time"));
starttime = tadd(starttime, -gmtoff);
}
}
if (defaulttype < 0)
defaulttype = 0;
if (0 <= lastatmax)
attypes[lastatmax].dontmerge = true;
if (do_extend) {
@ -2776,15 +2874,16 @@ error(_("can't determine time zone abbreviation to use just after until time"));
xr.r_dycode = DC_DOM;
xr.r_dayofmonth = 1;
xr.r_tod = 0;
for (lastat = &attypes[0], i = 1; i < timecnt; i++)
for (lastat = attypes, i = 1; i < timecnt; i++)
if (attypes[i].at > lastat->at)
lastat = &attypes[i];
if (lastat->at < rpytime(&xr, max_year - 1)) {
addtt(rpytime(&xr, max_year + 1), lastat->type);
if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) {
addtt(rpytime(&xr, max_year + 1),
lastat ? lastat->type : defaulttype);
attypes[timecnt - 1].dontmerge = true;
}
}
writezone(zpfirst->z_name, envvar, version);
writezone(zpfirst->z_name, envvar, version, defaulttype);
free(startbuf);
free(ab);
free(envvar);
@ -2793,20 +2892,6 @@ error(_("can't determine time zone abbreviation to use just after until time"));
static void
addtt(zic_t starttime, int type)
{
if (starttime <= early_time
|| (timecnt == 1 && attypes[0].at < early_time)) {
gmtoffs[0] = gmtoffs[type];
isdsts[0] = isdsts[type];
ttisstds[0] = ttisstds[type];
ttisgmts[0] = ttisgmts[type];
if (abbrinds[type] != 0)
strcpy(chars, &chars[abbrinds[type]]);
abbrinds[0] = 0;
charcnt = strlen(chars) + 1;
typecnt = 1;
timecnt = 0;
type = 0;
}
attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
attypes[timecnt].at = starttime;
attypes[timecnt].dontmerge = false;
@ -2966,7 +3051,7 @@ is_alpha(char a)
{
switch (a) {
default:
return 0;
return false;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
@ -3179,6 +3264,15 @@ rpytime(const struct rule *rp, zic_t wantedy)
dayoff = 0;
m = TM_JANUARY;
y = EPOCH_YEAR;
if (y < wantedy) {
wantedy -= y;
dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
wantedy %= YEARSPERREPEAT;
wantedy += y;
} else if (wantedy < 0) {
dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
wantedy %= YEARSPERREPEAT;
}
while (wantedy != y) {
if (wantedy > y) {
i = len_years[isleap(y)];