diff options
Diffstat (limited to 'time')
-rw-r--r-- | time/Makefile | 3 | ||||
-rw-r--r-- | time/strftime.c | 399 | ||||
-rw-r--r-- | time/time.h | 28 | ||||
-rw-r--r-- | time/tzfile.c | 2 | ||||
-rw-r--r-- | time/tzset.c | 2 |
5 files changed, 322 insertions, 112 deletions
diff --git a/time/Makefile b/time/Makefile index 84e52306d7..7eeedd4e53 100644 --- a/time/Makefile +++ b/time/Makefile @@ -22,7 +22,8 @@ subdir := time headers := time.h sys/time.h sys/timeb.h timebits.h -distribute := tzfile.h private.h scheck.c ialloc.c yearistype +distribute := tzfile.h private.h scheck.c ialloc.c yearistype \ + iso3166.tab zone.tab tzselect.ksh checktab.awk extra-objs = scheck.o ialloc.o $(tzfiles:%=z.%) routines := offtime asctime clock ctime ctime_r difftime \ diff --git a/time/strftime.c b/time/strftime.c index 129fd1412c..26f4b7f354 100644 --- a/time/strftime.c +++ b/time/strftime.c @@ -23,13 +23,13 @@ Cambridge, MA 02139, USA. */ #ifdef _LIBC # define HAVE_LIMITS_H 1 # define HAVE_MBLEN 1 +# define HAVE_TM_GMTOFF 1 # define HAVE_TM_ZONE 1 # define STDC_HEADERS 1 # include <ansidecl.h> # include "../locale/localeinfo.h" #endif -#include <stdio.h> #include <sys/types.h> /* Some systems define `time_t' here. */ #ifdef TIME_WITH_SYS_TIME @@ -75,9 +75,22 @@ Cambridge, MA 02139, USA. */ #endif #endif -/* Uncomment following line in the production version. */ -/* #define NDEBUG */ -#include <assert.h> +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define TYPE_SIGNED(t) ((t) -1 < 0) + +/* Bound on length of the string representing an integer value of type t. + Subtract one for the sign bit if t is signed; + 302 / 1000 is log10 (2) rounded up; + add one for integer division truncation; + add one more for a minus sign if t is signed. */ +#define INT_STRLEN_BOUND(t) \ + ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t)) + +#define TM_YEAR_BASE 1900 + static unsigned int week __P ((const struct tm *const, int, int)); @@ -97,11 +110,30 @@ static unsigned int week __P ((const struct tm *const, int, int)); } while (0) #define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n))) -#ifdef _LIBC -#define fmt(n, args) add((n), if (sprintf args != (n)) return 0) -#else -#define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0) -#endif +#if ! HAVE_TM_GMTOFF +/* Yield the difference between *A and *B, + measured in seconds, ignoring leap seconds. */ +static int tm_diff __P ((const struct tm *, const struct tm *)); +static int +tm_diff (a, b) + const struct tm *a; + const struct tm *b; +{ + int ay = a->tm_year + TM_YEAR_BASE - 1; + int by = b->tm_year + TM_YEAR_BASE - 1; + /* Divide years by 100, rounding towards minus infinity. */ + int ac = ay / 100 - (ay % 100 < 0); + int bc = by / 100 - (by % 100 < 0); + int intervening_leap_days = + ((ay >> 2) - (by >> 2)) - (ac - bc) + ((ac >> 2) - (bc >> 2)); + int years = ay - by; + int days = (365 * years + intervening_leap_days + + (a->tm_yday - b->tm_yday)); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} +#endif /* ! HAVE_TM_GMTOFF */ @@ -181,6 +213,9 @@ strftime (s, maxsize, format, tp) size_t aw_len = strlen(a_wkday); size_t am_len = strlen(a_month); size_t ap_len = strlen (ampm); + + const char * const*alt_digits = &_NL_CURRENT (LC_TIME, ALT_DIGITS); + int nr_alt_digits = (_NL_CURRENT (LC_TIME, ALT_DIGITS + 1) - *alt_digits); #else const char *const f_wkday = weekday_name[tp->tm_wday]; const char *const f_month = month_name[tp->tm_mon]; @@ -201,10 +236,6 @@ strftime (s, maxsize, format, tp) register size_t i = 0; register char *p = s; register const char *f; - char number_fmt[5]; - - /* Initialize the buffer we will use for the sprintf format for numbers. */ - number_fmt[0] = '%'; zone = 0; #if HAVE_TM_ZONE @@ -227,9 +258,15 @@ strftime (s, maxsize, format, tp) for (f = format; *f != '\0'; ++f) { enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */ - unsigned int maxdigits; /* Max digits for numeric format. */ + unsigned int digits; /* Max digits for numeric format. */ unsigned int number_value; /* Numeric value to be printed. */ - const char *subfmt; + int negative_number; /* 1 if the number is negative. */ + const char *subfmt = ""; + enum { none, alternate, era } modifier; + char *bufp; + char buf[1 + (sizeof (int) < sizeof (time_t) + ? INT_STRLEN_BOUND (time_t) + : INT_STRLEN_BOUND (int))]; #if HAVE_MBLEN if (!isascii (*f)) @@ -267,37 +304,76 @@ strftime (s, maxsize, format, tp) break; } + /* Check for modifiers. */ + switch (*f) + { + case 'E': + ++f; + modifier = era; + break; + case 'O': + ++f; + modifier = alternate; + break; + default: + modifier = none; + break; + } + /* Now do the specified format. */ switch (*f) { - case '\0': +#define DO_NUMBER(d, v) \ + digits = d; number_value = v; goto do_number +#define DO_NUMBER_SPACEPAD(d, v) \ + digits = d; number_value = v; goto do_number_spacepad + + case '\0': /* GNU extension: % at end of format. */ + --f; + /* Fall through. */ case '%': + if (modifier != none) + goto bad_format; add (1, *p = *f); break; case 'a': + if (modifier != none) + goto bad_format; cpy (aw_len, a_wkday); break; case 'A': + if (modifier != none) + goto bad_format; cpy (wkday_len, f_wkday); break; case 'b': case 'h': /* GNU extension. */ + if (modifier != none) + goto bad_format; cpy (am_len, a_month); break; case 'B': + if (modifier != none) + goto bad_format; cpy (month_len, f_month); break; case 'c': + if (modifier == alternate) + goto bad_format; #ifdef _NL_CURRENT - subfmt = _NL_CURRENT (LC_TIME, D_T_FMT); + if (modifier == era) + subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); + if (*subfmt == '\0') + subfmt = _NL_CURRENT (LC_TIME, D_T_FMT); #else - subfmt = "%a %b %d %H:%M:%S %Z %Y"; + subfmt = "%a %b %e %H:%M:%S %Z %Y"; #endif + subformat: { size_t len = strftime (p, maxsize - i, subfmt, tp); @@ -307,17 +383,25 @@ strftime (s, maxsize, format, tp) } break; -#define DO_NUMBER(digits, value) \ - maxdigits = digits; number_value = value; goto do_number -#define DO_NUMBER_SPACEPAD(digits, value) \ - maxdigits = digits; number_value = value; goto do_number_spacepad - case 'C': + if (modifier == alternate) + goto bad_format; +#ifdef _NL_CURRENT + /* XXX I'm not sure about this. --drepper@gnu */ + if (modifier == era && + *(subfmt = _NL_CURRENT (LC_TIME, ERA)) != '\0') + goto subformat; +#endif DO_NUMBER (2, (1900 + tp->tm_year) / 100); case 'x': + if (modifier == alternate) + goto bad_format; #ifdef _NL_CURRENT - subfmt = _NL_CURRENT (LC_TIME, D_FMT); + if (modifier == era) + subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); + if (*subfmt == '\0') + subfmt = _NL_CURRENT (LC_TIME, D_FMT); goto subformat; #endif /* Fall through. */ @@ -326,12 +410,18 @@ strftime (s, maxsize, format, tp) goto subformat; case 'd': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, tp->tm_mday); case 'e': /* GNU extension: %d, but blank-padded. */ + if (modifier == era) + goto bad_format; + DO_NUMBER_SPACEPAD (2, tp->tm_mday); - /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then + /* All numeric formats set DIGITS and NUMBER_VALUE and then jump to one of these two labels. */ do_number_spacepad: @@ -339,56 +429,106 @@ strftime (s, maxsize, format, tp) pad = pad_space; do_number: + /* Format the number according to the MODIFIER flag. */ + +#ifdef _NL_CURRENT + if (modifier == alternate && 0 <= number_value + && number_value < (unsigned int) nr_alt_digits) + { + /* ALT_DIGITS is the first entry in an array with + alternative digit symbols. */ + size_t digitlen = strlen (*(alt_digits + number_value)); + if (digitlen == 0) + break; + cpy (digitlen, *(alt_digits + number_value)); + goto done_with_number; + } +#endif { - /* Format the number according to the PAD flag. */ + unsigned int u = number_value; - register char *nf = &number_fmt[1]; - int printed = maxdigits; + bufp = buf + sizeof (buf); + negative_number = number_value < 0; - switch (pad) - { - case pad_zero: - *nf++ = '0'; - case pad_space: - *nf++ = '0' + maxdigits; - case pad_none: - *nf++ = 'u'; - *nf = '\0'; - } + if (negative_number) + u = -u; -#ifdef _LIBC - add (maxdigits, printed = sprintf (p, number_fmt, number_value)); -#else - add (maxdigits, sprintf (p, number_fmt, number_value); - printed = strlen (p)); -#endif - /* Back up if fewer than MAXDIGITS chars written for pad_none. */ - p -= maxdigits - printed; - i -= maxdigits - printed; + do + *--bufp = u % 10 + '0'; + while ((u /= 10) != 0); + } - break; - } + do_number_sign_and_padding: + if (negative_number) + *--bufp = '-'; + + if (pad != pad_none) + { + int padding = digits - (buf + sizeof (buf) - bufp); + + if (pad == pad_space) + { + while (0 < padding--) + *--bufp = ' '; + } + else + { + bufp += negative_number; + while (0 < padding--) + *--bufp = '0'; + if (negative_number) + *--bufp = '-'; + } + } + + cpy (buf + sizeof (buf) - bufp, bufp); + +#ifdef _NL_CURRENT + done_with_number: +#endif + break; case 'H': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, tp->tm_hour); case 'I': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, hour12); case 'k': /* GNU extension. */ + if (modifier == era) + goto bad_format; + DO_NUMBER_SPACEPAD (2, tp->tm_hour); case 'l': /* GNU extension. */ + if (modifier == era) + goto bad_format; + DO_NUMBER_SPACEPAD (2, hour12); case 'j': + if (modifier == era) + goto bad_format; + DO_NUMBER (3, 1 + tp->tm_yday); case 'M': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, tp->tm_min); case 'm': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, tp->tm_mon + 1); case 'n': /* GNU extension. */ @@ -408,32 +548,55 @@ strftime (s, maxsize, format, tp) goto subformat; case 'S': + if (modifier == era) + return 0; + DO_NUMBER (2, tp->tm_sec); case 's': /* GNU extension. */ - { - struct tm writable_tm = *tp; - unsigned long int num = (unsigned long int) mktime (&writable_tm); - /* `3 * sizeof (unsigned long int)' is an approximation of - the size of the decimal representation of NUM, valid - for sizes <= 16. */ - int printed = 3 * sizeof (unsigned long int); - maxdigits = printed; - assert (sizeof (unsigned long int) <= 16); -#ifdef _LIBC - add (maxdigits, printed = sprintf (p, "%lu", num)); -#else - add (maxdigits, sprintf (p, "%lu", num); printed = strlen (p)); -#endif - /* Back up if fewer than MAXDIGITS chars written for pad_none. */ - p -= maxdigits - printed; - i -= maxdigits - printed; + { + struct tm ltm = *tp; + time_t t = mktime (<m); + + /* Generate string value for T using time_t arithmetic; + this works even if sizeof (long) < sizeof (time_t). */ + + bufp = buf + sizeof (buf); + negative_number = t < 0; + + do + { + int d = t % 10; + t /= 10; + + if (negative_number) + { + d = -d; + + /* Adjust if division truncates to minus infinity. */ + if (0 < -1 % 10 && d < 0) + { + t++; + d += 10; + } + } + + *--bufp = d + '0'; + } + while (t != 0); + + digits = 1; + goto do_number_sign_and_padding; } - break; case 'X': + if (modifier == alternate) + goto bad_format; #ifdef _NL_CURRENT - subfmt = _NL_CURRENT (LC_TIME, T_FMT); + if (modifier == era) + subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); + if (*subfmt == '\0') + subfmt = _NL_CURRENT (LC_TIME, T_FMT); goto subformat; #endif /* Fall through. */ @@ -446,61 +609,88 @@ strftime (s, maxsize, format, tp) break; case 'U': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, y_week0); case 'V': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, y_week2); case 'W': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, y_week1); case 'w': + if (modifier == era) + goto bad_format; + DO_NUMBER (2, tp->tm_wday); case 'Y': - DO_NUMBER (4, 1900 + tp->tm_year); +#ifdef _NL_CURRENT + if (modifier == era + && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0') + goto subformat; + else +#endif + if (modifier == alternate) + goto bad_format; + else + DO_NUMBER (4, 1900 + tp->tm_year); case 'y': +#ifdef _NL_CURRENT + if (modifier == era + && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0') + goto subformat; +#endif DO_NUMBER (2, tp->tm_year % 100); case 'Z': cpy(zonelen, zone); break; - case 'z': + case 'z': /* GNU extension. */ + if (tp->tm_isdst < 0) + break; + { - struct tm tml = *tp; - struct tm tmg; - time_t t; - time_t offset = 0; int diff; +#if HAVE_TM_GMTOFF + diff = tp->tm_gmtoff; +#else + struct tm gtm; + struct tm ltm = *tp; + time_t lt = mktime (<m); - t = __mktime_internal (&tml, __localtime_r, &offset); - - /* Canonicalize the local time. */ - if (t == (time_t) -1 || __localtime_r (&t, &tml) == NULL) - /* We didn't managed to get the local time. Assume it - GMT as a reasonable default value. */ - diff = 0; - else + if (lt == (time_t) -1) { - __gmtime_r (&t, &tmg); + /* mktime returns -1 for errors, but -1 is also a + valid time_t value. Check whether an error really + occurred. */ + struct tm tm; + localtime_r (<, &tm); + + if ((ltm.tm_sec ^ tm.tm_sec) + | (ltm.tm_min ^ tm.tm_min) + | (ltm.tm_hour ^ tm.tm_hour) + | (ltm.tm_mday ^ tm.tm_mday) + | (ltm.tm_mon ^ tm.tm_mon) + | (ltm.tm_year ^ tm.tm_year)) + break; + } - /* Compute the difference. */ - diff = tml.tm_min - tmg.tm_min; - diff += 60 * (tml.tm_hour - tmg.tm_hour); + if (! gmtime_r (<, >m)) + break; - if (tml.tm_mon != tmg.tm_mon) - { - /* We assume no timezone differs from UTC by more - than +- 23 hours. This should be safe. */ - if (tmg.tm_mday == 1) - tml.tm_mday = 0; - else /* tml.tm_mday == 1 */ - tmg.tm_mday = 0; - } - diff += 1440 * (tml.tm_mday - tmg.tm_mday); - } + diff = tm_diff (<m, >m); +#endif if (diff < 0) { @@ -511,11 +701,24 @@ strftime (s, maxsize, format, tp) add (1, *p = '+'); pad = pad_zero; - DO_NUMBER (4, ((diff / 60) % 24) * 100 + diff % 60); + + diff /= 60; + DO_NUMBER (4, (diff / 60) * 100 + diff % 60); } default: /* Bad format. */ + bad_format: + if (pad == pad_space) + add (1, *p = '_'); + else if (pad == pad_zero) + add (1, *p = '0'); + + if (modifier == era) + add (1, *p = 'E'); + else if (modifier == alternate) + add (1, *p = 'O'); + add (1, *p = *f); break; } diff --git a/time/time.h b/time/time.h index 6b955f1986..f4c27f926e 100644 --- a/time/time.h +++ b/time/time.h @@ -22,7 +22,7 @@ Cambridge, MA 02139, USA. */ #ifndef _TIME_H -#if (! defined (__need_time_t) && !defined(__need_clock_t) && \ +#if (! defined (__need_time_t) && !defined (__need_clock_t) && \ ! defined (__need_timespec)) #define _TIME_H 1 #include <features.h> @@ -54,8 +54,8 @@ __BEGIN_DECLS #endif /* <time.h> included. */ -#if !defined(__clock_t_defined) && \ - (defined(_TIME_H) || defined(__need_clock_t)) +#if !defined (__clock_t_defined) && \ + (defined (_TIME_H) || defined (__need_clock_t)) #define __clock_t_defined 1 #include <gnu/types.h> @@ -66,8 +66,8 @@ typedef __clock_t clock_t; #endif /* clock_t not defined and <time.h> or need clock_t. */ #undef __need_clock_t -#if !defined(__time_t_defined) && \ - (defined(_TIME_H) || defined(__need_time_t)) +#if !defined (__time_t_defined) && \ + (defined (_TIME_H) || defined (__need_time_t)) #define __time_t_defined 1 #include <gnu/types.h> @@ -79,7 +79,7 @@ typedef __time_t time_t; #undef __need_time_t -#if ! defined(__timespec_defined) && \ +#if ! defined (__timespec_defined) && \ ((defined (_TIME_H) && defined (__USE_POSIX)) || \ defined (__need_timespec)) #define __timespec_defined 1 @@ -101,7 +101,7 @@ struct timespec /* Used by other time functions. */ struct tm { - int tm_sec; /* Seconds. [0-61] (2 leap seconds) */ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ int tm_min; /* Minutes. [0-59] */ int tm_hour; /* Hours. [0-23] */ int tm_mday; /* Day. [1-31] */ @@ -110,8 +110,14 @@ struct tm int tm_wday; /* Day of week. [0-6] */ int tm_yday; /* Days in year.[0-365] */ int tm_isdst; /* DST. [-1/0/1]*/ - long int tm_gmtoff; /* Seconds west of UTC. */ + +#ifdef __USE_BSD + long int tm_gmtoff; /* Seconds east of UTC. */ __const char *tm_zone; /* Timezone abbreviation. */ +#else + long int __tm_gmtoff; /* Seconds east of UTC. */ + __const char *__tm_zone; /* Timezone abbreviation. */ +#endif }; #endif /* <time.h> included. */ @@ -190,7 +196,7 @@ extern void __offtime __P ((__const time_t *__timer, that is the representation of TP in this format. */ extern char *asctime __P ((__const struct tm *__tp)); -/* Equivalent to `asctime(localtime(timer))'. */ +/* Equivalent to `asctime (localtime (timer))'. */ extern char *ctime __P ((__const time_t *__timer)); #ifdef __USE_REENTRANT @@ -201,14 +207,14 @@ extern char *ctime __P ((__const time_t *__timer)); extern char *__asctime_r __P ((__const struct tm *__tp, char *__buf)); extern char *asctime_r __P ((__const struct tm *__tp, char *__buf)); -/* Equivalent to `asctime_r(localtime_r(timer, *TMP*), buf)'. */ +/* Equivalent to `asctime_r (localtime_r (timer, *TMP*), buf)'. */ extern char *ctime_r __P ((__const time_t *__timer, char *__buf)); #endif /* reentrant */ /* Defined in localtime.c. */ extern char *__tzname[2]; /* Current timezone names. */ -extern int __daylight; /* If it is daylight savings time. */ +extern int __daylight; /* If daylight-saving time is ever in use. */ extern long int __timezone; /* Seconds west of UTC. */ /* Set time conversion information from the TZ environment variable. diff --git a/time/tzfile.c b/time/tzfile.c index 332ed46c33..e334249e77 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -402,7 +402,7 @@ DEFUN(__tzfile_compute, (timer, leap_correct, leap_hit), void DEFUN(compute_tzname_max, (chars), size_t chars) { - extern long int __tzname_cur_max; /* Defined in __tzset.c. */ + extern size_t __tzname_cur_max; /* Defined in __tzset.c. */ const char *p; diff --git a/time/tzset.c b/time/tzset.c index 8323c7be6d..e5b12f1e51 100644 --- a/time/tzset.c +++ b/time/tzset.c @@ -358,7 +358,7 @@ DEFUN_VOID(__tzset) /* Maximum length of a timezone name. __tz_compute keeps this up to date (never decreasing it) when ! __use_tzfile. tzfile.c keeps it up to date when __use_tzfile. */ -long int __tzname_cur_max; +size_t __tzname_cur_max; long int DEFUN_VOID(__tzname_max) |