Currently zic allows only GMT offsets in the range -24:00 to 24:00. And yet the recent query about Mt. Athos led me to think that in some cases it can be useful to allow GMT offsets outside the range, to approximate the Julian calendar. And at any rate zic should be able to generate whatever offset can be stored in the standard tz file format, if only for testing purposes. So here is a proposed patch to the zic code and documentation to support this. I'll follow up with a message illustrating how this might be used to support Julian dates and civil times on Mt. Athos. =================================================================== RCS file: RCS/zic.8,v retrieving revision 2007.4 retrieving revision 2007.4.0.1 diff -pu -r2007.4 -r2007.4.0.1 --- zic.8 2007/03/20 12:48:10 2007.4 +++ zic.8 2007/04/23 22:27:49 2007.4.0.1 @@ -239,14 +239,35 @@ wall clock time is assumed. .B SAVE Gives the amount of time to be added to local standard time when the rule is in effect. -This field has the same format as the +This field's format is similar to that of the .B AT -field -(although, of course, the -.B w +field, except that the +.BR s , +.BR u , and -.B s -suffixes are not used). +.B w +suffixes are not used. +.IP +Also, for completeness, the +.B SAVE +field can be preceded by an +optional day count of the form +.IB N d +where +.I N +is a nonnegative integer number of days. +Both the day count and the time can be preceded by an optional sign; +if the second sign is omitted it is assumed to be the same as the first. +A day consists of 24 hours. +For example, +.B \-2d+23:45 +means minus 2 days, plus 23 hours and 45 minutes, and is equivalent +to +.B \-1d0:15 +which means minus 1 day, minus another 15 minutes. +The day count is not needed for any real-world example using the +Gregorian calendar, but it may be useful for testing, or for +half-baked approximations to other calendars. .TP .B LETTER/S Gives the @@ -287,15 +308,16 @@ zone. .B GMTOFF The amount of time to add to UTC to get standard time in this zone. This field has the same format as the -.B AT -and .B SAVE fields of rule lines; begin the field with a minus sign if time must be subtracted from UTC. .TP .B RULES/SAVE The name of the rule(s) that apply in the time zone or, -alternately, an amount of time to add to local standard time. +alternately, an amount of time to add to local standard time, +using the same format as the +.B SAVE +fields of rule lines. If this field is .B \- then standard time always applies in the time zone. =================================================================== RCS file: RCS/zic.c,v retrieving revision 2007.5 retrieving revision 2007.5.0.1 diff -pu -r2007.5 -r2007.5.0.1 --- zic.c 2007/03/26 17:53:22 2007.5 +++ zic.c 2007/04/23 22:27:49 2007.5.0.1 @@ -914,8 +914,12 @@ _("%s: panic: Invalid l_value %d\n"), /* ** Convert a string of one of the forms -** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss +** h hh:mm hh:mm:ss ** into a number of seconds. +** The string can be preceded by an optional Nd, where N is a day count. +** Both the day count and the time can be preceded by an optional sign. +** For example, "-1d+23:45" means minus 1 day, plus 23 hours and 45 minutes. +** If the second sign is omitted it is assumed to be the same as the first. ** A null string maps to zero. ** Call error with errstring and return zero on errors. */ @@ -927,15 +931,27 @@ const char * const errstring; const int signable; { int hh, mm, ss, sign; + int abshourmax, gotday; + char * endptr; + long dd, dd1; if (string == NULL || *string == '\0') return 0; - if (!signable) - sign = 1; - else if (*string == '-') { - sign = -1; - ++string; - } else sign = 1; + sign = 1; + dd = 0; + gotday = FALSE; + if (signable) { + dd1 = strtol(string, &endptr, 10); + if (string != endptr && *endptr == 'd') { + if (*string == '-') + sign = -1; + string = endptr + 1; + dd = dd1; + gotday = TRUE; + if (noise) + warning(_("day offsets not handled by pre-2007 versions of zic")); + } + } if (sscanf(string, scheck(string, "%d"), &hh) == 1) mm = ss = 0; else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2) @@ -945,18 +961,28 @@ const int signable; error(errstring); return 0; } - if ((hh < 0 || hh >= HOURSPERDAY || + if (! (LONG_MIN / SECSPERDAY <= dd && dd <= LONG_MAX / SECSPERDAY)) { + error(_("time overflow")); + return 0; + } + abshourmax = HOURSPERDAY - (gotday || mm || ss); + if (hh < -abshourmax || hh > abshourmax || mm < 0 || mm >= MINSPERHOUR || - ss < 0 || ss > SECSPERMIN) && - !(hh == HOURSPERDAY && mm == 0 && ss == 0)) { + ss < 0 || ss > SECSPERMIN) { error(errstring); return 0; } + if (signable) + switch (*string) { + case '-': sign = -1; hh = -hh; break; + case '+': sign = 1; break; + } if (noise && hh == HOURSPERDAY) warning(_("24:00 not handled by pre-1998 versions of zic")); - return eitol(sign) * - (eitol(hh * MINSPERHOUR + mm) * - eitol(SECSPERMIN) + eitol(ss)); + return oadd(eitol(dd) * SECSPERDAY, + eitol(sign) * + (eitol(hh * MINSPERHOUR + mm) * + eitol(SECSPERMIN) + eitol(ss))); } static void