summaryrefslogtreecommitdiffstats
path: root/lib/libc/stdtime
diff options
context:
space:
mode:
authoredwin <edwin@FreeBSD.org>2009-05-23 06:31:50 +0000
committeredwin <edwin@FreeBSD.org>2009-05-23 06:31:50 +0000
commitfb1b2af8077c535d3cde4b474cf0840abc085379 (patch)
treedc007639a92f090be57a9884d0c66506ca4ecb14 /lib/libc/stdtime
parent040089ea0825fa08d374d75dc9d9c66e489dae6f (diff)
downloadFreeBSD-src-fb1b2af8077c535d3cde4b474cf0840abc085379.zip
FreeBSD-src-fb1b2af8077c535d3cde4b474cf0840abc085379.tar.gz
MFV of tzcode2009e:
Upgrade of the tzcode from 2004a to 2009e. Changes are numerous, but include... - New format of the output of zic, which supports both 32 and 64 bit time_t formats. - zdump on 64 bit platforms will actually produce some output instead of doing nothing for a looooooooong time. - linux_base-fX, with X >= at least 8, will work without problems related to the local time again. The original patch, based on the 2008e, has been running for a long time on both my laptop and desktop machine and have been tested by other people. After the installation of this code and the running of zic(8), you need to run tzsetup(8) again to install the new datafile. Approved by: wollman@ for usr.sbin/zic MFC after: 1 month
Diffstat (limited to 'lib/libc/stdtime')
-rw-r--r--lib/libc/stdtime/asctime.c106
-rw-r--r--lib/libc/stdtime/difftime.c98
-rw-r--r--lib/libc/stdtime/localtime.c743
-rw-r--r--lib/libc/stdtime/private.h154
-rw-r--r--lib/libc/stdtime/strftime.c110
-rw-r--r--lib/libc/stdtime/time2posix.33
-rw-r--r--lib/libc/stdtime/tzfile.522
-rw-r--r--lib/libc/stdtime/tzfile.h62
8 files changed, 922 insertions, 376 deletions
diff --git a/lib/libc/stdtime/asctime.c b/lib/libc/stdtime/asctime.c
index 0f4212f..30606f1 100644
--- a/lib/libc/stdtime/asctime.c
+++ b/lib/libc/stdtime/asctime.c
@@ -1,12 +1,18 @@
/*
** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** 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.
*/
#include <sys/cdefs.h>
#ifndef lint
#ifndef NOID
-static char elsieid[] __unused = "@(#)asctime.c 7.9";
+static char elsieid[] __unused = "@(#)asctime.c 8.2";
#endif /* !defined NOID */
#endif /* !defined lint */
__FBSDID("$FreeBSD$");
@@ -19,7 +25,57 @@ __FBSDID("$FreeBSD$");
#include "tzfile.h"
/*
-** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12.
+** Some systems only handle "%.2d"; others only handle "%02d";
+** "%02.2d" makes (most) everybody happy.
+** At least some versions of gcc warn about the %02.2d;
+** we conditionalize below to avoid the warning.
+*/
+/*
+** All years associated with 32-bit time_t values are exactly four digits long;
+** some years associated with 64-bit time_t values are not.
+** 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
+** 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.
+*/
+#ifdef __GNUC__
+#define ASCTIME_FMT "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %-4s\n"
+#else /* !defined __GNUC__ */
+#define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n"
+#endif /* !defined __GNUC__ */
+/*
+** 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).
+*/
+#ifdef __GNUC__
+#define ASCTIME_FMT_B "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %s\n"
+#else /* !defined __GNUC__ */
+#define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %s\n"
+#endif /* !defined __GNUC__ */
+
+#define STD_ASCTIME_BUF_SIZE 26
+/*
+** Big enough for something such as
+** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
+** (two three-character abbreviations, five strings denoting integers,
+** seven explicit spaces, two explicit colons, a newline,
+** and a trailing ASCII nul).
+** The values above are for systems where an int is 32 bits and are provided
+** as an example; the define below calculates the maximum for the system at
+** hand.
+*/
+#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
+
+static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
+
+/*
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
char *
@@ -36,6 +92,8 @@ char * buf;
};
const char * wn;
const char * mn;
+ char year[INT_STRLEN_MAXIMUM(int) + 2];
+ char result[MAX_ASCTIME_BUF_SIZE];
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
wn = "???";
@@ -44,35 +102,41 @@ char * buf;
mn = "???";
else mn = mon_name[timeptr->tm_mon];
/*
- ** The X3J11-suggested format is
- ** "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
- ** Since the .2 in 02.2d is ignored, we drop it.
+ ** 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) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+ (void) strftime(year, sizeof year, "%Y", timeptr);
+ /*
+ ** We avoid using snprintf since it's not available on all systems.
+ */
+ (void) sprintf(result,
+ ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
- TM_YEAR_BASE + timeptr->tm_year);
- return buf;
+ year);
+ if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
+ (void) strcpy(buf, result);
+ return buf;
+ } else {
+#ifdef EOVERFLOW
+ errno = EOVERFLOW;
+#else /* !defined EOVERFLOW */
+ errno = EINVAL;
+#endif /* !defined EOVERFLOW */
+ return NULL;
+ }
}
/*
-** A la X3J11, with core dump avoidance.
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
char *
asctime(timeptr)
const struct tm * timeptr;
{
- /*
- ** Big enough for something such as
- ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
- ** (two three-character abbreviations, five strings denoting integers,
- ** three explicit spaces, two explicit colons, a newline,
- ** and a trailing ASCII nul).
- */
- static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
- 3 + 2 + 1 + 1];
-
- return asctime_r(timeptr, result);
+ return asctime_r(timeptr, buf_asctime);
}
diff --git a/lib/libc/stdtime/difftime.c b/lib/libc/stdtime/difftime.c
index 596ea82..d16f9a0 100644
--- a/lib/libc/stdtime/difftime.c
+++ b/lib/libc/stdtime/difftime.c
@@ -1,12 +1,12 @@
/*
** 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.
*/
#include <sys/cdefs.h>
#ifndef lint
#ifndef NOID
-static char elsieid[] __unused = "@(#)difftime.c 7.9";
+static char elsieid[] __unused = "@(#)difftime.c 8.1";
#endif /* !defined NOID */
#endif /* !defined lint */
__FBSDID("$FreeBSD$");
@@ -14,74 +14,56 @@ __FBSDID("$FreeBSD$");
/*LINTLIBRARY*/
#include "namespace.h"
-#include "private.h"
+#include "private.h" /* for time_t, TYPE_INTEGRAL, and TYPE_SIGNED */
#include "un-namespace.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 */
-
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/stdtime/localtime.c b/lib/libc/stdtime/localtime.c
index 5928943..83cecd1 100644
--- a/lib/libc/stdtime/localtime.c
+++ b/lib/libc/stdtime/localtime.c
@@ -1,20 +1,19 @@
/*
** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
*/
#include <sys/cdefs.h>
#ifndef lint
#ifndef NOID
-static char elsieid[] __unused = "@(#)localtime.c 7.78";
+static char elsieid[] __unused = "@(#)localtime.c 8.9";
#endif /* !defined NOID */
#endif /* !defined lint */
__FBSDID("$FreeBSD$");
/*
-** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
-** POSIX-style TZ environment variable handling from Guy Harris
-** (guy@auspex.com).
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
*/
/*LINTLIBRARY*/
@@ -28,6 +27,20 @@ __FBSDID("$FreeBSD$");
#include "un-namespace.h"
#include "tzfile.h"
+#include "float.h" /* for FLT_MAX and DBL_MAX */
+
+#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 */
#include "libc_private.h"
@@ -74,16 +87,16 @@ __FBSDID("$FreeBSD$");
** 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;
/*
* In June 2004 it was decided UTC was a more appropriate default time
@@ -130,6 +143,8 @@ struct state {
int timecnt;
int typecnt;
int charcnt;
+ int goback;
+ int goahead;
time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES];
@@ -155,40 +170,49 @@ struct rule {
*/
static long detzcode(const char * codep);
+static time_t detzcode64(const char * codep);
+static int differ_by_repeat(time_t t1, time_t t0);
static const char * getzname(const char * strp);
+static const char * getqzname(const char * strp, const int delim);
static const char * getnum(const char * strp, int * nump, int min,
int max);
static const char * getsecs(const char * strp, long * secsp);
static const char * getoffset(const char * strp, long * offsetp);
static const char * getrule(const char * strp, struct rule * rulep);
static void gmtload(struct state * sp);
-static void gmtsub(const time_t * timep, long offset,
+static struct tm * gmtsub(const time_t * timep, long offset,
struct tm * tmp);
-static void localsub(const time_t * timep, long offset,
+static struct tm * localsub(const time_t * timep, long offset,
struct tm * tmp);
static int increment_overflow(int * number, int delta);
+static int leaps_thru_end_of(int y);
+static int long_increment_overflow(long * number, int delta);
+static int long_normalize_overflow(long * tensptr,
+ int * unitsptr, int base);
static int normalize_overflow(int * tensptr, int * unitsptr,
int base);
static void settzname(void);
static time_t time1(struct tm * tmp,
- void(*funcp) (const time_t *,
+ struct tm * (*funcp)(const time_t *,
long, struct tm *),
long offset);
static time_t time2(struct tm *tmp,
- void(*funcp) (const time_t *,
+ struct tm * (*funcp)(const time_t *,
long, struct tm*),
long offset, int * okayp);
static time_t time2sub(struct tm *tmp,
- void(*funcp) (const time_t *,
+ struct tm * (*funcp)(const time_t *,
long, struct tm*),
long offset, int * okayp, int do_norm_secs);
-static void timesub(const time_t * timep, long offset,
+static struct tm * timesub(const time_t * timep, long offset,
const struct state * sp, struct tm * tmp);
static int tmcomp(const struct tm * atmp,
const struct tm * btmp);
static time_t transtime(time_t janfirst, int year,
const struct rule * rulep, long offset);
-static int tzload(const char * name, struct state * sp);
+static int typesequiv(const struct state * sp, int a, int b);
+static int tzload(const char * name, struct state * sp,
+ int doextend);
static int tzparse(const char * name, struct state * sp,
int lastditch);
@@ -224,7 +248,7 @@ char * tzname[2] = {
** Except for the strftime function, these functions [asctime,
** ctime, gmtime, localtime] return values in one of two static
** objects: a broken-down time structure and an array of char.
-** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
+** Thanks to Paul Eggert for noting this.
*/
static struct tm tm;
@@ -245,12 +269,25 @@ const char * const codep;
long result;
int i;
- result = (codep[0] & 0x80) ? ~0L : 0L;
+ result = (codep[0] & 0x80) ? ~0L : 0;
for (i = 0; i < 4; ++i)
result = (result << 8) | (codep[i] & 0xff);
return result;
}
+static time_t
+detzcode64(codep)
+const char * const codep;
+{
+ register time_t result;
+ register int i;
+
+ result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0;
+ for (i = 0; i < 8; ++i)
+ result = result * 256 + (codep[i] & 0xff);
+ return result;
+}
+
static void
settzname(void)
{
@@ -299,16 +336,58 @@ settzname(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
+differ_by_repeat(t1, t0)
+const time_t t1;
+const time_t t0;
+{
+ int_fast64_t _t0 = t0;
+ int_fast64_t _t1 = t1;
+
+ if (TYPE_INTEGRAL(time_t) &&
+ TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+ return 0;
+ //turn ((int_fast64_t)(t1 - t0) == SECSPERREPEAT);
+ return _t1 - _t0 == SECSPERREPEAT;
}
static int
-tzload(name, sp)
+tzload(name, sp, doextend)
const char * name;
struct state * const sp;
+register const int doextend;
{
const char * p;
int i;
int fid;
+ int stored;
+ int nread;
+ union {
+ struct tzhead tzhead;
+ char buf[2 * sizeof(struct tzhead) +
+ 2 * sizeof *sp +
+ 4 * TZ_MAX_TIMES];
+ } u;
/* XXX The following is from OpenBSD, and I'm not sure it is correct */
if (name != NULL && issetugid() != 0)
@@ -356,18 +435,13 @@ struct state * const sp;
return -1;
}
}
- {
- struct tzhead * tzhp;
- union {
- struct tzhead tzhead;
- char buf[sizeof *sp + sizeof *tzhp];
- } u;
+ nread = _read(fid, u.buf, sizeof u.buf);
+ if (_close(fid) < 0 || nread <= 0)
+ return -1;
+ for (stored = 4; stored <= 8; stored *= 2) {
int ttisstdcnt;
int ttisgmtcnt;
- i = _read(fid, u.buf, sizeof u.buf);
- if (_close(fid) != 0)
- return -1;
ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
@@ -382,17 +456,19 @@ struct state * const sp;
(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
return -1;
- if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
+ if (nread - (p - u.buf) <
+ sp->timecnt * stored + /* ats */
sp->timecnt + /* types */
- sp->typecnt * (4 + 2) + /* ttinfos */
+ sp->typecnt * 6 + /* ttinfos */
sp->charcnt + /* chars */
- sp->leapcnt * (4 + 4) + /* lsinfos */
+ sp->leapcnt * (stored + 4) + /* lsinfos */
ttisstdcnt + /* ttisstds */
ttisgmtcnt) /* ttisgmts */
return -1;
for (i = 0; i < sp->timecnt; ++i) {
- sp->ats[i] = detzcode(p);
- p += 4;
+ sp->ats[i] = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
}
for (i = 0; i < sp->timecnt; ++i) {
sp->types[i] = (unsigned char) *p++;
@@ -420,8 +496,9 @@ struct state * const sp;
struct lsinfo * lsisp;
lsisp = &sp->lsis[i];
- lsisp->ls_trans = detzcode(p);
- p += 4;
+ lsisp->ls_trans = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
lsisp->ls_corr = detzcode(p);
p += 4;
}
@@ -451,10 +528,127 @@ 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;
+ }
+ /*
+ ** If this is an old file, we're done.
+ */
+ if (u.tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - u.buf;
+ for (i = 0; i < nread; ++i)
+ u.buf[i] = p[i];
+ /*
+ ** If this is a narrow integer time_t system, we're done.
+ */
+ if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+ break;
+ }
+ if (doextend && nread > 2 &&
+ u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state ts;
+ register int result;
+
+ u.buf[nread - 1] = '\0';
+ result = tzparse(&u.buf[1], &ts, FALSE);
+ if (result == 0 && ts.typecnt == 2 &&
+ sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+ for (i = 0; i < 2; ++i)
+ ts.ttis[i].tt_abbrind +=
+ sp->charcnt;
+ for (i = 0; i < ts.charcnt; ++i)
+ sp->chars[sp->charcnt++] =
+ ts.chars[i];
+ i = 0;
+ while (i < ts.timecnt &&
+ ts.ats[i] <=
+ sp->ats[sp->timecnt - 1])
+ ++i;
+ while (i < ts.timecnt &&
+ sp->timecnt < TZ_MAX_TIMES) {
+ sp->ats[sp->timecnt] =
+ ts.ats[i];
+ sp->types[sp->timecnt] =
+ sp->typecnt +
+ ts.types[i];
+ ++sp->timecnt;
+ ++i;
+ }
+ sp->ttis[sp->typecnt++] = ts.ttis[0];
+ sp->ttis[sp->typecnt++] = ts.ttis[1];
+ }
+ }
+ sp->goback = sp->goahead = FALSE;
+ if (sp->timecnt > 1) {
+ for (i = 1; i < sp->timecnt; ++i)
+ if (typesequiv(sp, sp->types[i], sp->types[0]) &&
+ differ_by_repeat(sp->ats[i], sp->ats[0])) {
+ sp->goback = TRUE;
+ break;
+ }
+ for (i = sp->timecnt - 2; i >= 0; --i)
+ if (typesequiv(sp, sp->types[sp->timecnt - 1],
+ sp->types[i]) &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[i])) {
+ sp->goahead = TRUE;
+ break;
+ }
}
return 0;
}
+static int
+typesequiv(sp, a, b)
+const struct state * const sp;
+const int a;
+const int b;
+{
+ register int result;
+
+ if (sp == NULL ||
+ a < 0 || a >= sp->typecnt ||
+ b < 0 || b >= sp->typecnt)
+ result = FALSE;
+ else {
+ register const struct ttinfo * ap = &sp->ttis[a];
+ register const struct ttinfo * bp = &sp->ttis[b];
+ result = ap->tt_gmtoff == bp->tt_gmtoff &&
+ ap->tt_isdst == bp->tt_isdst &&
+ ap->tt_ttisstd == bp->tt_ttisstd &&
+ ap->tt_ttisgmt == bp->tt_ttisgmt &&
+ strcmp(&sp->chars[ap->tt_abbrind],
+ &sp->chars[bp->tt_abbrind]) == 0;
+ }
+ return result;
+}
+
static const int mon_lengths[2][MONSPERYEAR] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
@@ -466,7 +660,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.
*/
@@ -483,6 +677,25 @@ 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 don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char *
+getqzname(register const char *strp, const int delim)
+{
+ register int 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.
@@ -547,7 +760,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;
@@ -586,7 +799,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.
*/
@@ -705,7 +918,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.
*/
@@ -728,7 +941,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.
*/
@@ -766,10 +979,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; /* was "stdoffset = 0;" */
else {
@@ -778,15 +999,22 @@ const int lastditch;
return -1;
}
}
- load_result = tzload(TZDEFRULES, sp);
+ load_result = tzload(TZDEFRULES, sp, FALSE);
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)
@@ -813,11 +1041,8 @@ const int lastditch;
return -1;
sp->typecnt = 2; /* standard time and DST */
/*
- ** Two transitions per year, from EPOCH_YEAR to 2037.
+ ** Two transitions per year, from EPOCH_YEAR forward.
*/
- sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
- if (sp->timecnt > TZ_MAX_TIMES)
- return -1;
sp->ttis[0].tt_gmtoff = -dstoffset;
sp->ttis[0].tt_isdst = 1;
sp->ttis[0].tt_abbrind = stdlen + 1;
@@ -827,7 +1052,12 @@ const int lastditch;
atp = sp->ats;
typep = sp->types;
janfirst = 0;
- for (year = EPOCH_YEAR; year <= 2037; ++year) {
+ sp->timecnt = 0;
+ for (year = EPOCH_YEAR;
+ sp->timecnt + 2 <= TZ_MAX_TIMES;
+ ++year) {
+ time_t newfirst;
+
starttime = transtime(janfirst, year, &start,
stdoffset);
endtime = transtime(janfirst, year, &end,
@@ -843,8 +1073,13 @@ const int lastditch;
*atp++ = endtime;
*typep++ = 1; /* DST ends */
}
- janfirst += year_lengths[isleap(year)] *
+ sp->timecnt += 2;
+ newfirst = janfirst;
+ newfirst += year_lengths[isleap(year)] *
SECSPERDAY;
+ if (newfirst <= janfirst)
+ break;
+ janfirst = newfirst;
}
} else {
long theirstdoffset;
@@ -959,7 +1194,7 @@ static void
gmtload(sp)
struct state * const sp;
{
- if (tzload(gmt, sp) != 0)
+ if (tzload(gmt, sp, TRUE) != 0)
(void) tzparse(gmt, sp, TRUE);
}
@@ -990,7 +1225,7 @@ tzsetwall_basic(int rdlocked)
}
}
#endif /* defined ALL_STATE */
- if (tzload((char *) NULL, lclptr) != 0)
+ if (tzload((char *) NULL, lclptr, TRUE) != 0)
gmtload(lclptr);
settzname();
_RWLOCK_UNLOCK(&lcl_rwlock);
@@ -1053,7 +1288,7 @@ tzset_basic(int rdlocked)
lclptr->ttis[0].tt_gmtoff = 0;
lclptr->ttis[0].tt_abbrind = 0;
(void) strcpy(lclptr->chars, gmt);
- } else if (tzload(name, lclptr) != 0)
+ } else if (tzload(name, lclptr, TRUE) != 0)
if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
(void) gmtload(lclptr);
settzname();
@@ -1072,14 +1307,14 @@ tzset(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;
@@ -1088,15 +1323,53 @@ struct tm * const tmp;
struct state * sp;
const struct ttinfo * ttisp;
int i;
- const time_t t = *timep;
+ 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->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t newt = t;
+ register time_t seconds;
+ register time_t tcycles;
+ register int_fast64_t icycles;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return NULL;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ newt += seconds;
+ else newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(&newt, offset, tmp);
+ if (result == tmp) {
+ register time_t newy;
+
+ newy = tmp->tm_year;
+ if (t < sp->ats[0])
+ newy -= icycles * YEARSPERREPEAT;
+ else newy += icycles * YEARSPERREPEAT;
+ tmp->tm_year = newy;
+ if (tmp->tm_year != newy)
+ return NULL;
+ }
+ return result;
+ }
if (sp->timecnt == 0 || t < sp->ats[0]) {
i = 0;
while (sp->ttis[i].tt_isdst)
@@ -1105,10 +1378,17 @@ struct tm * const tmp;
break;
}
} else {
- for (i = 1; i < sp->timecnt; ++i)
- if (t < sp->ats[i])
- break;
- i = sp->types[i - 1];
+ register int lo = 1;
+ register int hi = sp->timecnt;
+
+ while (lo < hi) {
+ register int mid = (lo + hi) >> 1;
+
+ if (t < sp->ats[mid])
+ hi = mid;
+ else lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
}
ttisp = &sp->ttis[i];
/*
@@ -1117,12 +1397,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;
}
struct tm *
@@ -1168,27 +1449,29 @@ const time_t * const timep;
*/
struct tm *
-localtime_r(timep, tm)
+localtime_r(timep, tmp)
const time_t * const timep;
-struct tm * tm;
+struct tm * tmp;
{
_RWLOCK_RDLOCK(&lcl_rwlock);
tzset_basic(1);
- localsub(timep, 0L, tm);
+ localsub(timep, 0L, tmp);
_RWLOCK_UNLOCK(&lcl_rwlock);
- return tm;
+ return tmp;
}
/*
** 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;
+
if (!gmt_is_set) {
_MUTEX_LOCK(&gmt_mutex);
if (!gmt_is_set) {
@@ -1201,7 +1484,7 @@ struct tm * const tmp;
}
_MUTEX_UNLOCK(&gmt_mutex);
}
- timesub(timep, offset, gmtptr, tmp);
+ result = timesub(timep, offset, gmtptr, tmp);
#ifdef TM_ZONE
/*
** Could get fancy here and deliver something such as
@@ -1221,6 +1504,7 @@ struct tm * const tmp;
#endif /* State Farm */
}
#endif /* defined TM_ZONE */
+ return result;
}
struct tm *
@@ -1267,12 +1551,11 @@ const time_t * const timep;
*/
struct tm *
-gmtime_r(timep, tm)
+gmtime_r(timep, tmp)
const time_t * const timep;
-struct tm * tm;
+struct tm * tmp;
{
- gmtsub(timep, 0L, tm);
- return tm;
+ return gmtsub(timep, 0L, tmp);
}
#ifdef STD_INSPIRED
@@ -1282,13 +1565,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;
@@ -1296,10 +1591,10 @@ const struct state * const sp;
struct tm * const tmp;
{
const struct lsinfo * lp;
- long days;
+ time_t tdays;
+ int idays; /* unsigned would be so 2003 */
long rem;
- long y;
- int yleap;
+ int y;
const int * ip;
long corr;
int hit;
@@ -1333,60 +1628,93 @@ 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;
}
-#endif /* defined mc68k */
- rem += (offset - corr);
+ {
+ register long seconds;
+
+ seconds = tdays * SECSPERDAY + 0.5;
+ tdays = seconds / SECSPERDAY;
+ rem += seconds - tdays * SECSPERDAY;
+ }
+ /*
+ ** 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)]) {
- 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 *
@@ -1396,7 +1724,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));
@@ -1407,17 +1735,16 @@ 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);
}
/*
** Adapted from code provided by Robert Elz, who writes:
** 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).
*/
@@ -1427,7 +1754,7 @@ char * buf;
#endif /* !defined WRONG */
/*
-** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
+** Simplified normalize logic courtesy Paul Eggert.
*/
static int
@@ -1443,6 +1770,18 @@ int delta;
}
static int
+long_increment_overflow(number, delta)
+long * number;
+int delta;
+{
+ long number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int
normalize_overflow(tensptr, unitsptr, base)
int * const tensptr;
int * const unitsptr;
@@ -1458,6 +1797,21 @@ const int base;
}
static int
+long_normalize_overflow(tensptr, unitsptr, base)
+long * const tensptr;
+int * const unitsptr;
+const int base;
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return long_increment_overflow(tensptr, tensdelta);
+}
+
+static int
tmcomp(atmp, btmp)
const struct tm * const atmp;
const struct tm * const btmp;
@@ -1476,19 +1830,22 @@ const struct tm * const btmp;
static time_t
time2sub(tmp, funcp, offset, okayp, do_norm_secs)
struct tm * const tmp;
-void (* const funcp)(const time_t*, long, struct tm*);
+struct tm * (* const funcp)(const time_t*, long, struct tm*);
const long offset;
int * const okayp;
const int do_norm_secs;
{
const struct state * sp;
int dir;
- int bits;
- int i, j ;
+ int i, j;
int saved_seconds;
- time_t newt;
- time_t t;
- struct tm yourtm, mytm;
+ long li;
+ time_t lo;
+ time_t hi;
+ long y;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
*okayp = FALSE;
yourtm = *tmp;
@@ -1501,45 +1858,49 @@ const int do_norm_secs;
return WRONG;
if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
return WRONG;
- if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
+ y = yourtm.tm_year;
+ if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
return WRONG;
/*
- ** Turn yourtm.tm_year into an actual year number for now.
+ ** Turn y into an actual year number for now.
** It is converted back to an offset from TM_YEAR_BASE later.
*/
- if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
+ if (long_increment_overflow(&y, TM_YEAR_BASE))
return WRONG;
while (yourtm.tm_mday <= 0) {
- if (increment_overflow(&yourtm.tm_year, -1))
+ if (long_increment_overflow(&y, -1))
return WRONG;
- i = yourtm.tm_year + (1 < yourtm.tm_mon);
- yourtm.tm_mday += year_lengths[isleap(i)];
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
}
while (yourtm.tm_mday > DAYSPERLYEAR) {
- i = yourtm.tm_year + (1 < yourtm.tm_mon);
- yourtm.tm_mday -= year_lengths[isleap(i)];
- if (increment_overflow(&yourtm.tm_year, 1))
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(li)];
+ if (long_increment_overflow(&y, 1))
return WRONG;
}
for ( ; ; ) {
- i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
+ i = mon_lengths[isleap(y)][yourtm.tm_mon];
if (yourtm.tm_mday <= i)
break;
yourtm.tm_mday -= i;
if (++yourtm.tm_mon >= MONSPERYEAR) {
yourtm.tm_mon = 0;
- if (increment_overflow(&yourtm.tm_year, 1))
+ if (long_increment_overflow(&y, 1))
return WRONG;
}
}
- if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
+ if (long_increment_overflow(&y, -TM_YEAR_BASE))
+ return WRONG;
+ yourtm.tm_year = y;
+ if (yourtm.tm_year != y)
return WRONG;
/* Don't go below 1900 for POLA */
if (yourtm.tm_year < 0)
return WRONG;
if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
saved_seconds = 0;
- else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+ else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
/*
** We can't set tm_sec to 0, because that might push the
** time below the minimum representable time.
@@ -1557,33 +1918,53 @@ 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 we have more than this, we will overflow tm_year for tmcomp().
- ** We should really return an error if we cannot represent it.
- */
- if (bits > 48)
- bits = 48;
- /*
- ** 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;
+ if (t <= lo)
+ return WRONG;
+ ++lo;
+ } else if (t == hi) {
+ --t;
+ if (t >= hi)
+ return WRONG;
+ --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)
@@ -1594,7 +1975,8 @@ const int do_norm_secs;
** It's okay to guess wrong since the guess
** gets checked.
*/
- sp = (funcp == localsub) ? lclptr : gmtptr;
+ sp = (const struct state *)
+ ((funcp == localsub) ? lclptr : gmtptr);
#ifdef ALL_STATE
if (sp == NULL)
return WRONG;
@@ -1607,7 +1989,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)
@@ -1626,15 +2009,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)(const time_t*, long, struct tm*);
+struct tm * (* const funcp)(const time_t*, long, struct tm*);
const long offset;
int * const okayp;
{
@@ -1652,7 +2035,7 @@ int * const okayp;
static time_t
time1(tmp, funcp, offset)
struct tm * const tmp;
-void (* const funcp)(const time_t *, long, struct tm *);
+struct tm * (* const funcp)(const time_t *, long, struct tm *);
const long offset;
{
time_t t;
@@ -1670,7 +2053,7 @@ const long offset;
t = time2(tmp, funcp, offset, &okay);
#ifdef PCTS
/*
- ** PCTS code courtesy Grant Sullivan (grant@osf.org).
+ ** PCTS code courtesy Grant Sullivan.
*/
if (okay)
return t;
@@ -1687,7 +2070,7 @@ const long offset;
** We try to divine the type they started from and adjust to the
** type they need.
*/
- sp = (funcp == localsub) ? lclptr : gmtptr;
+ sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr);
#ifdef ALL_STATE
if (sp == NULL)
return WRONG;
@@ -1833,7 +2216,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/stdtime/private.h b/lib/libc/stdtime/private.h
index a3c7778..dda5bef 100644
--- a/lib/libc/stdtime/private.h
+++ b/lib/libc/stdtime/private.h
@@ -4,7 +4,7 @@
/*
** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
**
** $FreeBSD$
*/
@@ -37,11 +37,13 @@
#ifndef lint
#ifndef NOID
/*
-static char privatehid[] = "@(#)private.h 7.53";
+static char privatehid[] = "@(#)private.h 8.6";
*/
#endif /* !defined NOID */
#endif /* !defined lint */
+#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'.
@@ -63,10 +65,6 @@ static char privatehid[] = "@(#)private.h 7.53";
#define HAVE_SETTIMEOFDAY 3
#endif /* !defined HAVE_SETTIMEOFDAY */
-#ifndef HAVE_STRERROR
-#define HAVE_STRERROR 1
-#endif /* !defined HAVE_STRERROR */
-
#ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
@@ -104,17 +102,17 @@ static char privatehid[] = "@(#)private.h 7.53";
#include "stdio.h"
#include "errno.h"
#include "string.h"
-#include "limits.h" /* for CHAR_BIT */
+#include "limits.h" /* for CHAR_BIT et al. */
#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)
@@ -123,55 +121,84 @@ static char privatehid[] = "@(#)private.h 7.53";
#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
#endif /* !defined WEXITSTATUS */
-#if HAVE_UNISTD_H - 0
-#include "unistd.h" /* for F_OK and R_OK */
-#endif /* HAVE_UNISTD_H - 0 */
+#if HAVE_UNISTD_H
+#include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */
+#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)
/*
-** SunOS 4.1.1 headers lack FILENAME_MAX.
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+ (199901 <= __STDC_VERSION__ || \
+ 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
-#ifndef FILENAME_MAX
-
-#ifndef MAXPATHLEN
-#ifdef unix
-#include "sys/param.h"
-#endif /* defined unix */
-#endif /* !defined MAXPATHLEN */
+/*
+** Workarounds for compilers/systems.
+*/
-#ifdef MAXPATHLEN
-#define FILENAME_MAX MAXPATHLEN
-#endif /* defined MAXPATHLEN */
-#ifndef MAXPATHLEN
-#define FILENAME_MAX 1024 /* Pure guesswork */
-#endif /* !defined MAXPATHLEN */
+/*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+*/
-#endif /* !defined FILENAME_MAX */
+#ifndef asctime_r
+extern char * asctime_r(struct tm const *, char *);
+#endif
/*
** Private function declarations.
*/
-char * icalloc(int nelem, int elsize);
-char * icatalloc(char * old, const char * new);
-char * icpyalloc(const char * string);
-char * imalloc(int n);
-void * irealloc(void * pointer, int size);
-void icfree(char * pointer);
-void ifree(char * pointer);
-char * scheck(const char *string, const char *format);
+char * icalloc(int nelem, int elsize);
+char * icatalloc(char * old, const char * new);
+char * icpyalloc(const char * string);
+char * imalloc(int n);
+void * irealloc(void * pointer, int size);
+void icfree(char * pointer);
+void ifree(char * pointer);
+const char * scheck(const char * string, const char * format);
/*
** Finally, some convenience items.
@@ -193,6 +220,24 @@ char * scheck(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 */
+
+/*
+** 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.
@@ -201,7 +246,8 @@ char * scheck(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 */
/*
@@ -235,11 +281,11 @@ char * scheck(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
@@ -252,6 +298,26 @@ char * scheck(const char *string, const char *format);
char *asctime_r(struct tm const *, char *);
char *ctime_r(time_t const *, char *);
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR 31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
/*
** UNIX was a registered trademark of The Open Group in 2003.
diff --git a/lib/libc/stdtime/strftime.c b/lib/libc/stdtime/strftime.c
index b0ba1ff..ac85830 100644
--- a/lib/libc/stdtime/strftime.c
+++ b/lib/libc/stdtime/strftime.c
@@ -7,7 +7,7 @@
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley. The name of the
+ * by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
@@ -17,7 +17,7 @@
#ifndef lint
#ifndef NOID
-static const char elsieid[] = "@(#)strftime.c 7.64";
+static const char elsieid[] = "@(#)strftime.3 8.3";
/*
** Based on the UCB version with the ID appearing below.
** This is ANSIish only when "multibyte character == plain character".
@@ -42,10 +42,9 @@ __FBSDID("$FreeBSD$");
static char * _add(const char *, char *, const char *);
static char * _conv(int, const char *, char *, const char *);
-static char * _fmt(const char *, const struct tm *, char *, const char *, int *);
-
-size_t strftime(char * __restrict, size_t, const char * __restrict,
- const struct tm * __restrict);
+static char * _fmt(const char *, const struct tm *, char *, const char *,
+ int *);
+static char * _yconv(int, int, int, int, char *, const char *);
extern char * tzname[];
@@ -53,7 +52,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
@@ -170,8 +168,8 @@ label:
** something completely different.
** (ado, 1993-05-24)
*/
- pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
- fmt_padding[PAD_FMT_CENTURY][PadIndex], pt, ptlim);
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
+ pt, ptlim);
continue;
case 'c':
{
@@ -240,7 +238,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)
*/
@@ -261,7 +259,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)
*/
@@ -340,7 +338,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)
@@ -353,17 +351,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)
*/
{
int year;
+ int base;
int yday;
int wday;
int w;
- year = t->tm_year + TM_YEAR_BASE;
+ year = t->tm_year;
+ base = TM_YEAR_BASE;
yday = t->tm_yday;
wday = t->tm_wday;
for ( ; ; ) {
@@ -371,7 +371,7 @@ label:
int bot;
int top;
- len = isleap(year) ?
+ len = isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
/*
@@ -390,7 +390,7 @@ label:
top += DAYSPERWEEK;
top += len;
if (yday >= top) {
- ++year;
+ ++base;
w = 1;
break;
}
@@ -399,26 +399,26 @@ label:
DAYSPERWEEK);
break;
}
- --year;
- yday += isleap(year) ?
+ --base;
+ yday += isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
}
#ifdef XPG4_1994_04_09
- if ((w == 52
- && t->tm_mon == TM_JANUARY)
- || (w == 1
- && t->tm_mon == TM_DECEMBER))
- w = 53;
+ if ((w == 52 &&
+ t->tm_mon == TM_JANUARY) ||
+ (w == 1 &&
+ t->tm_mon == TM_DECEMBER))
+ w = 53;
#endif /* defined XPG4_1994_04_09 */
if (*format == 'V')
pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],
pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
- pt = _conv(year % 100, fmt_padding[PAD_FMT_SHORTYEAR][PadIndex],
+ pt = _yconv(year, base, 0, 1,
pt, ptlim);
- } else pt = _conv(year, fmt_padding[PAD_FMT_YEAR][PadIndex],
+ } else pt = _yconv(year, base, 1, 1,
pt, ptlim);
}
continue;
@@ -456,12 +456,11 @@ label:
continue;
case 'y':
*warnp = IN_ALL;
- pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
- fmt_padding[PAD_FMT_SHORTYEAR][PadIndex], pt, ptlim);
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
+ pt, ptlim);
continue;
case 'Y':
- pt = _conv(t->tm_year + TM_YEAR_BASE,
- fmt_padding[PAD_FMT_YEAR][PadIndex],
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
pt, ptlim);
continue;
case 'Z':
@@ -492,12 +491,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
@@ -526,8 +525,10 @@ label:
diff = -diff;
} else sign = "+";
pt = _add(sign, pt, ptlim);
- diff /= 60;
- pt = _conv((diff/60)*100 + diff%60,
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 +
+ (diff % MINSPERHOUR);
+ pt = _conv(diff,
fmt_padding[PAD_FMT_YEAR][PadIndex], pt, ptlim);
}
continue;
@@ -553,7 +554,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:
@@ -590,3 +591,44 @@ const char * const ptlim;
++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 *
+_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;
+{
+ 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;
+}
diff --git a/lib/libc/stdtime/time2posix.3 b/lib/libc/stdtime/time2posix.3
index ce9c68d..e7c0f01 100644
--- a/lib/libc/stdtime/time2posix.3
+++ b/lib/libc/stdtime/time2posix.3
@@ -118,3 +118,6 @@ degenerate to the identity function.
.Xr localtime 3 ,
.Xr mktime 3 ,
.Xr time 3
+.\" @(#)time2posix.3 8.1
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
diff --git a/lib/libc/stdtime/tzfile.5 b/lib/libc/stdtime/tzfile.5
index cc7e659..15625d2 100644
--- a/lib/libc/stdtime/tzfile.5
+++ b/lib/libc/stdtime/tzfile.5
@@ -14,7 +14,9 @@ begin with the magic characters
.Dq Li TZif
to identify them as
time zone information files,
-followed by sixteen bytes reserved for future use,
+followed by a character identifying the version of the file's format
+(as of 2005, either an ASCII NUL or a '2')
+followed by fifteen bytes containing zeroes reserved for future use,
followed by four four-byte values
written in a ``standard'' byte order
(the high-order byte of the value is written first).
@@ -56,7 +58,9 @@ each one tells which of the different types of ``local time'' types
described in the file is associated with the same-indexed transition time.
These values serve as indices into an array of
.Fa ttinfo
-structures that appears next in the file;
+structures (with
+.Fa tzh_typecnt
+entries) that appears next in the file;
these structures are defined as follows:
.Pp
.Bd -literal -offset indent
@@ -129,10 +133,20 @@ if either
.Li tzh_timecnt
is zero or the time argument is less than the first transition time recorded
in the file.
+.Pp
+For version-2-format time zone files,
+the above header and data is followed by a second header and data,
+identical in format except that eight bytes are used for each
+transition time or leap second time.
+After the second header and data comes a newline-enclosed,
+POSIX-TZ-environment-variable-style string for use in handling instants
+after the last transition time stored in the file
+(with nothing between the newlines if there is no POSIX representation for
+such instants).
.Sh SEE ALSO
.Xr ctime 3 ,
.Xr time2posix 3 ,
.Xr zic 8
-.\" @(#)tzfile.5 7.2
+.\" @(#)tzfile.5 8.2
.\" This file is in the public domain, so clarified as of
-.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+.\" 1996-06-05 by Arthur David Olson.
diff --git a/lib/libc/stdtime/tzfile.h b/lib/libc/stdtime/tzfile.h
index 89c5694..85b945e 100644
--- a/lib/libc/stdtime/tzfile.h
+++ b/lib/libc/stdtime/tzfile.h
@@ -4,7 +4,7 @@
/*
** This file is in the public domain, so clarified as of
-** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
**
** $FreeBSD$
*/
@@ -24,7 +24,7 @@
#ifndef lint
#ifndef NOID
/*
-static char tzfilehid[] = "@(#)tzfile.h 7.14";
+static char tzfilehid[] = "@(#)tzfile.h 8.1";
*/
#endif /* !defined NOID */
#endif /* !defined lint */
@@ -52,8 +52,9 @@ static char tzfilehid[] = "@(#)tzfile.h 7.14";
#define TZ_MAGIC "TZif"
struct tzhead {
- char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_reserved[16]; /* reserved for future use */
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' as of 2005 */
+ char tzh_reserved[15]; /* reserved--must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
@@ -88,18 +89,22 @@ struct tzhead {
*/
/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+*/
+
+/*
** In the current implementation, "tzset()" refuses to deal with files that
** exceed any of the limits below.
*/
#ifndef TZ_MAX_TIMES
-/*
-** The TZ_MAX_TIMES value below is enough to handle a bit more than a
-** year's worth of solar time (corrected daily to the nearest second) or
-** 138 years of Pacific Presidential Election time
-** (where there are three time zone transitions every fourth year).
-*/
-#define TZ_MAX_TIMES 370
+#define TZ_MAX_TIMES 1200
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
@@ -109,7 +114,7 @@ struct tzhead {
#ifdef NOSOLAR
/*
** Must be at least 14 for Europe/Riga as of Jan 12 1995,
-** as noted by Earl Chew <earl@hpato.aus.hp.com>.
+** as noted by Earl Chew.
*/
#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
#endif /* !defined NOSOLAR */
@@ -160,33 +165,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 */
OpenPOWER on IntegriCloud