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:
5
NEWS
5
NEWS
@ -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
13
zic.8
@ -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
82
zic.c
@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user