diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2005-07-05 13:40:52 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2005-07-05 13:40:52 +0000 |
commit | 9bb16badc66573c132b67bb6f23e8ca692eedb65 (patch) | |
tree | e31874322aa666685b8c54d940f1c35e8ccf6c3c /lib/libc/time | |
parent | 61fb473f97cfd7b86cb2e1e5308693c7c2a9cadf (diff) |
Update to tzcode2005j.
Diffstat (limited to 'lib/libc/time')
-rw-r--r-- | lib/libc/time/README | 14 | ||||
-rw-r--r-- | lib/libc/time/asctime.c | 37 | ||||
-rw-r--r-- | lib/libc/time/ctime.3 | 8 | ||||
-rw-r--r-- | lib/libc/time/difftime.c | 100 | ||||
-rw-r--r-- | lib/libc/time/ialloc.c | 10 | ||||
-rw-r--r-- | lib/libc/time/localtime.c | 385 | ||||
-rw-r--r-- | lib/libc/time/private.h | 47 | ||||
-rw-r--r-- | lib/libc/time/scheck.c | 6 | ||||
-rw-r--r-- | lib/libc/time/strftime.c | 128 | ||||
-rw-r--r-- | lib/libc/time/tz-art.htm | 4 | ||||
-rw-r--r-- | lib/libc/time/tz-link.htm | 96 | ||||
-rw-r--r-- | lib/libc/time/tzfile.h | 37 | ||||
-rw-r--r-- | lib/libc/time/zdump.8 | 29 | ||||
-rw-r--r-- | lib/libc/time/zdump.c | 425 | ||||
-rw-r--r-- | lib/libc/time/zic.c | 201 |
15 files changed, 1041 insertions, 486 deletions
diff --git a/lib/libc/time/README b/lib/libc/time/README index c143c778e96..180384cb1c5 100644 --- a/lib/libc/time/README +++ b/lib/libc/time/README @@ -1,5 +1,5 @@ -$OpenBSD: README,v 1.4 2002/04/04 19:12:08 millert Exp $ -@(#)README 7.11 +$OpenBSD: README,v 1.5 2005/07/05 13:40:51 millert Exp $ +@(#)README 7.12 "What time is it?" -- Richard Deacon as The King "Any time you want it to be." -- Frank Baxter as The Scientist @@ -53,8 +53,10 @@ substituting your desired installation directory for "$HOME/tzdir": To use the new functions, use a "-ltz" option when compiling or linking. -Historical local time information has been included here not because it -is particularly useful, but rather to: +Historical local time information has been included here to: + +* provide a compendium of data about the history of civil time + that is useful even if the data are not 100% accurate; * give an idea of the variety of local time rules that have existed in the past and thus an idea of the variety that may be @@ -64,7 +66,9 @@ is particularly useful, but rather to: system. The information in the time zone data files is by no means authoritative; -if you know that the rules are different from those in a file, by all means +the files currently do not even attempt to covar all time stamps before +1970, and there are undoubtedly errors even for time stamps since 1970. +If you know that the rules are different from those in a file, by all means feel free to change file (and please send the changed version to tz@elsie.nci.nih.gov for use in the future). Europeans take note! diff --git a/lib/libc/time/asctime.c b/lib/libc/time/asctime.c index 3004075cf0b..f9b299a0740 100644 --- a/lib/libc/time/asctime.c +++ b/lib/libc/time/asctime.c @@ -3,9 +3,15 @@ ** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ +/* +** Avoid the temptation to punt entirely to strftime; +** the output of strftime is supposed to be locale specific +** whereas the output of asctime is supposed to be constant. +*/ + #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)asctime.c 7.22"; -static char rcsid[] = "$OpenBSD: asctime.c,v 1.11 2005/03/06 01:40:05 cloder Exp $"; +static char elsieid[] = "@(#)asctime.c 7.31"; +static char rcsid[] = "$OpenBSD: asctime.c,v 1.12 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ /*LINTLIBRARY*/ @@ -14,10 +20,6 @@ static char rcsid[] = "$OpenBSD: asctime.c,v 1.11 2005/03/06 01:40:05 cloder Exp #include "tzfile.h" #include "thread_private.h" -#if STRICTLY_STANDARD_ASCTIME -#define ASCTIME_FMT "%.3s %.3s%3d %.2d:%.2d:%.2d %ld\n" -#define ASCTIME_FMT_B ASCTIME_FMT -#else /* !STRICTLY_STANDARD_ASCTIME */ /* ** Some systems only handle "%.2d"; others only handle "%02d"; ** "%02.2d" makes (most) everybody happy. @@ -29,17 +31,20 @@ static char rcsid[] = "$OpenBSD: asctime.c,v 1.11 2005/03/06 01:40:05 cloder Exp ** Vintage programs are coded for years that are always four digits long ** and may assume that the newline always lands in the same place. ** For years that are less than four digits, we pad the output with -** spaces before the newline to get the newline in the traditional place. +** leading zeroes to get the newline in the traditional place. +** The -4 ensures that we get four characters of output even if +** we call a strftime variant that produces fewer characters for some years. +** The ISO C 1999 and POSIX 1003.1-2004 standards prohibit padding the year, +** but many implementations pad anyway; most likely the standards are buggy. */ -#define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4ld\n" +#define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n" /* ** For years that are more than four digits we put extra spaces before the year ** so that code trying to overwrite the newline won't end up overwriting ** a digit within a year and truncating the year (operating on the assumption ** that no output is better than wrong output). */ -#define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %ld\n" -#endif /* !STRICTLY_STANDARD_ASCTIME */ +#define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %s\n" #define STD_ASCTIME_BUF_SIZE 26 /* @@ -69,7 +74,7 @@ int bufsize; }; register const char * wn; register const char * mn; - long year; + char year[INT_STRLEN_MAXIMUM(int) + 2]; int len; if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) @@ -78,9 +83,15 @@ int bufsize; if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; - year = timeptr->tm_year + (long) TM_YEAR_BASE; + /* + ** Use strftime's %Y to generate the year, to avoid overflow problems + ** when computing timeptr->tm_year + TM_YEAR_BASE. + ** Assume that strftime is unaffected by other out-of-range members + ** (e.g., timeptr->tm_mday) when processing "%Y". + */ + (void) strftime(year, sizeof year, "%Y", timeptr); len = snprintf(buf, bufsize, - ((year >= -999 && year <= 9999) ? ASCTIME_FMT : ASCTIME_FMT_B), + ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, diff --git a/lib/libc/time/ctime.3 b/lib/libc/time/ctime.3 index d7bc31615f2..f262932ab41 100644 --- a/lib/libc/time/ctime.3 +++ b/lib/libc/time/ctime.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ctime.3,v 1.29 2004/10/18 22:33:43 millert Exp $ +.\" $OpenBSD: ctime.3,v 1.30 2005/07/05 13:40:51 millert Exp $ .\" .\" .Dd February 16, 1999 @@ -63,13 +63,13 @@ string of the form .Pp .Dl Thu Nov 24 18:22:48 1986\en .Pp -Years requiring fewer than four characters are padded with trailing spaces. +Years requiring fewer than four characters are padded with leading zeroes. For years longer than four characters, the string is of the form .Pp .Dl Thu Nov 24 18:22:48\ \ \ \ \ 81986\en .Pp with five spaces before the year. -This unusual format is designed to make it less likely that older +These unusual formats are designed to make it less likely that older software that expects exactly 26 bytes of output will mistakenly output misleading values for out-of-range years. .Pp @@ -99,7 +99,7 @@ functions return pointers to structures, described below. .Fn localtime corrects for the time zone and any time zone adjustments -(such as Daylight Saving Time in the U.S.A.). +(such as Daylight Saving Time in the United States). After filling in the .Li tm structure, diff --git a/lib/libc/time/difftime.c b/lib/libc/time/difftime.c index d5fa21fec35..efc0da2695e 100644 --- a/lib/libc/time/difftime.c +++ b/lib/libc/time/difftime.c @@ -1,82 +1,64 @@ /* ** This file is in the public domain, so clarified as of -** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)difftime.c 7.9"; -static char rcsid[] = "$OpenBSD: difftime.c,v 1.6 2002/04/04 19:12:09 millert Exp $"; +static char elsieid[] = "@(#)difftime.c 7.18"; +static char rcsid[] = "$OpenBSD: difftime.c,v 1.7 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ /*LINTLIBRARY*/ -#include "private.h" - -/* -** Algorithm courtesy Paul Eggert (eggert@twinsun.com). -*/ - -#ifdef HAVE_LONG_DOUBLE -#define long_double long double -#endif /* defined HAVE_LONG_DOUBLE */ -#ifndef HAVE_LONG_DOUBLE -#define long_double double -#endif /* !defined HAVE_LONG_DOUBLE */ +#include "private.h" /* for time_t, TYPE_INTEGRAL, and TYPE_SIGNED */ double difftime(time1, time0) const time_t time1; const time_t time0; { - time_t delta; - time_t hibit; - - { - time_t tt; - double d; - long_double ld; - - if (sizeof tt < sizeof d) - return (double) time1 - (double) time0; - if (sizeof tt < sizeof ld) - return (long_double) time1 - (long_double) time0; + /* + ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract + ** (assuming that the larger type has more precision). + ** This is the common real-world case circa 2004. + */ + if (sizeof (double) > sizeof (time_t)) + return (double) time1 - (double) time0; + if (!TYPE_INTEGRAL(time_t)) { + /* + ** time_t is floating. + */ + return time1 - time0; + } + if (!TYPE_SIGNED(time_t)) { + /* + ** time_t is integral and unsigned. + ** The difference of two unsigned values can't overflow + ** if the minuend is greater than or equal to the subtrahend. + */ + if (time1 >= time0) + return time1 - time0; + else return -((double) (time0 - time1)); } - if (time1 < time0) - return -difftime(time0, time1); /* - ** As much as possible, avoid loss of precision - ** by computing the difference before converting to double. + ** time_t is integral and signed. + ** Handle cases where both time1 and time0 have the same sign + ** (meaning that their difference cannot overflow). */ - delta = time1 - time0; - if (delta >= 0) - return delta; + if ((time1 < 0) == (time0 < 0)) + return time1 - time0; /* - ** Repair delta overflow. + ** time1 and time0 have opposite signs. + ** Punt if unsigned long is too narrow. */ - hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1); + if (sizeof (unsigned long) < sizeof (time_t)) + return (double) time1 - (double) time0; /* - ** The following expression rounds twice, which means - ** the result may not be the closest to the true answer. - ** For example, suppose time_t is 64-bit signed int, - ** long_double is IEEE 754 double with default rounding, - ** time1 = 9223372036854775807 and time0 = -1536. - ** Then the true difference is 9223372036854777343, - ** which rounds to 9223372036854777856 - ** with a total error of 513. - ** But delta overflows to -9223372036854774273, - ** which rounds to -9223372036854774784, and correcting - ** this by subtracting 2 * (long_double) hibit - ** (i.e. by adding 2**64 = 18446744073709551616) - ** yields 9223372036854776832, which - ** rounds to 9223372036854775808 - ** with a total error of 1535 instead. - ** This problem occurs only with very large differences. - ** It's too painful to fix this portably. - ** We are not alone in this problem; - ** some C compilers round twice when converting - ** large unsigned types to small floating types, - ** so if time_t is unsigned the "return delta" above - ** has the same double-rounding problem with those compilers. + ** Stay calm...decent optimizers will eliminate the complexity below. */ - return delta - 2 * (long_double) hibit; + if (time1 >= 0 /* && time0 < 0 */) + return (unsigned long) time1 + + (unsigned long) (-(time0 + 1)) + 1; + return -(double) ((unsigned long) time0 + + (unsigned long) (-(time1 + 1)) + 1); } diff --git a/lib/libc/time/ialloc.c b/lib/libc/time/ialloc.c index 10055e77559..395f92a8a4f 100644 --- a/lib/libc/time/ialloc.c +++ b/lib/libc/time/ialloc.c @@ -5,7 +5,7 @@ #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) static char elsieid[] = "@(#)ialloc.c 8.29"; -static char rcsid[] = "$OpenBSD: ialloc.c,v 1.8 2003/03/13 15:47:34 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: ialloc.c,v 1.9 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ /*LINTLIBRARY*/ @@ -41,7 +41,7 @@ const int size; if (pointer == NULL) return imalloc(size); p = realloc((void *) pointer, (size_t) nonzero(size)); - if (p == NULL && pointer) + if (p == NULL) free(pointer); return p; } @@ -53,7 +53,6 @@ const char * const new; { register char * result; register int oldsize, newsize; - int size; newsize = (new == NULL) ? 0 : strlen(new); if (old == NULL) @@ -62,12 +61,9 @@ const char * const new; return old; else oldsize = strlen(old); - size = oldsize + newsize + 1; - if ((result = irealloc(old, size)) != NULL) + if ((result = irealloc(old, oldsize + newsize + 1)) != NULL) if (new != NULL) (void) strlcpy(result + oldsize, new, newsize + 1); - else - free(old); return result; } diff --git a/lib/libc/time/localtime.c b/lib/libc/time/localtime.c index e9bb299afe9..f49fca6042c 100644 --- a/lib/libc/time/localtime.c +++ b/lib/libc/time/localtime.c @@ -4,8 +4,8 @@ */ #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)localtime.c 7.80"; -static char rcsid[] = "$OpenBSD: localtime.c,v 1.25 2004/10/18 22:33:43 millert Exp $"; +static char elsieid[] = "@(#)localtime.c 7.95"; +static char rcsid[] = "$OpenBSD: localtime.c,v 1.26 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ /* @@ -19,8 +19,22 @@ static char rcsid[] = "$OpenBSD: localtime.c,v 1.25 2004/10/18 22:33:43 millert #include "private.h" #include "tzfile.h" #include "fcntl.h" +#include "float.h" /* for FLT_MAX and DBL_MAX */ #include "thread_private.h" +#ifndef TZ_ABBR_MAX_LEN +#define TZ_ABBR_MAX_LEN 16 +#endif /* !defined TZ_ABBR_MAX_LEN */ + +#ifndef TZ_ABBR_CHAR_SET +#define TZ_ABBR_CHAR_SET \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" +#endif /* !defined TZ_ABBR_CHAR_SET */ + +#ifndef TZ_ABBR_ERR_CHAR +#define TZ_ABBR_ERR_CHAR '_' +#endif /* !defined TZ_ABBR_ERR_CHAR */ + /* ** SunOS 4.1.1 headers lack O_BINARY. */ @@ -46,16 +60,16 @@ static char rcsid[] = "$OpenBSD: localtime.c,v 1.25 2004/10/18 22:33:43 millert ** 5. They might reference tm.TM_ZONE after calling offtime. ** What's best to do in the above cases is open to debate; ** for now, we just set things up so that in any of the five cases -** WILDABBR is used. Another possibility: initialize tzname[0] to the +** WILDABBR is used. Another possibility: initialize tzname[0] to the ** string "tzname[0] used before set", and similarly for the other cases. -** And another: initialize tzname[0] to "ERA", with an explanation in the +** And another: initialize tzname[0] to "ERA", with an explanation in the ** manual page of what this "time zone abbreviation" means (doing this so ** that tzname[0] has the "normal" length of three characters). */ #define WILDABBR " " #endif /* !defined WILDABBR */ -static char wildabbr[] = "WILDABBR"; +static char wildabbr[] = WILDABBR; static const char gmt[] = "GMT"; @@ -123,17 +137,19 @@ struct rule { static long detzcode P((const char * codep)); static const char * getzname P((const char * strp)); +static const char * getqzname P((const char * strp, const char delim)); static const char * getnum P((const char * strp, int * nump, int min, int max)); static const char * getsecs P((const char * strp, long * secsp)); static const char * getoffset P((const char * strp, long * offsetp)); static const char * getrule P((const char * strp, struct rule * rulep)); static void gmtload P((struct state * sp)); -static void gmtsub P((const time_t * timep, long offset, +static struct tm * gmtsub P((const time_t * timep, long offset, struct tm * tmp)); -static void localsub P((const time_t * timep, long offset, +static struct tm * localsub P((const time_t * timep, long offset, struct tm * tmp)); static int increment_overflow P((int * number, int delta)); +static int leaps_thru_end_of P((int y)); static int long_increment_overflow P((long * number, int delta)); static int long_normalize_overflow P((long * tensptr, int * unitsptr, int base)); @@ -141,18 +157,18 @@ static int normalize_overflow P((int * tensptr, int * unitsptr, int base)); static void settzname P((void)); static time_t time1 P((struct tm * tmp, - void(*funcp) P((const time_t *, + struct tm * (*funcp) P((const time_t *, long, struct tm *)), long offset)); static time_t time2 P((struct tm *tmp, - void(*funcp) P((const time_t *, + struct tm * (*funcp) P((const time_t *, long, struct tm*)), long offset, int * okayp)); static time_t time2sub P((struct tm *tmp, - void(*funcp) P((const time_t *, + struct tm * (*funcp) P((const time_t *, long, struct tm*)), long offset, int * okayp, int do_norm_secs)); -static void timesub P((const time_t * timep, long offset, +static struct tm * timesub P((const time_t * timep, long offset, const struct state * sp, struct tm * tmp)); static int tmcomp P((const struct tm * atmp, const struct tm * btmp)); @@ -269,6 +285,24 @@ settzname P((void)) tzname[ttisp->tt_isdst] = &sp->chars[ttisp->tt_abbrind]; } + /* + ** Finally, scrub the abbreviations. + ** First, replace bogus characters. + */ + for (i = 0; i < sp->charcnt; ++i) + if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL) + sp->chars[i] = TZ_ABBR_ERR_CHAR; + /* + ** Second, truncate long abbreviations. + */ + for (i = 0; i < sp->typecnt; ++i) { + register const struct ttinfo * const ttisp = &sp->ttis[i]; + register char * cp = &sp->chars[ttisp->tt_abbrind]; + + if (strlen(cp) > TZ_ABBR_MAX_LEN && + strcmp(cp, GRANDPARENTED) != 0) + *(cp + TZ_ABBR_MAX_LEN) = '\0'; + } } static int @@ -415,6 +449,33 @@ register struct state * const sp; return -1; } } + /* + ** Out-of-sort ats should mean we're running on a + ** signed time_t system but using a data file with + ** unsigned values (or vice versa). + */ + for (i = 0; i < sp->timecnt - 2; ++i) + if (sp->ats[i] > sp->ats[i + 1]) { + ++i; + if (TYPE_SIGNED(time_t)) { + /* + ** Ignore the end (easy). + */ + sp->timecnt = i; + } else { + /* + ** Ignore the beginning (harder). + */ + register int j; + + for (j = 0; j + i < sp->timecnt; ++j) { + sp->ats[j] = sp->ats[j + i]; + sp->types[j] = sp->types[j + i]; + } + sp->timecnt = j; + } + break; + } } return 0; } @@ -430,7 +491,7 @@ static const int year_lengths[2] = { /* ** Given a pointer into a time zone string, scan until a character that is not -** a valid character in a zone name is found. Return a pointer to that +** a valid character in a zone name is found. Return a pointer to that ** character. */ @@ -447,6 +508,27 @@ register const char * strp; } /* +** Given a pointer into an extended time zone string, scan until the ending +** delimiter of the zone name is located. Return a pointer to the delimiter. +** +** As with getzname above, the legal character set is actually quite +** restricted, with other characters producing undefined results. +** We choose not to care - allowing almost anything to be in the zone abbrev. +*/ + +static const char * +getqzname(strp, delim) +register const char * strp; +const char delim; +{ + register char c; + + while ((c = *strp) != '\0' && c != delim) + ++strp; + return strp; +} + +/* ** Given a pointer into a time zone string, extract a number from that string. ** Check that the number is within a specified range; if it is not, return ** NULL. @@ -511,7 +593,7 @@ long * const secsp; *secsp += num * SECSPERMIN; if (*strp == ':') { ++strp; - /* `SECSPERMIN' allows for leap seconds. */ + /* `SECSPERMIN' allows for leap seconds. */ strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) return NULL; @@ -550,7 +632,7 @@ long * const offsetp; /* ** Given a pointer into a time zone string, extract a rule in the form -** date[/time]. See POSIX section 8 for the format of "date" and "time". +** date[/time]. See POSIX section 8 for the format of "date" and "time". ** If a valid rule is not found, return NULL. ** Otherwise, return a pointer to the first character not part of the rule. */ @@ -669,7 +751,7 @@ const long offset; dow += DAYSPERWEEK; /* - ** "dow" is the day-of-week of the first day of the month. Get + ** "dow" is the day-of-week of the first day of the month. Get ** the day-of-month (zero-origin) of the first "dow" day of the ** month. */ @@ -692,7 +774,7 @@ const long offset; /* ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in - ** question. To get the Epoch-relative time of the specified local + ** question. To get the Epoch-relative time of the specified local ** time on that day, add the transition time and the current offset ** from UTC. */ @@ -730,10 +812,18 @@ const int lastditch; stdlen = (sizeof sp->chars) - 1; stdoffset = 0; } else { - name = getzname(name); - stdlen = name - stdname; - if (stdlen < 3) - return -1; + if (*name == '<') { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') + return (-1); + stdlen = name - stdname; + name++; + } else { + name = getzname(name); + stdlen = name - stdname; + } if (*name == '\0') return -1; name = getoffset(name, &stdoffset); @@ -744,11 +834,18 @@ const int lastditch; if (load_result != 0) sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - if (dstlen < 3) - return -1; + if (*name == '<') { + dstname = ++name; + name = getqzname(name, '>'); + if (*name != '>') + return -1; + dstlen = name - dstname; + name++; + } else { + dstname = name; + name = getzname(name); + dstlen = name - dstname; /* length of DST zone name */ + } if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); if (name == NULL) @@ -1014,14 +1111,14 @@ tzset P((void)) /* ** The easy way to behave "as if no library function calls" localtime ** is to not call it--so we drop its guts into "localsub", which can be -** freely called. (And no, the PANS doesn't require the above behavior-- +** freely called. (And no, the PANS doesn't require the above behavior-- ** but it *is* desirable.) ** ** The unused offset argument is for the benefit of mktime variants. */ /*ARGSUSED*/ -static void +static struct tm * localsub(timep, offset, tmp) const time_t * const timep; const long offset; @@ -1030,14 +1127,13 @@ struct tm * const tmp; register struct state * sp; register const struct ttinfo * ttisp; register int i; + register struct tm * result; const time_t t = *timep; sp = lclptr; #ifdef ALL_STATE - if (sp == NULL) { - gmtsub(timep, offset, tmp); - return; - } + if (sp == NULL) + return gmtsub(timep, offset, tmp); #endif /* defined ALL_STATE */ if (sp->timecnt == 0 || t < sp->ats[0]) { i = 0; @@ -1050,7 +1146,7 @@ struct tm * const tmp; for (i = 1; i < sp->timecnt; ++i) if (t < sp->ats[i]) break; - i = sp->types[i - 1]; + i = (int) sp->types[i - 1]; } ttisp = &sp->ttis[i]; /* @@ -1059,12 +1155,13 @@ struct tm * const tmp; ** t += ttisp->tt_gmtoff; ** timesub(&t, 0L, sp, tmp); */ - timesub(&t, ttisp->tt_gmtoff, sp, tmp); + result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); tmp->tm_isdst = ttisp->tt_isdst; tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; #ifdef TM_ZONE tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; #endif /* defined TM_ZONE */ + return result; } /* @@ -1099,12 +1196,14 @@ const time_t * const timep; ** gmtsub is to gmtime as localsub is to localtime. */ -static void +static struct tm * gmtsub(timep, offset, tmp) const time_t * const timep; const long offset; struct tm * const tmp; { + register struct tm * result; + _THREAD_PRIVATE_MUTEX_LOCK(gmt); if (!gmt_is_set) { gmt_is_set = TRUE; @@ -1115,7 +1214,7 @@ struct tm * const tmp; gmtload(gmtptr); } _THREAD_PRIVATE_MUTEX_UNLOCK(gmt); - timesub(timep, offset, gmtptr, tmp); + result = timesub(timep, offset, gmtptr, tmp); #ifdef TM_ZONE /* ** Could get fancy here and deliver something such as @@ -1135,6 +1234,7 @@ struct tm * const tmp; #endif /* State Farm */ } #endif /* defined TM_ZONE */ + return result; } /* @@ -1170,13 +1270,25 @@ offtime(timep, offset) const time_t * const timep; const long offset; { - gmtsub(timep, offset, &tm); - return &tm; + return gmtsub(timep, offset, &tm); } #endif /* defined STD_INSPIRED */ -static void +/* +** Return the number of leap years through the end of the given year +** where, to make the math easy, the answer for year zero is defined as zero. +*/ + +static int +leaps_thru_end_of(y) +register const int y; +{ + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : + -(leaps_thru_end_of(-(y + 1)) + 1); +} + +static struct tm * timesub(timep, offset, sp, tmp) const time_t * const timep; const long offset; @@ -1184,10 +1296,10 @@ register const struct state * const sp; register struct tm * const tmp; { register const struct lsinfo * lp; - register long days; + register time_t tdays; + register int idays; /* unsigned would be so 2003 */ register long rem; - register long y; - register int yleap; + int y; register const int * ip; register long corr; register int hit; @@ -1221,60 +1333,93 @@ register struct tm * const tmp; break; } } - days = *timep / SECSPERDAY; - rem = *timep % SECSPERDAY; -#ifdef mc68k - if (*timep == 0x80000000) { - /* - ** A 3B1 muffs the division on the most negative number. - */ - days = -24855; - rem = -11648; + y = EPOCH_YEAR; + tdays = *timep / SECSPERDAY; + rem = *timep - tdays * SECSPERDAY; + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { + int newy; + register time_t tdelta; + register int idelta; + register int leapdays; + + tdelta = tdays / DAYSPERLYEAR; + idelta = tdelta; + if (tdelta - idelta >= 1 || idelta - tdelta >= 1) + return NULL; + if (idelta == 0) + idelta = (tdays < 0) ? -1 : 1; + newy = y; + if (increment_overflow(&newy, idelta)) + return NULL; + leapdays = leaps_thru_end_of(newy - 1) - + leaps_thru_end_of(y - 1); + tdays -= ((time_t) newy - y) * DAYSPERNYEAR; + tdays -= leapdays; + y = newy; + } + { + register long seconds; + + seconds = tdays * SECSPERDAY + 0.5; + tdays = seconds / SECSPERDAY; + rem += seconds - tdays * SECSPERDAY; } -#endif /* defined mc68k */ - rem += (offset - corr); + /* + ** Given the range, we can now fearlessly cast... + */ + idays = tdays; + rem += offset - corr; while (rem < 0) { rem += SECSPERDAY; - --days; + --idays; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; - ++days; + ++idays; } + while (idays < 0) { + if (increment_overflow(&y, -1)) + return NULL; + idays += year_lengths[isleap(y)]; + } + while (idays >= year_lengths[isleap(y)]) { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) + return NULL; + } + tmp->tm_year = y; + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) + return NULL; + tmp->tm_yday = idays; + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = EPOCH_WDAY + + ((y - EPOCH_YEAR) % DAYSPERWEEK) * + (DAYSPERNYEAR % DAYSPERWEEK) + + leaps_thru_end_of(y - 1) - + leaps_thru_end_of(EPOCH_YEAR - 1) + + idays; + tmp->tm_wday %= DAYSPERWEEK; + if (tmp->tm_wday < 0) + tmp->tm_wday += DAYSPERWEEK; tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem = rem % SECSPERHOUR; + rem %= SECSPERHOUR; tmp->tm_min = (int) (rem / SECSPERMIN); /* ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. + ** representation. This uses "... ??:59:60" et seq. */ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); - if (tmp->tm_wday < 0) - tmp->tm_wday += DAYSPERWEEK; - y = EPOCH_YEAR; -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) - while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { - register long newy; - - newy = y + days / DAYSPERNYEAR; - if (days < 0) - --newy; - days -= (newy - y) * DAYSPERNYEAR + - LEAPS_THRU_END_OF(newy - 1) - - LEAPS_THRU_END_OF(y - 1); - y = newy; - } - tmp->tm_year = y - TM_YEAR_BASE; - tmp->tm_yday = (int) days; - ip = mon_lengths[yleap]; - for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) - days = days - (long) ip[tmp->tm_mon]; - tmp->tm_mday = (int) (days + 1); + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = (int) (idays + 1); tmp->tm_isdst = 0; #ifdef TM_GMTOFF tmp->TM_GMTOFF = offset; #endif /* defined TM_GMTOFF */ + return tmp; } char * @@ -1284,7 +1429,7 @@ const time_t * const timep; /* ** Section 4.12.3.2 of X3.159-1989 requires that ** The ctime function converts the calendar time pointed to by timer -** to local time in the form of a string. It is equivalent to +** to local time in the form of a string. It is equivalent to ** asctime(localtime(timer)) */ return asctime(localtime(timep)); @@ -1295,9 +1440,9 @@ ctime_r(timep, buf) const time_t * const timep; char * buf; { - struct tm tm; + struct tm mytm; - return asctime_r(localtime_r(timep, &tm), buf); + return asctime_r(localtime_r(timep, &mytm), buf); } /* @@ -1305,7 +1450,7 @@ char * buf; ** The "best" way to do mktime I think is based on an idea of Bob ** Kridle's (so its said...) from a long time ago. ** [kridle@xinet.com as of 1996-01-16.] -** It does a binary search of the time_t space. Since time_t's are +** It does a binary search of the time_t space. Since time_t's are ** just 32 bits, its a max of 32 iterations (even at 64 bits it ** would still be very reasonable). */ @@ -1391,17 +1536,18 @@ register const struct tm * const btmp; static time_t time2sub(tmp, funcp, offset, okayp, do_norm_secs) struct tm * const tmp; -void (* const funcp) P((const time_t*, long, struct tm*)); +struct tm * (* const funcp) P((const time_t*, long, struct tm*)); const long offset; int * const okayp; const int do_norm_secs; { register const struct state * sp; register int dir; - register int bits; register int i, j; register int saved_seconds; register long li; + register time_t lo; + register time_t hi; long y; time_t newt; time_t t; @@ -1454,7 +1600,7 @@ const int do_norm_secs; return WRONG; yourtm.tm_year = y; if (yourtm.tm_year != y) - return WRONG; + return WRONG; if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) saved_seconds = 0; else if (y + TM_YEAR_BASE < EPOCH_YEAR) { @@ -1475,27 +1621,49 @@ const int do_norm_secs; yourtm.tm_sec = 0; } /* - ** Divide the search space in half - ** (this works whether time_t is signed or unsigned). + ** Do a binary search (this works whatever time_t's type is). */ - bits = TYPE_BIT(time_t) - 1; - /* - ** If time_t is signed, then 0 is just above the median, - ** assuming two's complement arithmetic. - ** If time_t is unsigned, then (1 << bits) is just above the median. - */ - t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); + if (!TYPE_SIGNED(time_t)) { + lo = 0; + hi = lo - 1; + } else if (!TYPE_INTEGRAL(time_t)) { + if (sizeof(time_t) > sizeof(float)) + hi = (time_t) DBL_MAX; + else hi = (time_t) FLT_MAX; + lo = -hi; + } else { + lo = 1; + for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) + lo *= 2; + hi = -(lo + 1); + } for ( ; ; ) { - (*funcp)(&t, offset, &mytm); - dir = tmcomp(&mytm, &yourtm); + t = lo / 2 + hi / 2; + if (t < lo) + t = lo; + else if (t > hi) + t = hi; + if ((*funcp)(&t, offset, &mytm) == NULL) { + /* + ** Assume that t is too extreme to be represented in + ** a struct tm; arrange things so that it is less + ** extreme on the next pass. + */ + dir = (t > 0) ? 1 : -1; + } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { - if (bits-- < 0) + if (t == lo) { + ++t; + ++lo; + } else if (t == hi) { + --t; + --hi; + } + if (lo > hi) return WRONG; - if (bits < 0) - --t; /* may be needed if new t is minimal */ - else if (dir > 0) - t -= ((time_t) 1) << bits; - else t += ((time_t) 1) << bits; + if (dir > 0) + hi = t; + else lo = t; continue; } if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) @@ -1524,7 +1692,8 @@ const int do_norm_secs; continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; - (*funcp)(&newt, offset, &mytm); + if ((*funcp)(&newt, offset, &mytm) == NULL) + continue; if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) @@ -1543,15 +1712,15 @@ label: if ((newt < t) != (saved_seconds < 0)) return WRONG; t = newt; - (*funcp)(&t, offset, tmp); - *okayp = TRUE; + if ((*funcp)(&t, offset, tmp)) + *okayp = TRUE; return t; } static time_t time2(tmp, funcp, offset, okayp) struct tm * const tmp; -void (* const funcp) P((const time_t*, long, struct tm*)); +struct tm * (* const funcp) P((const time_t*, long, struct tm*)); const long offset; int * const okayp; { @@ -1569,7 +1738,7 @@ int * const okayp; static time_t time1(tmp, funcp, offset) struct tm * const tmp; -void (* const funcp) P((const time_t *, long, struct tm *)); +struct tm * (* const funcp) P((const time_t *, long, struct tm *)); const long offset; { register time_t t; @@ -1755,7 +1924,7 @@ time_t t; tzset(); /* ** For a positive leap second hit, the result - ** is not unique. For a negative leap second + ** is not unique. For a negative leap second ** hit, the corresponding time doesn't exist, ** so we return an adjacent second. */ diff --git a/lib/libc/time/private.h b/lib/libc/time/private.h index 72a1a785323..8f8c737e870 100644 --- a/lib/libc/time/private.h +++ b/lib/libc/time/private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: private.h,v 1.16 2004/10/18 22:33:43 millert Exp $ */ +/* $OpenBSD: private.h,v 1.17 2005/07/05 13:40:51 millert Exp $ */ #ifndef PRIVATE_H #define PRIVATE_H @@ -14,7 +14,6 @@ #define PCTS 1 #define XPG4_1994_04_09 1 #define STD_INSPIRED 1 -#define HAVE_LONG_DOUBLE 1 #define HAVE_STRERROR 1 #define NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU 1 @@ -33,11 +32,13 @@ #if 0 #ifndef lint #ifndef NOID -static char privatehid[] = "@(#)private.h 7.54"; +static char privatehid[] = "@(#)private.h 7.55"; #endif /* !defined NOID */ #endif /* !defined lint */ #endif +#define GRANDPARENTED "Local time zone must be set--see zic manual page" + /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. @@ -106,13 +107,13 @@ static char privatehid[] = "@(#)private.h 7.54"; #include "time.h" #include "stdlib.h" -#if HAVE_GETTEXT - 0 +#if HAVE_GETTEXT #include "libintl.h" -#endif /* HAVE_GETTEXT - 0 */ +#endif /* HAVE_GETTEXT */ -#if HAVE_SYS_WAIT_H - 0 +#if HAVE_SYS_WAIT_H #include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */ -#endif /* HAVE_SYS_WAIT_H - 0 */ +#endif /* HAVE_SYS_WAIT_H */ #ifndef WIFEXITED #define WIFEXITED(status) (((status) & 0xff) == 0) @@ -121,20 +122,20 @@ static char privatehid[] = "@(#)private.h 7.54"; #define WEXITSTATUS(status) (((status) >> 8) & 0xff) #endif /* !defined WEXITSTATUS */ -#if HAVE_UNISTD_H - 0 +#if HAVE_UNISTD_H #include "unistd.h" /* for F_OK and R_OK */ -#endif /* HAVE_UNISTD_H - 0 */ +#endif /* HAVE_UNISTD_H */ -#if !(HAVE_UNISTD_H - 0) +#if !HAVE_UNISTD_H #ifndef F_OK #define F_OK 0 #endif /* !defined F_OK */ #ifndef R_OK #define R_OK 4 #endif /* !defined R_OK */ -#endif /* !(HAVE_UNISTD_H - 0) */ +#endif /* !HAVE_UNISTD_H */ -/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ +/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) /* @@ -224,6 +225,7 @@ extern char * asctime_r(); /* ** Private function declarations. */ + char * icalloc P((int nelem, int elsize)); char * icatalloc P((char * old, const char * new)); char * icpyalloc P((const char * string)); @@ -231,8 +233,7 @@ char * imalloc P((int n)); void * irealloc P((void * pointer, int size)); void icfree P((char * pointer)); void ifree P((char * pointer)); -char * scheck P((const char *string, const char *format)); - +char * scheck P((const char *string, char *format)); /* ** Finally, some convenience items. @@ -254,6 +255,15 @@ char * scheck P((const char *string, const char *format)); #define TYPE_SIGNED(type) (((type) -1) < 0) #endif /* !defined TYPE_SIGNED */ +/* +** Since the definition of TYPE_INTEGRAL contains floating point numbers, +** it cannot be used in preprocessor directives. +*/ + +#ifndef TYPE_INTEGRAL +#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) +#endif /* !defined TYPE_INTEGRAL */ + #ifndef INT_STRLEN_MAXIMUM /* ** 302 / 1000 is log10(2.0) rounded up. @@ -262,7 +272,8 @@ char * scheck P((const char *string, const char *format)); ** add one more for a minus sign if the type is signed. */ #define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) #endif /* !defined INT_STRLEN_MAXIMUM */ /* @@ -296,11 +307,11 @@ char * scheck P((const char *string, const char *format)); */ #ifndef _ -#if HAVE_GETTEXT - 0 +#if HAVE_GETTEXT #define _(msgid) gettext(msgid) -#else /* !(HAVE_GETTEXT - 0) */ +#else /* !HAVE_GETTEXT */ #define _(msgid) msgid -#endif /* !(HAVE_GETTEXT - 0) */ +#endif /* !HAVE_GETTEXT */ #endif /* !defined _ */ #ifndef TZ_DOMAIN diff --git a/lib/libc/time/scheck.c b/lib/libc/time/scheck.c index ad3db5b38d9..9f44c0724da 100644 --- a/lib/libc/time/scheck.c +++ b/lib/libc/time/scheck.c @@ -4,8 +4,8 @@ */ #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)scheck.c 8.15"; -static char rcsid[] = "$OpenBSD: scheck.c,v 1.6 2003/02/14 18:24:53 millert Exp $"; +static char elsieid[] = "@(#)scheck.c 8.16"; +static char rcsid[] = "$OpenBSD: scheck.c,v 1.7 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ /*LINTLIBRARY*/ @@ -15,7 +15,7 @@ static char rcsid[] = "$OpenBSD: scheck.c,v 1.6 2003/02/14 18:24:53 millert Exp char * scheck(string, format) const char * const string; -const char * const format; +char * const format; { register char * fbuf; register const char * fp; diff --git a/lib/libc/time/strftime.c b/lib/libc/time/strftime.c index 8246302c69d..cfa345569f9 100644 --- a/lib/libc/time/strftime.c +++ b/lib/libc/time/strftime.c @@ -1,12 +1,6 @@ -/* -** XXX To do: figure out correct (as distinct from standard-mandated) -** output for "two digits of year" and "century" formats when -** the year is negative or less than 100. --ado, 2004-09-09 -*/ - #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)strftime.c 7.64"; -static char *rcsid = "$OpenBSD: strftime.c,v 1.14 2004/10/18 22:33:43 millert Exp $"; +static char elsieid[] = "@(#)strftime.c 7.75"; +static char *rcsid = "$OpenBSD: strftime.c,v 1.15 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ #include "private.h" @@ -109,7 +103,7 @@ static const struct lc_time_T C_time_locale = { ** C99 requires this format. ** Previously this code used "%D %X", but we now conform to C99. ** Note that - ** "%a %b %d %H:%M:%S %Y" + ** "%a %b %d %H:%M:%S %Y" ** is used by Solaris 2.3. */ "%a %b %e %T %Y", @@ -126,10 +120,9 @@ static const struct lc_time_T C_time_locale = { static char * _add P((const char *, char *, const char *)); static char * _conv P((int, const char *, char *, const char *)); -static char * _lconv P((long, const char *, char *, const char *)); -static char * _fmt P((const char *, const struct tm *, char *, const char *, int *)); - -size_t strftime P((char *, size_t, const char *, const struct tm *)); +static char * _fmt P((const char *, const struct tm *, char *, const char *, + int *)); +static char * _yconv P((int, int, int, int, char *, const char *)); extern char * tzname[]; @@ -137,7 +130,6 @@ extern char * tzname[]; #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" #endif /* !defined YEAR_2000_NAME */ - #define IN_NONE 0 #define IN_SOME 1 #define IN_THIS 2 @@ -232,9 +224,8 @@ label: ** something completely different. ** (ado, 1993-05-24) */ - pt = _conv((int) ((t->tm_year + - (long) TM_YEAR_BASE) / 100), - "%02d", pt, ptlim); + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, + pt, ptlim); continue; case 'c': { @@ -289,7 +280,7 @@ label: ** t->tm_hour % 12 : 12, 2, ' '); ** ...and has been changed to the below to ** match SunOS 4.1.1 and Arnold Robbins' - ** strftime version 3.0. That is, "%k" and + ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. ** (ado, 1993-05-24) */ @@ -309,7 +300,7 @@ label: ** _conv(t->tm_hour, 2, ' '); ** ...and has been changed to the below to ** match SunOS 4.1.1 and Arnold Robbin's - ** strftime version 3.0. That is, "%k" and + ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. ** (ado, 1993-05-24) */ @@ -384,7 +375,7 @@ label: case 'G': /* ISO 8601 year (four digits) */ case 'g': /* ISO 8601 year (two digits) */ /* -** From Arnold Robbins' strftime version 3.0: "the week number of the +** From Arnold Robbins' strftime version 3.0: "the week number of the ** year (the first Monday as the first day of week 1) as a decimal number ** (01-53)." ** (ado, 1993-05-24) @@ -397,18 +388,19 @@ label: ** might also contain days from the previous year and the week before week ** 01 of a year is the last week (52 or 53) of the previous year even if ** it contains days from the new year. A week starts with Monday (day 1) -** and ends with Sunday (day 7). For example, the first week of the year +** and ends with Sunday (day 7). For example, the first week of the year ** 1997 lasts from 1996-12-30 to 1997-01-05..." ** (ado, 1996-01-02) */ { - long year; + int year; + int base; int yday; int wday; int w; year = t->tm_year; - year += TM_YEAR_BASE; + base = TM_YEAR_BASE; yday = t->tm_yday; wday = t->tm_wday; for ( ; ; ) { @@ -416,7 +408,7 @@ label: int bot; int top; - len = isleap(year) ? + len = isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; /* @@ -435,7 +427,7 @@ label: top += DAYSPERWEEK; top += len; if (yday >= top) { - ++year; + ++base; w = 1; break; } @@ -444,8 +436,8 @@ label: DAYSPERWEEK); break; } - --year; - yday += isleap(year) ? + --base; + yday += isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; } @@ -461,9 +453,9 @@ label: pt, ptlim); else if (*format == 'g') { *warnp = IN_ALL; - pt = _conv((int) (year % 100), - "%02d", pt, ptlim); - } else pt = _lconv(year, "%04ld", + pt = _yconv(year, base, 0, 1, + pt, ptlim); + } else pt = _yconv(year, base, 1, 1, pt, ptlim); } continue; @@ -501,13 +493,12 @@ label: continue; case 'y': *warnp = IN_ALL; - pt = _conv((int) ((t->tm_year + - (long) TM_YEAR_BASE) % 100), - "%02d", pt, ptlim); + pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, + pt, ptlim); continue; case 'Y': - pt = _lconv(t->tm_year + (long) TM_YEAR_BASE, - "%04ld", pt, ptlim); + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, + pt, ptlim); continue; case 'Z': #ifdef TM_ZONE @@ -537,12 +528,12 @@ label: /* ** C99 says that the UTC offset must ** be computed by looking only at - ** tm_isdst. This requirement is + ** tm_isdst. This requirement is ** incorrect, since it means the code ** must rely on magic (in this case ** altzone and timezone), and the ** magic might not have the correct - ** offset. Doing things correctly is + ** offset. Doing things correctly is ** tricky and requires disobeying C99; ** see GNU C strftime for details. ** For now, punt and conform to the @@ -571,9 +562,10 @@ label: diff = -diff; } else sign = "+"; pt = _add(sign, pt, ptlim); - diff /= 60; - pt = _conv((diff/60)*100 + diff%60, - "%04d", pt, ptlim); + diff /= SECSPERMIN; + diff = (diff / MINSPERHOUR) * 100 + + (diff % MINSPERHOUR); + pt = _conv(diff, "%04d", pt, ptlim); } continue; case '+': @@ -583,7 +575,7 @@ label: case '%': /* ** X311J/88-090 (4.12.3.5): if conversion char is - ** undefined, behavior is undefined. Print out the + ** undefined, behavior is undefined. Print out the ** character itself as printf(3) also does. */ default: @@ -611,26 +603,54 @@ const char * const ptlim; } static char * -_lconv(n, format, pt, ptlim) -const long n; -const char * const format; -char * const pt; +_add(str, pt, ptlim) +const char * str; +char * pt; const char * const ptlim; { - char buf[INT_STRLEN_MAXIMUM(long) + 1]; - - (void) snprintf(buf, sizeof buf, format, n); - return _add(buf, pt, ptlim); + while (pt < ptlim && (*pt = *str++) != '\0') + ++pt; + return pt; } +/* +** POSIX and the C Standard are unclear or inconsistent about +** what %C and %y do if the year is negative or exceeds 9999. +** Use the convention that %C concatenated with %y yields the +** same output as %Y, and that %Y contains at least 4 bytes, +** with more only if necessary. +*/ + static char * -_add(str, pt, ptlim) -const char * str; +_yconv(a, b, convert_top, convert_yy, pt, ptlim) +const int a; +const int b; +const int convert_top; +const int convert_yy; char * pt; const char * const ptlim; { - while (pt < ptlim && (*pt = *str++) != '\0') - ++pt; + register int lead; + register int trail; + +#define DIVISOR 100 + trail = a % DIVISOR + b % DIVISOR; + lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) { + if (lead == 0 && trail < 0) + pt = _add("-0", pt, ptlim); + else pt = _conv(lead, "%02d", pt, ptlim); + } + if (convert_yy) + pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); return pt; } @@ -679,7 +699,7 @@ _loc P((void)) ** Slurp the locale file into the cache. */ namesize = strlen(name) + 1; - if (sizeof filename < + if (sizeof filename < ((sizeof locale_home) + namesize + (sizeof lc_time))) goto no_locale; oldsun = 0; diff --git a/lib/libc/time/tz-art.htm b/lib/libc/time/tz-art.htm index 56f78ace2e0..962246d74c1 100644 --- a/lib/libc/time/tz-art.htm +++ b/lib/libc/time/tz-art.htm @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="US-ASCII"?> -<!DOCTYPE html +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> @@ -10,7 +10,7 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" <body> <h1>Time and the Arts</h1> <address> -@(#)tz-art.htm 7.53 +@(#)tz-art.htm 7.54 </address> <p> Please send corrections to this web page to the diff --git a/lib/libc/time/tz-link.htm b/lib/libc/time/tz-link.htm index e55d1f6a288..76ae60be8b7 100644 --- a/lib/libc/time/tz-link.htm +++ b/lib/libc/time/tz-link.htm @@ -9,7 +9,7 @@ <meta http-equiv="Content-type" content='text/html; charset="US-ASCII"' /> <meta name="DC.Creator" content="Eggert, Paul" /> <meta name="DC.Contributor" content="Olson, Arthur David" /> -<meta name="DC.Date" content="2004-09-22" /> +<meta name="DC.Date" content="2005-03-11" /> <meta name="DC.Description" content="Sources of information about time zones and daylight saving time" /> <meta name="DC.Identifier" content="http://www.twinsun.com/tz/tz-link.htm" /> @@ -19,7 +19,7 @@ <body> <h1>Sources for Time Zone and Daylight Saving Time Data</h1> <address> -@(#)tz-link.htm 7.44 +@(#)tz-link.htm 7.48 </address> <p> Please send corrections to this web page to the @@ -36,9 +36,9 @@ This database (often called <code>tz</code> or <code>zoneinfo</code>) is used by several implementations, including <a href="http://www.gnu.org/software/libc/">the -<acronym title="GNU's Not Unix">GNU</acronym> +<abbr title="GNU's Not Unix">GNU</abbr> C Library</a> used in -<a href="http://www.linux.org/"><acronym>GNU</acronym>/Linux</a>, +<a href="http://www.linux.org/"><abbr>GNU</abbr>/Linux</a>, <a href="http://www.freebsd.org/">FreeBSD</a>, <a href="http://www.netbsd.org/">NetBSD</a>, <a href="http://www.openbsd.org/">OpenBSD</a>, @@ -80,7 +80,7 @@ where <code><var>C</var></code> is the code's version; similarly, the data are in <code>tzdata<var>D</var>.tar.gz</code>, where <code><var>D</var></code> is the data's version. The following shell commands download -these files to a <acronym>GNU</acronym>/Linux or similar host; +these files to a <abbr>GNU</abbr>/Linux or similar host; see the downloaded <code>README</code> file for what to do next.</p> <pre style="margin-left: 2em"><code><a href="http://www.gnu.org/software/wget/">wget</a> 'ftp://elsie.nci.nih.gov/pub/tz*.tar.gz' @@ -136,7 +136,9 @@ title="Internet Engineering Task Force">IETF</abbr> Calendaring and Scheduling Working Group (<abbr title="Calendaring and Scheduling Working Group">calsch</abbr>)</a> covers time zone -data; see its VTIMEZONE calendar component.</li> +data; see its VTIMEZONE calendar component. +The <a href='http://www.calconnect.org/'>Calendaring and Scheduling +Consortium</a> is promoting further work in this area.</li> <li>The <a href="http://lists.w3.org/Archives/Public/www-rdf-calendar/">www-rdf-calendar</a> list discusses <a @@ -157,12 +159,12 @@ definition that corresponded to iCalendar.</li> </ul> <h2>Other <code>tz</code> compilers</h2> <ul> -<li><a href="http://www.dachaplin.dsl.pipex.com/vzic">Vzic iCalendar +<li><a href="http://dialspace.dial.pipex.com/prod/dialspace/town/pipexdsl/s/asbm26/vzic/">Vzic iCalendar Timezone Converter</a> describes a program Vzic that compiles <code>tz</code> source into iCalendar-compatible VTIMEZONE files. Vzic is freely available under the <a -href="http://www.gnu.org/copyleft/gpl.html"><acronym>GNU</acronym> +href="http://www.gnu.org/copyleft/gpl.html"><abbr>GNU</abbr> General Public License (<abbr title="General Public License">GPL</abbr>)</a>.</li> <li><a @@ -178,7 +180,7 @@ transition in the <code>tz</code> database.</li> <li><a href="http://oss.software.ibm.com/icu/"><abbr title="International Components for Unicode">ICU</abbr></a> contains a C/C++ library for internationalization that -has a compiler from <samp>tz</samp> source +has a compiler from <code>tz</code> source into an <abbr>ICU</abbr>-specific format. <abbr>ICU</abbr> is freely available under a <abbr title="Berkeley Software Distribution">BSD</abbr>-style license.</li> @@ -196,18 +198,18 @@ It is freely available under a <abbr>BSD</abbr>-style license.</li> <h2>Other <code>tz</code> binary file readers</h2> <ul> <li>The <a -href="http://www.gnu.org/software/libc/"><acronym>GNU</acronym> C +href="http://www.gnu.org/software/libc/"><abbr>GNU</abbr> C Library</a> has an independent, thread-safe implementation of a <code>tz</code> binary file reader. This library is freely available under the <a href="http://www.gnu.org/copyleft/lesser.html"> -<acronym>GNU</acronym> Lesser General Public License +<abbr>GNU</abbr> Lesser General Public License (<abbr title="Lesser General Public License">LGPL</abbr>)</a>, -and is widely used in <acronym>GNU</acronym>/Linux systems.</li> +and is widely used in <abbr>GNU</abbr>/Linux systems.</li> <li><a href="http://www.bmsi.com/java/#TZ">ZoneInfo.java</a> is a <code>tz</code> binary file reader written in Java. -It is freely available under the <acronym>GNU</acronym> +It is freely available under the <abbr>GNU</abbr> <abbr>LGPL</abbr>.</li> <li><a href="http://s.keim.free.fr/tz/doc.html">Python time zones</a> is a <code>tz</code> binary file reader written in Python. @@ -216,12 +218,17 @@ It is freely available under a <abbr>BSD</abbr>-style license.</li> <h2>Other <code>tz</code>-based time zone conversion software</h2> <ul> <li><a href="http://java.sun.com/">Sun Java</a> releases since 1.4 -contain a copy of a subset of a recent <samp>tz</samp> database in a +contain a copy of a subset of a recent <code>tz</code> database in a Java-specific format.</li> <li><a href="http://www1.tip.nl/~t876506/AboutTimeZonesHC.html">HyperCard time zones calculator</a> is a HyperCard stack.</li> <li><a +href="http://www.veladg.com/velaterra.html">VelaTerra</a> is +a Mac OS X program. Its developers +<a href="http://www.veladg.com/tzoffer.html">offer free +licenses</a> to <code>tz</code> contributors.</li> +<li><a href="http://www.cimmyt.org/timezone/">World Time Explorer</a> is a Microsoft Windows program.</li> </ul> @@ -243,10 +250,7 @@ is another time zone database.</li> contains data from the Time Service Department of the <abbr>US</abbr> Naval Observatory, used as the source for the <code>usno*</code> files in the <code>tz</code> distribution.</li> -<li><a href="http://www.airportcitycodes.com/aaa/">Airlines, Airplanes -and Airports</a> lists current standard times for thousands of -airports around the world. This seems to be derived from -the <a href="http://www.iata.org/ps/publications/9179.htm">Standard +<li>The <a href="http://www.iata.org/ps/publications/9179.htm">Standard Schedules Information Manual</a> of the the <a href="http://www.iata.org/">International Air Transport Association</a>, @@ -285,7 +289,7 @@ zone boundaries for multizone countries</a> summarizes legal boundaries between time zones within countries.</li> <li>Manifold.net's <a href="http://www.manifold.net/download/freemaps.html">Free Maps and -<acronym title="Geographic Information Systems">GIS</acronym> +<abbr title="Geographic Information Systems">GIS</abbr> Data</a> includes a Manifold-format map of world time zone boundaries distributed under the <abbr>GPL</abbr>.</li> @@ -299,11 +303,15 @@ Time Zones and Time Zone Data</a>.</li> </ul> <h2>Civil time concepts and history</h2> <ul> -<li><a href="http://physics.nist.gov/time">A Walk through Time</a> +<li><a href="http://physics.nist.gov/GenInt/Time/time.html">A +Walk through Time</a> surveys the evolution of timekeeping.</li> <li><a href="http://webexhibits.org/daylightsaving/">About Daylight Saving Time - History, rationale, laws & dates</a> is an overall history of <abbr>DST</abbr>.</li> +<li><a href="http://www.seizethedaylight.com/dst/">Who Knew? A Brief +History of Daylight Saving Time</a> summarizes some of the contentious +history of <abbr>DST</abbr>.</li> <li><a href="http://toi.iriti.cnr.it/">The Time of Internet</a> describes time zones and daylight saving time, @@ -410,8 +418,8 @@ Times</a> explains more abstruse astronomical time scales like <abbr title="Barycentric Dynamic Time">TDB</abbr>.</li> <li>The <a href="http://www.iau.org/"><abbr title="International Astronomical Union">IAU</abbr></a>'s <a -href="http://www.iau-sofa.rl.ac.uk/"><acronym -title="Standards Of Fundamental Astronomy">SOFA</acronym></a> +href="http://www.iau-sofa.rl.ac.uk/"><abbr +title="Standards Of Fundamental Astronomy">SOFA</abbr></a> initiative publishes Fortran code for converting among time scales like <abbr title="International Atomic Time">TAI</abbr>, @@ -447,7 +455,7 @@ title="Global Positioning System">GPS</abbr> World</a> (1999-11), 50–57 and discussed further in R. A. Nelson et al., <a href="http://www.cl.cam.ac.uk/~mgk25/time/metrologia-leapsecond.pdf">The leap second: its history and possible future</a>, -<a href="http://www.bipm.fr/metrologia/metrologia.html">Metrologia</a> +<a href="http://www.bipm.fr/metrologia/">Metrologia</a> <strong>38</strong> (2001), 509–529. <a href="http://www.ucolick.org/~sla/leapsecs/onlinebib.html">The Future of Leap Seconds</a> catalogs information about this @@ -459,12 +467,11 @@ contentious issue.</li> <a href="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">A Summary of the International Standard Date and Time Notation</a> is a good summary of -<abbr title="International Organization for Standardization">ISO</abbr> -8601:1988 - Data elements and interchange formats - Information interchange -- Representation of dates and times (which has been superseded by <a -href="http://www.iso.org/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=26780"><abbr>ISO</abbr> -8601:2000</a>).</li> +href="http://www.iso.org/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=40874"><abbr +title="International Organization for Standardization">ISO</abbr> +8601:2004 -- Data elements and interchange formats -- Information +interchange -- Representation of dates and times</a>.</li> <li> Section 3.3 of <a href="ftp://ftp.rfc-editor.org/in-notes/rfc2822.txt">Internet @@ -482,13 +489,21 @@ protocols.</li> Best of Dates, the Worst of Dates</a> covers many problems encountered by software developers when handling dates and time stamps.</li> <li><a -href="http://oss.software.ibm.com/cvs/icu/~checkout~/icuhtml/design/formatting/time_zone_localization.html">Time -Zone Localization</a> is a proposed mechanism for localizing time zone -labels and abbreviations; for example, one might use it to specify +href="http://ibm.com/software/globalization/icu"><abbr +title="International Components for Unicode">ICU</abbr></a> contains a +mechanism for localizing time zone +labels and abbreviations; for example, one can use it to specify Russian translations for "Eastern European Summer Time", "<abbr title="Eastern European Summer Time">EEST</abbr>", -and <code>Europe/Bucharest</code>.</li> -<li> Alphabetic time zone abbreviations should not be used as unique +and <code>Europe/Bucharest</code>. +This mechanism is part of the +<a href="http://unicode.org/cldr/">Unicode +<abbr title="Common Locale Data Repository">CLDR</abbr> Project</a>; +for example, the <a +href="http://www.unicode.org/cldr/data/diff/by_type/dates_timeZoneNames.html">By-Type +Chart for //ldml/dates/timeZoneNames/…</a> +shows values for time zone names in many locales.</li> +<li>Alphabetic time zone abbreviations should not be used as unique identifiers for <abbr>UTC</abbr> offsets as they are ambiguous in practice. For example, "<abbr>EST</abbr>" denotes 5 hours behind <abbr>UTC</abbr> in English-speaking North America, but it denotes 10 @@ -499,6 +514,21 @@ French-speaking North Americans prefer database contains English abbreviations for all time stamps but in many cases these are merely inventions of the database maintainers.</li> +<li>Numeric time zone abbreviations typically count hours east of +<abbr>UTC</abbr>, e.g., <code>+09</code> for Japan and +<code>-10</code> for Hawaii. However, the <abbr>POSIX</abbr> +<code>TZ</code> environment variable uses the opposite convention. For +example, one might use <code>TZ="JST-9"</code> and +<code>TZ="HST10"</code> for Japan and Hawaii, respectively. If the +<code>tz</code> database is available, it is usually better to use +settings like <code>TZ="Asia/Tokyo"</code> and +<code>TZ="Pacific/Honolulu"</code> instead, as this should avoid +confusion, handle old timestamps better, and insulate you better from +any future changes to the rules. One should never set +<abbr>POSIX</abbr> <code>TZ</code> to a value like +<code>"GMT-9"</code>, though, since this would falsely claim that +local time is nine hours ahead of <abbr>UTC</abbr> and the time zone +is called "<abbr>GMT</abbr>".</li> </ul> <h2>Related indexes</h2> <ul> diff --git a/lib/libc/time/tzfile.h b/lib/libc/time/tzfile.h index 40331f46bc6..96d7d05a3ff 100644 --- a/lib/libc/time/tzfile.h +++ b/lib/libc/time/tzfile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tzfile.h,v 1.5 1998/01/18 23:25:00 millert Exp $ */ +/* $OpenBSD: tzfile.h,v 1.6 2005/07/05 13:40:51 millert Exp $ */ #ifndef TZFILE_H @@ -24,7 +24,7 @@ #if 0 #ifndef lint #ifndef NOID -static char tzfilehid[] = "@(#)tzfile.h 7.14"; +static char tzfilehid[] = "@(#)tzfile.h 7.17"; #endif /* !defined NOID */ #endif /* !defined lint */ #endif @@ -160,33 +160,20 @@ struct tzhead { #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY -/* -** Accurate only for the past couple of centuries; -** that will probably do. -*/ - #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) -#ifndef USG - /* -** Use of the underscored variants may cause problems if you move your code to -** certain System-V-based systems; for maximum portability, use the -** underscore-free variants. The underscored variants are provided for -** backward compatibility only; they may disappear from future versions of -** this file. +** Since everything in isleap is modulo 400 (or a factor of 400), we know that +** isleap(y) == isleap(y % 400) +** and so +** isleap(a + b) == isleap((a + b) % 400) +** or +** isleap(a + b) == isleap(a % 400 + b % 400) +** This is true even if % means modulo rather than Fortran remainder +** (which is allowed by C89 but not C99). +** We use this to avoid addition overflow problems. */ -#define SECS_PER_MIN SECSPERMIN -#define MINS_PER_HOUR MINSPERHOUR -#define HOURS_PER_DAY HOURSPERDAY -#define DAYS_PER_WEEK DAYSPERWEEK -#define DAYS_PER_NYEAR DAYSPERNYEAR -#define DAYS_PER_LYEAR DAYSPERLYEAR -#define SECS_PER_HOUR SECSPERHOUR -#define SECS_PER_DAY SECSPERDAY -#define MONS_PER_YEAR MONSPERYEAR - -#endif /* !defined USG */ +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #endif /* !defined TZFILE_H */ diff --git a/lib/libc/time/zdump.8 b/lib/libc/time/zdump.8 index 6eaa952075a..8819f8aa1da 100644 --- a/lib/libc/time/zdump.8 +++ b/lib/libc/time/zdump.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: zdump.8,v 1.9 2004/04/01 11:37:02 jmc Exp $ +.\" $OpenBSD: zdump.8,v 1.10 2005/07/05 13:40:51 millert Exp $ .Dd May 23, 1999 .Dt ZDUMP 8 .Os @@ -8,7 +8,13 @@ .Sh SYNOPSIS .Nm zdump .Op Fl v -.Op Fl c Ar cutoffyear +.Sm off +.Oo +.Fl c No \ \& +.Op Ar loyear, +.Ar hiyear +.Oc +.Sm on .Ar zonename Ar ... .Sh DESCRIPTION .Nm @@ -18,8 +24,10 @@ named on the command line. .Pp These options are available: .Bl -tag -width Fl -.It Fl c Ar cutoffyear -Cut off the verbose output near the start of the given year. +.It Fl c Ar Op loyear, Ns hiyear +Cut off verbose output near the start of the given year(s). +By default, +the program cuts off verbose output near the starts of the years -500 and 2500. .It Fl v For each .Ar zonename @@ -36,10 +44,21 @@ if the given time is Daylight Saving Time or .Dq isdst=0 otherwise. .El +.Sh LIMITATIONS +The +.Fl v +option may not be used on systems with floating-point +.Li time_t +values that are neither float nor double. +.Pp +Time discontinuities are found by sampling the results returned by localtime +at twelve-hour intervals. +This works in all real-world cases; +one can construct artificial time zones for which this fails. .Sh SEE ALSO .Xr ctime 3 , .Xr tzfile 5 , .Xr zic 8 -.\" @(#)zdump.8 7.4 +.\" @(#)zdump.8 7.7 .\" This file is in the public domain, so clarified as of .\" 2003-02-14 by Arthur David Olson (arthur_david_olson@nih.gov). diff --git a/lib/libc/time/zdump.c b/lib/libc/time/zdump.c index 77ba168f464..351dc65fa63 100644 --- a/lib/libc/time/zdump.c +++ b/lib/libc/time/zdump.c @@ -4,9 +4,8 @@ */ #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)zdump.c 7.31"; -static char rcsid[] = "$OpenBSD: zdump.c,v 1.15 2004/10/19 05:01:01 deraadt Exp $"; -static char elsieid[] = "@(#)zdump.c 7.40"; +static char elsieid[] = "@(#)zdump.c 7.65"; +static char rcsid[] = "$OpenBSD: zdump.c,v 1.16 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ /* @@ -17,9 +16,19 @@ static char elsieid[] = "@(#)zdump.c 7.40"; #include "stdio.h" /* for stdout, stderr, perror */ #include "string.h" /* for strlcpy */ +#include "ctype.h" /* for isascii, isalpha, isdigit */ #include "sys/types.h" /* for time_t */ #include "time.h" /* for struct tm */ #include "stdlib.h" /* for exit, malloc, atoi */ +#include "float.h" /* for FLT_MAX and DBL_MAX */ + +#ifndef ZDUMP_LO_YEAR +#define ZDUMP_LO_YEAR (-500) +#endif /* !defined ZDUMP_LO_YEAR */ + +#ifndef ZDUMP_HI_YEAR +#define ZDUMP_HI_YEAR 2500 +#endif /* !defined ZDUMP_HI_YEAR */ #ifndef MAX_STRING_LENGTH #define MAX_STRING_LENGTH 1024 @@ -70,9 +79,20 @@ static char elsieid[] = "@(#)zdump.c 7.40"; #endif /* !defined DAYSPERNYEAR */ #ifndef isleap -#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) #endif /* !defined isleap */ +#ifndef isleap_sum +/* +** See tzfile.h for details on isleap_sum. +*/ +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) +#endif /* !defined isleap_sum */ + +#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) +#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) +#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) + #if HAVE_GETTEXT #include "locale.h" /* for setlocale */ #include "libintl.h" @@ -124,18 +144,100 @@ static char elsieid[] = "@(#)zdump.c 7.40"; extern char ** environ; extern int getopt P((int argc, char * const argv[], - const char * options)); + const char * options)); extern char * optarg; extern int optind; extern char * tzname[2]; +static time_t absolute_min_time; +static time_t absolute_max_time; +static size_t longest; +static char * progname; +static int warned; + static char * abbr P((struct tm * tmp)); +static void abbrok P((const char * abbr, const char * zone)); static long delta P((struct tm * newp, struct tm * oldp)); +static void dumptime P((const struct tm * tmp)); static time_t hunt P((char * name, time_t lot, time_t hit)); -static size_t longest; -static char * progname; +static void setabsolutes P((void)); static void show P((char * zone, time_t t, int v)); -static void dumptime P((const struct tm * tmp)); +static const char * tformat P((void)); +static time_t yeartot P((long y)); + +#ifndef TYPECHECK +#define my_localtime localtime +#else /* !defined TYPECHECK */ +static struct tm * +my_localtime(tp) +time_t * tp; +{ + register struct tm * tmp; + + tmp = localtime(tp); + if (tp != NULL && tmp != NULL) { + struct tm tm; + register time_t t; + + tm = *tmp; + t = mktime(&tm); + if (t - *tp >= 1 || *tp - t >= 1) { + (void) fflush(stdout); + (void) fprintf(stderr, "\n%s: ", progname); + (void) fprintf(stderr, tformat(), *tp); + (void) fprintf(stderr, " ->"); + (void) fprintf(stderr, " year=%d", tmp->tm_year); + (void) fprintf(stderr, " mon=%d", tmp->tm_mon); + (void) fprintf(stderr, " mday=%d", tmp->tm_mday); + (void) fprintf(stderr, " hour=%d", tmp->tm_hour); + (void) fprintf(stderr, " min=%d", tmp->tm_min); + (void) fprintf(stderr, " sec=%d", tmp->tm_sec); + (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst); + (void) fprintf(stderr, " -> "); + (void) fprintf(stderr, tformat(), t); + (void) fprintf(stderr, "\n"); + } + } + return tmp; +} +#endif /* !defined TYPECHECK */ + +static void +abbrok(abbr, zone) +const char * const abbr; +const char * const zone; +{ + register const char * cp; + register char * wp; + + if (warned) + return; + cp = abbr; + wp = NULL; + while (isascii(*cp) && isalpha(*cp)) + ++cp; + if (cp - abbr == 0) + wp = _("lacks alphabetic at start"); + if (cp - abbr < 3) + wp = _("has fewer than 3 alphabetics"); + if (cp - abbr > 6) + wp = _("has more than 6 alphabetics"); + if (wp == NULL && (*cp == '+' || *cp == '-')) { + ++cp; + if (isascii(*cp) && isdigit(*cp)) + if (*cp++ == '1' && *cp >= '0' && *cp <= '4') + ++cp; + } + if (*cp != '\0') + wp = _("differs from POSIX standard"); + if (wp == NULL) + return; + (void) fflush(stdout); + (void) fprintf(stderr, + "%s: warning: zone \"%s\" abbreviation \"%s\" %s\n", + progname, zone, abbr, wp); + warned = TRUE; +} int main(argc, argv) @@ -145,20 +247,24 @@ char * argv[]; register int i; register int c; register int vflag; - register char * cutoff; - register int cutyear; - register long cuttime; - char ** fakeenv; + register char * cutarg; + register long cutloyear = ZDUMP_LO_YEAR; + register long cuthiyear = ZDUMP_HI_YEAR; + register time_t cutlotime; + register time_t cuthitime; + register char ** fakeenv; time_t now; time_t t; time_t newt; - time_t hibit; struct tm tm; struct tm newtm; + register struct tm * tmp; + register struct tm * newtmp; - INITIALIZE(cuttime); + INITIALIZE(cutlotime); + INITIALIZE(cuthitime); #if HAVE_GETTEXT - (void) setlocale(LC_MESSAGES, ""); + (void) setlocale(LC_ALL, ""); #ifdef TZ_DOMAINDIR (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); #endif /* defined TEXTDOMAINDIR */ @@ -166,39 +272,50 @@ char * argv[]; #endif /* HAVE_GETTEXT */ progname = argv[0]; vflag = 0; - cutoff = NULL; + cutarg = NULL; while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') if (c == 'v') vflag = 1; - else cutoff = optarg; + else cutarg = optarg; if ((c != EOF && c != -1) || (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { (void) fprintf(stderr, -_("%s: usage is %s [-v] [-c cutoffyear] zonename ...\n"), - argv[0], argv[0]); +_("usage %s [-v] [-c [loyear,]hiyear] zonename ...\n"), + progname); (void) exit(EXIT_FAILURE); } - if (cutoff != NULL) { - int y; - - cutyear = atoi(cutoff); - cuttime = 0; - for (y = EPOCH_YEAR; y < cutyear; ++y) - cuttime += DAYSPERNYEAR + isleap(y); - cuttime *= SECSPERHOUR * HOURSPERDAY; + if (vflag) { + if (cutarg != NULL) { + long lo; + long hi; + char dummy; + + if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) { + cuthiyear = hi; + } else if (sscanf(cutarg, "%ld,%ld%c", + &lo, &hi, &dummy) == 2) { + cutloyear = lo; + cuthiyear = hi; + } else { +(void) fprintf(stderr, _("%s: wild -c argument %s\n"), + progname, cutarg); + (void) exit(EXIT_FAILURE); + } + } + setabsolutes(); + cutlotime = yeartot(cutloyear); + cuthitime = yeartot(cuthiyear); } (void) time(&now); longest = 0; for (i = optind; i < argc; ++i) if (strlen(argv[i]) > longest) longest = strlen(argv[i]); - for (hibit = 1; (hibit << 1) != 0; hibit <<= 1) - continue; { register int from; register int to; - for (i = 0; environ[i] != NULL; ++i) + for (i = 0; environ[i] != NULL; ++i) continue; fakeenv = (char **) malloc((size_t) ((i + 2) * sizeof *fakeenv)); @@ -223,58 +340,129 @@ _("%s: usage is %s [-v] [-c cutoffyear] zonename ...\n"), show(argv[i], now, FALSE); continue; } - /* - ** Get lowest value of t. - */ - t = hibit; - if (t > 0) /* time_t is unsigned */ - t = 0; + warned = FALSE; + t = absolute_min_time; show(argv[i], t, TRUE); t += SECSPERHOUR * HOURSPERDAY; show(argv[i], t, TRUE); - tm = *localtime(&t); - strlcpy(buf, abbr(&tm), (sizeof buf)); + if (t < cutlotime) + t = cutlotime; + tmp = my_localtime(&t); + if (tmp != NULL) { + tm = *tmp; + strlcpy(buf, abbr(&tm), sizeof buf); + } for ( ; ; ) { - if (cutoff != NULL && t >= cuttime) + if (t >= cuthitime) break; newt = t + SECSPERHOUR * 12; - if (cutoff != NULL && newt >= cuttime) + if (newt >= cuthitime) break; if (newt <= t) break; - newtm = *localtime(&newt); - if (delta(&newtm, &tm) != (newt - t) || + newtmp = localtime(&newt); + if (newtmp != NULL) + newtm = *newtmp; + if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : + (delta(&newtm, &tm) != (newt - t) || newtm.tm_isdst != tm.tm_isdst || - strcmp(abbr(&newtm), buf) != 0) { + strcmp(abbr(&newtm), buf) != 0)) { newt = hunt(argv[i], t, newt); - newtm = *localtime(&newt); - strlcpy(buf, abbr(&newtm), - (sizeof buf)); + newtmp = localtime(&newt); + if (newtmp != NULL) { + newtm = *newtmp; + strlcpy(buf, abbr(&newtm), + sizeof buf); + } } t = newt; tm = newtm; + tmp = newtmp; } - /* - ** Get highest value of t. - */ - t = ~((time_t) 0); - if (t < 0) /* time_t is signed */ - t &= ~hibit; + t = absolute_max_time; t -= SECSPERHOUR * HOURSPERDAY; show(argv[i], t, TRUE); t += SECSPERHOUR * HOURSPERDAY; show(argv[i], t, TRUE); } if (fflush(stdout) || ferror(stdout)) { - (void) fprintf(stderr, "%s: ", argv[0]); + (void) fprintf(stderr, "%s: ", progname); (void) perror(_("Error writing standard output")); (void) exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); + /* If exit fails to exit... */ + return EXIT_FAILURE; +} - /* gcc -Wall pacifier */ - for ( ; ; ) - continue; +static void +setabsolutes() +{ + if (0.5 == (time_t) 0.5) { + /* + ** time_t is floating. + */ + if (sizeof (time_t) == sizeof (float)) { + absolute_min_time = (time_t) -FLT_MAX; + absolute_max_time = (time_t) FLT_MAX; + } else if (sizeof (time_t) == sizeof (double)) { + absolute_min_time = (time_t) -DBL_MAX; + absolute_max_time = (time_t) DBL_MAX; + } else { + (void) fprintf(stderr, +_("%s: use of -v on system with floating time_t other than float or double\n"), + progname); + (void) exit(EXIT_FAILURE); + } + } else if (0 > (time_t) -1) { + /* + ** time_t is signed. + */ + register time_t hibit; + + for (hibit = 1; (hibit * 2) != 0; hibit *= 2) + continue; + absolute_min_time = hibit; + absolute_max_time = -(hibit + 1); + } else { + /* + ** time_t is unsigned. + */ + absolute_min_time = 0; + absolute_max_time = absolute_min_time - 1; + } +} + +static time_t +yeartot(y) +const long y; +{ + register long myy; + register long seconds; + register time_t t; + + myy = EPOCH_YEAR; + t = 0; + while (myy != y) { + if (myy < y) { + seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; + ++myy; + if (t > absolute_max_time - seconds) { + t = absolute_max_time; + break; + } + t += seconds; + } else { + --myy; + seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; + if (t < absolute_min_time + seconds) { + t = absolute_min_time; + break; + } + t -= seconds; + } + } + return t; } static time_t @@ -283,25 +471,39 @@ char * name; time_t lot; time_t hit; { - time_t t; - struct tm lotm; - struct tm tm; - static char loab[MAX_STRING_LENGTH]; - - lotm = *localtime(&lot); - strlcpy(loab, abbr(&lotm), (sizeof loab)); - while ((hit - lot) >= 2) { - t = lot / 2 + hit / 2; + time_t t; + long diff; + struct tm lotm; + register struct tm * lotmp; + struct tm tm; + register struct tm * tmp; + char loab[MAX_STRING_LENGTH]; + + lotmp = my_localtime(&lot); + if (lotmp != NULL) { + lotm = *lotmp; + (void) strlcpy(loab, abbr(&lotm), sizeof loab); + } + for ( ; ; ) { + diff = (long) (hit - lot); + if (diff < 2) + break; + t = lot; + t += diff / 2; if (t <= lot) ++t; else if (t >= hit) --t; - tm = *localtime(&t); - if (delta(&tm, &lotm) == (t - lot) && + tmp = my_localtime(&t); + if (tmp != NULL) + tm = *tmp; + if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : + (delta(&tm, &lotm) == (t - lot) && tm.tm_isdst == lotm.tm_isdst && - strcmp(abbr(&tm), loab) == 0) { + strcmp(abbr(&tm), loab) == 0)) { lot = t; lotm = tm; + lotmp = tmp; } else hit = t; } show(name, lot, TRUE); @@ -318,14 +520,14 @@ delta(newp, oldp) struct tm * newp; struct tm * oldp; { - long result; - int tmy; + register long result; + register int tmy; if (newp->tm_year < oldp->tm_year) return -delta(oldp, newp); result = 0; for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) - result += DAYSPERNYEAR + isleap(tmy + (long) TM_YEAR_BASE); + result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); result += newp->tm_yday - oldp->tm_yday; result *= HOURSPERDAY; result += newp->tm_hour - oldp->tm_hour; @@ -342,24 +544,34 @@ char * zone; time_t t; int v; { - struct tm * tmp; + register struct tm * tmp; (void) printf("%-*s ", (int) longest, zone); if (v) { - dumptime(gmtime(&t)); - (void) printf(" UTC = "); + tmp = gmtime(&t); + if (tmp == NULL) { + (void) printf(tformat(), t); + } else { + dumptime(tmp); + (void) printf(" UTC"); + } + (void) printf(" = "); } - tmp = localtime(&t); + tmp = my_localtime(&t); dumptime(tmp); - if (*abbr(tmp) != '\0') - (void) printf(" %s", abbr(tmp)); - if (v) { - (void) printf(" isdst=%d", tmp->tm_isdst); + if (tmp != NULL) { + if (*abbr(tmp) != '\0') + (void) printf(" %s", abbr(tmp)); + if (v) { + (void) printf(" isdst=%d", tmp->tm_isdst); #ifdef TM_GMTOFF - (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); + (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF); #endif /* defined TM_GMTOFF */ + } } (void) printf("\n"); + if (tmp != NULL && *abbr(tmp) != '\0') + abbrok(abbr(tmp), zone); } static char * @@ -375,6 +587,33 @@ struct tm * tmp; return (result == NULL) ? &nada : result; } +/* +** The code below can fail on certain theoretical systems; +** it works on all known real-world systems as of 2004-12-30. +*/ + +static const char * +tformat() +{ + if (0.5 == (time_t) 0.5) { /* floating */ + if (sizeof (time_t) > sizeof (double)) + return "%Lg"; + return "%g"; + } + if (0 > (time_t) -1) { /* signed */ + if (sizeof (time_t) > sizeof (long)) + return "%lld"; + if (sizeof (time_t) > sizeof (int)) + return "%ld"; + return "%d"; + } + if (sizeof (time_t) > sizeof (unsigned long)) + return "%llu"; + if (sizeof (time_t) > sizeof (unsigned int)) + return "%lu"; + return "%u"; +} + static void dumptime(timeptr) register const struct tm * timeptr; @@ -388,7 +627,13 @@ register const struct tm * timeptr; }; register const char * wn; register const char * mn; + register int lead; + register int trail; + if (timeptr == NULL) { + (void) printf("NULL"); + return; + } /* ** The packaged versions of localtime and gmtime never put out-of-range ** values in tm_wday or tm_mon, but since this code might be compiled @@ -402,9 +647,23 @@ register const struct tm * timeptr; (int) (sizeof mon_name / sizeof mon_name[0])) mn = "???"; else mn = mon_name[timeptr->tm_mon]; - (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d %ld", + (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", wn, mn, timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec, - timeptr->tm_year + (long) TM_YEAR_BASE); + timeptr->tm_min, timeptr->tm_sec); +#define DIVISOR 10 + trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; + lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (lead == 0) + (void) printf("%d", trail); + else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); } diff --git a/lib/libc/time/zic.c b/lib/libc/time/zic.c index b813ecd191a..036534f9edd 100644 --- a/lib/libc/time/zic.c +++ b/lib/libc/time/zic.c @@ -4,14 +4,24 @@ */ #if defined(LIBC_SCCS) && !defined(lint) && !defined(NOID) -static char elsieid[] = "@(#)zic.c 7.116"; -static char rcsid[] = "$OpenBSD: zic.c,v 1.23 2004/06/28 14:47:41 millert Exp $"; +static char elsieid[] = "@(#)zic.c 7.124"; +static char rcsid[] = "$OpenBSD: zic.c,v 1.24 2005/07/05 13:40:51 millert Exp $"; #endif /* LIBC_SCCS and not lint */ +/* +** Regardless of the type of time_t, we do our work using this type. +*/ + +typedef int zic_t; + #include "private.h" #include "locale.h" #include "tzfile.h" +#ifndef ZIC_MAX_ABBR_LEN_WO_WARN +#define ZIC_MAX_ABBR_LEN_WO_WARN 6 +#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ + #if HAVE_SYS_STAT_H #include "sys/stat.h" #endif @@ -23,7 +33,7 @@ static char rcsid[] = "$OpenBSD: zic.c,v 1.23 2004/06/28 14:47:41 millert Exp $" /* ** On some ancient hosts, predicates like `isspace(C)' are defined -** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, +** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, ** which says they are defined only if C == ((unsigned char) C) || C == EOF. ** Neither the C Standard nor Posix require that `isascii' exist. ** For portability, we check both ancient and modern requirements. @@ -58,7 +68,7 @@ struct rule { const char * r_abbrvar; /* variable part of abbreviation */ int r_todo; /* a rule to do (used in outzone) */ - time_t r_temp; /* used in outzone */ + zic_t r_temp; /* used in outzone */ }; /* @@ -84,7 +94,7 @@ struct zone { int z_nrules; struct rule z_untilrule; - time_t z_untiltime; + zic_t z_untiltime; }; extern int getopt P((int argc, char * const argv[], @@ -93,10 +103,10 @@ extern int link P((const char * fromname, const char * toname)); extern char * optarg; extern int optind; -static void addtt P((time_t starttime, int type)); +static void addtt P((zic_t starttime, int type)); static int addtype P((long gmtoff, const char * abbr, int isdst, int ttisstd, int ttisgmt)); -static void leapadd P((time_t t, int positive, int rolling, int count)); +static void leapadd P((zic_t t, int positive, int rolling, int count)); static void adjleap P((void)); static void associate P((void)); static int ciequal P((const char * ap, const char * bp)); @@ -129,30 +139,30 @@ static long oadd P((long t1, long t2)); static void outzone P((const struct zone * zp, int ntzones)); static void puttzcode P((long code, FILE * fp)); static int rcomp P((const void * leftp, const void * rightp)); -static time_t rpytime P((const struct rule * rp, int wantedy)); +static zic_t rpytime P((const struct rule * rp, int wantedy)); static void rulesub P((struct rule * rp, const char * loyearp, const char * hiyearp, const char * typep, const char * monthp, const char * dayp, const char * timep)); static void setboundaries P((void)); -static time_t tadd P((time_t t1, long t2)); +static zic_t tadd P((zic_t t1, long t2)); static void usage P((void)); static void writezone P((const char * name)); static int yearistype P((int year, const char * type)); -#if !(HAVE_STRERROR - 0) +#if !HAVE_STRERROR static char * strerror P((int)); -#endif /* !(HAVE_STRERROR - 0) */ +#endif /* !HAVE_STRERROR */ static int charcnt; static int errors; static const char * filename; static int leapcnt; static int linenum; -static time_t max_time; +static zic_t max_time; static int max_year; static int max_year_representable; -static time_t min_time; +static zic_t min_time; static int min_year; static int min_year_representable; static int noise; @@ -342,7 +352,7 @@ static const int len_years[2] = { }; static struct attype { - time_t at; + zic_t at; unsigned char type; } attypes[TZ_MAX_TIMES]; static long gmtoffs[TZ_MAX_TYPES]; @@ -351,7 +361,7 @@ static unsigned char abbrinds[TZ_MAX_TYPES]; static char ttisstds[TZ_MAX_TYPES]; static char ttisgmts[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS]; -static time_t trans[TZ_MAX_LEAPS]; +static zic_t trans[TZ_MAX_LEAPS]; static long corr[TZ_MAX_LEAPS]; static char roll[TZ_MAX_LEAPS]; @@ -382,7 +392,7 @@ char * const ptr; ** Error handling. */ -#if !(HAVE_STRERROR - 0) +#if !HAVE_STRERROR static char * strerror(errnum) int errnum; @@ -393,7 +403,7 @@ int errnum; return (errnum > 0 && errnum <= sys_nerr) ? sys_errlist[errnum] : _("Unknown system error"); } -#endif /* !(HAVE_STRERROR - 0) */ +#endif /* !HAVE_STRERROR */ static void eats(name, num, rname, rnum) @@ -450,8 +460,8 @@ const char * const string; static void usage P((void)) { - (void) fprintf(stderr, _("%s: usage is %s [-sv] [-d directory] [-L leapsecondfilename] [-l timezone]\n\t[-p timezone] [-y command] [filename ...]\n"), - progname, progname); + (void) fprintf(stderr, _("usage: %s [-sv] [-d directory] [-L leapsecondfilename] [-l timezone]\n\t[-p timezone] [-y command] [filename ...]\n"), + progname); (void) exit(EXIT_FAILURE); } @@ -474,13 +484,13 @@ char * argv[]; #ifdef unix (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); #endif /* defined unix */ -#if HAVE_GETTEXT - 0 - (void) setlocale(LC_MESSAGES, ""); +#if HAVE_GETTEXT + (void) setlocale(LC_ALL, ""); #ifdef TZ_DOMAINDIR (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); #endif /* defined TEXTDOMAINDIR */ (void) textdomain(TZ_DOMAIN); -#endif /* HAVE_GETTEXT - 0 */ +#endif /* HAVE_GETTEXT */ progname = argv[0]; while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != -1) switch (c) { @@ -576,6 +586,11 @@ _("%s: More than one -L option specified\n"), for (i = 0; i < nlinks; ++i) { eat(links[i].l_filename, links[i].l_linenum); dolink(links[i].l_from, links[i].l_to); + if (noise) + for (j = 0; j < nlinks; ++j) + if (strcmp(links[i].l_to, + links[j].l_from) == 0) + warning(_("link to link")); } if (lcltime != NULL) { eat("command line", 1); @@ -623,7 +638,7 @@ const char * const tofile; (void) exit(EXIT_FAILURE); result = link(fromname, toname); -#if (HAVE_SYMLINK - 0) +#if HAVE_SYMLINK if (result != 0 && errno == EXDEV) result = symlink(fromname, toname); #endif @@ -656,25 +671,36 @@ const char * const tofile; */ #define MAX_BITS_IN_FILE 32 -#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE) +#define TIME_T_BITS_IN_FILE ((TYPE_BIT(zic_t) < MAX_BITS_IN_FILE) ? \ + TYPE_BIT(zic_t) : MAX_BITS_IN_FILE) static void setboundaries P((void)) { - if (TYPE_SIGNED(time_t)) { - min_time = ~ (time_t) 0; - min_time <<= TIME_T_BITS_IN_FILE - 1; - max_time = ~ (time_t) 0 - min_time; + register int i; + + if (TYPE_SIGNED(zic_t)) { + min_time = -1; + for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) + min_time *= 2; + max_time = -(min_time + 1); if (sflag) min_time = 0; } else { min_time = 0; max_time = 2 - sflag; - max_time <<= TIME_T_BITS_IN_FILE - 1; + for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) + max_time *= 2; --max_time; } - min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year; - max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year; + { + time_t t; + + t = (time_t) min_time; + min_year = TM_YEAR_BASE + gmtime(&t)->tm_year; + t = (time_t) max_time; + max_year = TM_YEAR_BASE + gmtime(&t)->tm_year; + } min_year_representable = min_year; max_year_representable = max_year; } @@ -773,7 +799,7 @@ associate P((void)) */ eat(zp->z_filename, zp->z_linenum); zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), - TRUE); + TRUE); /* ** Note, though, that if there's no rule, ** a '%s' in the format is a bad thing. @@ -1079,7 +1105,9 @@ const int iscont; zones[nzones - 1].z_untiltime > min_time && zones[nzones - 1].z_untiltime < max_time && zones[nzones - 1].z_untiltime >= z.z_untiltime) { - error(_("Zone continuation line end time is not after end time of previous line")); + error(_( +"Zone continuation line end time is not after end time of previous line" + )); return FALSE; } } @@ -1103,7 +1131,7 @@ const int nfields; register int i, j; int year, month, day; long dayoff, tod; - time_t t; + zic_t t; if (nfields != LEAP_FIELDS) { error(_("wrong number of fields on Leap line")); @@ -1112,11 +1140,11 @@ const int nfields; dayoff = 0; cp = fields[LP_YEAR]; if (sscanf(cp, scheck(cp, "%d"), &year) != 1) { - /* - * Leapin' Lizards! - */ - error(_("invalid leaping year")); - return; + /* + ** Leapin' Lizards! + */ + error(_("invalid leaping year")); + return; } j = EPOCH_YEAR; while (j != year) { @@ -1147,7 +1175,7 @@ const int nfields; return; } dayoff = oadd(dayoff, eitol(day - 1)); - if (dayoff < 0 && !TYPE_SIGNED(time_t)) { + if (dayoff < 0 && !TYPE_SIGNED(zic_t)) { error(_("time before zero")); return; } @@ -1159,7 +1187,7 @@ const int nfields; error(_("time too large")); return; } - t = (time_t) dayoff * SECSPERDAY; + t = (zic_t) dayoff * SECSPERDAY; tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); cp = fields[LP_CORR]; { @@ -1183,7 +1211,9 @@ const int nfields; return; } if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { - error(_("illegal Rolling/Stationary field on Leap line")); + error(_( + "illegal Rolling/Stationary field on Leap line" + )); return; } leapadd(tadd(t, tod), positive, lp->l_value, count); @@ -1421,7 +1451,7 @@ const char * const name; register int i, j; static char * fullname; static struct tzhead tzh; - time_t ats[TZ_MAX_TIMES]; + zic_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; size_t len; @@ -1446,14 +1476,13 @@ const char * const name; while (fromi < timecnt && attypes[fromi].type == 0) ++fromi; /* handled by default rule */ for ( ; fromi < timecnt; ++fromi) { - if (toi != 0 - && ((attypes[fromi].at - + gmtoffs[attypes[toi - 1].type]) - <= (attypes[toi - 1].at - + gmtoffs[toi == 1 ? 0 - : attypes[toi - 2].type]))) { - attypes[toi - 1].type = attypes[fromi].type; - continue; + if (toi != 0 && ((attypes[fromi].at + + gmtoffs[attypes[toi - 1].type]) <= + (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 + : attypes[toi - 2].type]))) { + attypes[toi - 1].type = + attypes[fromi].type; + continue; } if (toi == 0 || attypes[toi - 1].type != attypes[fromi].type) @@ -1500,7 +1529,8 @@ const char * const name; convert(eitol(typecnt), tzh.tzh_typecnt); convert(eitol(charcnt), tzh.tzh_charcnt); (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); -#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp) +#define DO(field) (void) fwrite((void *) tzh.field, \ + (size_t) sizeof tzh.field, (size_t) 1, fp) DO(tzh_magic); DO(tzh_reserved); DO(tzh_ttisgmtcnt); @@ -1589,7 +1619,7 @@ const int zonecount; register struct rule * rp; register int i, j; register int usestart, useuntil; - register time_t starttime, untiltime; + register zic_t starttime, untiltime; register long gmtoff; register long stdoff; register int year; @@ -1658,7 +1688,7 @@ const int zonecount; } for ( ; ; ) { register int k; - register time_t jtime, ktime; + register zic_t jtime, ktime; register long offset; char buf[BUFSIZ]; @@ -1723,13 +1753,12 @@ const int zonecount; } if (*startbuf == '\0' && startoff == oadd(zp->z_gmtoff, - stdoff)) { + stdoff)) doabbr(startbuf, sizeof(startbuf), zp->z_format, rp->r_abbrvar, rp->r_stdoff != 0); - } } eats(zp->z_filename, zp->z_linenum, rp->r_filename, rp->r_linenum); @@ -1775,7 +1804,7 @@ error(_("can't determine time zone abbreviation to use just after until time")); static void addtt(starttime, type) -const time_t starttime; +const zic_t starttime; int type; { if (starttime <= min_time || @@ -1859,7 +1888,7 @@ const int ttisgmt; static void leapadd(t, positive, rolling, count) -const time_t t; +const zic_t t; const int positive; const int rolling; int count; @@ -2023,7 +2052,9 @@ register char * cp; else while ((*dp = *cp++) != '"') if (*dp != '\0') ++dp; - else error(_("Odd number of quotation marks")); + else error(_( + "Odd number of quotation marks" + )); } while (*cp != '\0' && *cp != '#' && (!isascii(*cp) || !isspace((unsigned char) *cp))); if (isascii(*cp) && isspace((unsigned char) *cp)) @@ -2049,12 +2080,12 @@ const long t2; return t; } -static time_t +static zic_t tadd(t1, t2) -const time_t t1; +const zic_t t1; const long t2; { - register time_t t; + register zic_t t; if (t1 == max_time && t2 > 0) return max_time; @@ -2073,14 +2104,14 @@ const long t2; ** 1970, 00:00 LOCAL time - in that year that the rule refers to. */ -static time_t +static zic_t rpytime(rp, wantedy) register const struct rule * const rp; register const int wantedy; { register int y, m, i; register long dayoff; /* with a nod to Margaret O. */ - register time_t t; + register zic_t t; if (wantedy == INT_MIN) return min_time; @@ -2144,16 +2175,17 @@ register const int wantedy; } if (i < 0 || i >= len_months[isleap(y)][m]) { if (noise) - warning(_("rule goes past start/end of month--will not work with pre-2004 versions of zic")); + warning(_("rule goes past start/end of month--\ +will not work with pre-2004 versions of zic")); } } - if (dayoff < 0 && !TYPE_SIGNED(time_t)) + if (dayoff < 0 && !TYPE_SIGNED(zic_t)) return min_time; if (dayoff < min_time / SECSPERDAY) return min_time; if (dayoff > max_time / SECSPERDAY) return max_time; - t = (time_t) dayoff * SECSPERDAY; + t = (zic_t) dayoff * SECSPERDAY; return tadd(t, rp->r_tod); } @@ -2163,6 +2195,41 @@ const char * const string; { register int i; + if (strcmp(string, GRANDPARENTED) != 0) { + register const char * cp; + register char * wp; + + /* + ** Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics + ** optionally followed by a + or - and a number from 1 to 14. + */ + cp = string; + wp = NULL; + while (isascii(*cp) && isalpha(*cp)) + ++cp; + if (cp - string == 0) +wp = _("time zone abbreviation lacks alphabetic at start"); + if (noise && cp - string > 3) +wp = _("time zone abbreviation has more than 3 alphabetics"); + if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) +wp = _("time zone abbreviation has too many alphabetics"); + if (wp == NULL && (*cp == '+' || *cp == '-')) { + ++cp; + if (isascii(*cp) && isdigit(*cp)) + if (*cp++ == '1' && *cp >= '0' && *cp <= '4') + ++cp; + } + if (*cp != '\0') +wp = _("time zone abbreviation differs from POSIX standard"); + if (wp != NULL) { + wp = ecpyalloc(wp); + wp = ecatalloc(wp, " ("); + wp = ecatalloc(wp, string); + wp = ecatalloc(wp, ")"); + warning(wp); + ifree(wp); + } + } i = strlen(string) + 1; if (charcnt + i > TZ_MAX_CHARS) { error(_("too many, or too long, time zone abbreviations")); |