1
0
mirror of https://frontier.innolan.net/rainlance/amiga-tz.git synced 2025-11-19 23:09:30 +00:00

Support %z in Zone formats

This extends the zic input format to add support for %z, which
expands to a UTC offset in as-short-as-possible ISO 8601 format.
It's intended to better support zones that do not have an
established abbreviation already.  This is a change only to zic;
it does not affect the data, which can still be compiled with
older zic versions.
* NEWS, zic.8: Document this.
* zic.c (struct zone): New member z_format_specifier.
(PERCENT_Z_LEN_BOUND): New constant.
(max_abbrvar_len): Initialize to it, rather than to 0.
(associate, inzsub, doabbr): Add support for %z.
(abbroffset): New function.
(doabbr): 2nd arg is now struct zone *, not a char *.
All callers changed.
This commit is contained in:
Paul Eggert
2015-07-19 20:22:55 -07:00
parent 24ee915c34
commit e6a6d029b2
3 changed files with 85 additions and 15 deletions

5
NEWS
View File

@ -25,6 +25,11 @@ Unreleased, experimental changes
(Thanks to Jon Skeet and Arthur David Olson.) Constraints on (Thanks to Jon Skeet and Arthur David Olson.) Constraints on
simultaneity are now documented. simultaneity are now documented.
The two characters '%z' in a zone format now stand for the UTC
offset, e.g., '-07' for seven hours behind UTC and '+0530' for
five hours and thirty minutes ahead. This better supports time
zone abbreviations conforming to POSIX.1-2001 and later.
Changes affecting installed data files Changes affecting installed data files
Data entries have been simplified for Atlantic/Canary, Europe/Simferopol, Data entries have been simplified for Atlantic/Canary, Europe/Simferopol,

13
zic.8
View File

@ -350,6 +350,19 @@ The pair of characters
is used to show where the is used to show where the
.q "variable part" .q "variable part"
of the time zone abbreviation goes. of the time zone abbreviation goes.
Alternately, a format can use the pair of characters
.B %z
to stand for the UTC offset in the form
.RI \(+- hh ,
.RI \(+- hhmm ,
or
.RI \(+- hhmmss ,
using the shortest form that does not lose information, where
.IR hh ,
.IR mm ,
and
.I ss
are the hours, minutes, and seconds east (+) or west (\(mi) of UTC.
Alternately, Alternately,
a slash (/) a slash (/)
separates standard and daylight abbreviations. separates standard and daylight abbreviations.

82
zic.c
View File

@ -76,6 +76,7 @@ struct zone {
zic_t z_gmtoff; zic_t z_gmtoff;
const char * z_rule; const char * z_rule;
const char * z_format; const char * z_format;
char z_format_specifier;
zic_t z_stdoff; zic_t z_stdoff;
@ -130,6 +131,9 @@ static void rulesub(struct rule * rp,
static zic_t tadd(zic_t t1, zic_t t2); static zic_t tadd(zic_t t1, zic_t t2);
static bool yearistype(int year, const char * type); static bool yearistype(int year, const char * type);
/* Bound on length of what %z can expand to. */
enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
static int charcnt; static int charcnt;
static bool errors; static bool errors;
static bool warnings; static bool warnings;
@ -139,7 +143,7 @@ static bool leapseen;
static zic_t leapminyear; static zic_t leapminyear;
static zic_t leapmaxyear; static zic_t leapmaxyear;
static int linenum; static int linenum;
static int max_abbrvar_len; static int max_abbrvar_len = PERCENT_Z_LEN_BOUND;
static int max_format_len; static int max_format_len;
static zic_t max_year; static zic_t max_year;
static zic_t min_year; static zic_t min_year;
@ -955,7 +959,7 @@ associate(void)
** Note, though, that if there's no rule, ** Note, though, that if there's no rule,
** a '%s' in the format is a bad thing. ** a '%s' in the format is a bad thing.
*/ */
if (strchr(zp->z_format, '%') != 0) if (zp->z_format_specifier == 's')
error("%s", _("%s in ruleless zone")); error("%s", _("%s in ruleless zone"));
} }
} }
@ -1170,6 +1174,7 @@ static bool
inzsub(char **fields, int nfields, bool iscont) inzsub(char **fields, int nfields, bool iscont)
{ {
register char * cp; register char * cp;
char * cp1;
static struct zone z; static struct zone z;
register int i_gmtoff, i_rule, i_format; register int i_gmtoff, i_rule, i_format;
register int i_untilyear, i_untilmonth; register int i_untilyear, i_untilmonth;
@ -1201,13 +1206,21 @@ inzsub(char **fields, int nfields, bool iscont)
z.z_linenum = linenum; 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"), true);
if ((cp = strchr(fields[i_format], '%')) != 0) { if ((cp = strchr(fields[i_format], '%')) != 0) {
if (*++cp != 's' || strchr(cp, '%') != 0) { if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/')) {
error(_("invalid abbreviation format")); error(_("invalid abbreviation format"));
return false; return false;
} }
} }
z.z_rule = ecpyalloc(fields[i_rule]); z.z_rule = ecpyalloc(fields[i_rule]);
z.z_format = ecpyalloc(fields[i_format]); z.z_format = cp1 = ecpyalloc(fields[i_format]);
z.z_format_specifier = cp ? *cp : '\0';
if (z.z_format_specifier == 'z') {
if (noise)
warning(_("format '%s' not handled by pre-2015 versions of zic"),
z.z_format);
cp1[cp - fields[i_format]] = 's';
}
if (max_format_len < strlen(z.z_format)) if (max_format_len < strlen(z.z_format))
max_format_len = strlen(z.z_format); max_format_len = strlen(z.z_format);
hasuntil = nfields > i_untilyear; hasuntil = nfields > i_untilyear;
@ -1890,19 +1903,59 @@ writezone(const char *const name, const char *const string, char version)
free(fullname); free(fullname);
} }
static char const *
abbroffset(char *buf, zic_t offset)
{
char sign = '+';
int seconds, minutes;
if (offset < 0) {
offset = -offset;
sign = '-';
}
seconds = offset % SECSPERMIN;
offset /= SECSPERMIN;
minutes = offset % MINSPERHOUR;
offset /= MINSPERHOUR;
if (100 <= offset) {
error(_("%%z UTC offset magnitude exceeds 99:59:59"));
return "%z";
} else {
char *p = buf;
*p++ = sign;
*p++ = '0' + offset / 10;
*p++ = '0' + offset % 10;
if (minutes | seconds) {
*p++ = '0' + minutes / 10;
*p++ = '0' + minutes % 10;
if (seconds) {
*p++ = '0' + seconds / 10;
*p++ = '0' + seconds % 10;
}
}
*p = '\0';
return buf;
}
}
static size_t static size_t
doabbr(char *const abbr, const char *const format, const char *const letters, doabbr(char *abbr, struct zone const *zp, char const *letters,
bool isdst, bool doquotes) bool isdst, bool doquotes)
{ {
register char * cp; register char * cp;
register char * slashp; register char * slashp;
register size_t len; register size_t len;
char const *format = zp->z_format;
slashp = strchr(format, '/'); slashp = strchr(format, '/');
if (slashp == NULL) { if (slashp == NULL) {
if (letters == NULL) char letterbuf[PERCENT_Z_LEN_BOUND + 1];
strcpy(abbr, format); if (zp->z_format_specifier == 'z')
else sprintf(abbr, format, letters); letters = abbroffset(letterbuf, -zp->z_gmtoff);
else if (!letters)
letters = "%s";
sprintf(abbr, format, letters);
} else if (isdst) { } else if (isdst) {
strcpy(abbr, slashp + 1); strcpy(abbr, slashp + 1);
} else { } else {
@ -2127,7 +2180,7 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
return -1; return -1;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
len = doabbr(result, zp->z_format, abbrvar, false, true); len = doabbr(result, zp, abbrvar, false, true);
offsetlen = stringoffset(result + len, -zp->z_gmtoff); offsetlen = stringoffset(result + len, -zp->z_gmtoff);
if (! offsetlen) { if (! offsetlen) {
result[0] = '\0'; result[0] = '\0';
@ -2136,7 +2189,7 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
len += offsetlen; len += offsetlen;
if (dstrp == NULL) if (dstrp == NULL)
return compat; return compat;
len += doabbr(result + len, zp->z_format, dstrp->r_abbrvar, true, true); len += doabbr(result + len, zp, dstrp->r_abbrvar, true, true);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) { if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) {
offsetlen = stringoffset(result + len, offsetlen = stringoffset(result + len,
-(zp->z_gmtoff + dstrp->r_stdoff)); -(zp->z_gmtoff + dstrp->r_stdoff));
@ -2307,8 +2360,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
startoff = zp->z_gmtoff; startoff = zp->z_gmtoff;
if (zp->z_nrules == 0) { if (zp->z_nrules == 0) {
stdoff = zp->z_stdoff; stdoff = zp->z_stdoff;
doabbr(startbuf, zp->z_format, doabbr(startbuf, zp, NULL, stdoff != 0, false);
NULL, stdoff != 0, false);
type = addtype(oadd(zp->z_gmtoff, stdoff), type = addtype(oadd(zp->z_gmtoff, stdoff),
startbuf, stdoff != 0, startttisstd, startbuf, stdoff != 0, startttisstd,
startttisgmt); startttisgmt);
@ -2400,7 +2452,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
if (ktime < starttime) { if (ktime < starttime) {
startoff = oadd(zp->z_gmtoff, startoff = oadd(zp->z_gmtoff,
stdoff); stdoff);
doabbr(startbuf, zp->z_format, doabbr(startbuf, zp,
rp->r_abbrvar, rp->r_abbrvar,
rp->r_stdoff != 0, rp->r_stdoff != 0,
false); false);
@ -2410,7 +2462,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
startoff == oadd(zp->z_gmtoff, startoff == oadd(zp->z_gmtoff,
stdoff)) { stdoff)) {
doabbr(startbuf, doabbr(startbuf,
zp->z_format, zp,
rp->r_abbrvar, rp->r_abbrvar,
rp->r_stdoff != rp->r_stdoff !=
false, false,
@ -2419,7 +2471,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
} }
eats(zp->z_filename, zp->z_linenum, eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum); rp->r_filename, rp->r_linenum);
doabbr(ab, zp->z_format, rp->r_abbrvar, doabbr(ab, zp, rp->r_abbrvar,
rp->r_stdoff != 0, false); rp->r_stdoff != 0, false);
offset = oadd(zp->z_gmtoff, rp->r_stdoff); offset = oadd(zp->z_gmtoff, rp->r_stdoff);
type = addtype(offset, ab, rp->r_stdoff != 0, type = addtype(offset, ab, rp->r_stdoff != 0,