proposed change to 'zic' to support "outlandish" offsets

Paul Eggert eggert at CS.UCLA.EDU
Mon Apr 23 22:37:05 UTC 2007


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



More information about the tz mailing list