Просмотр исходного кода

Begin the process of reworking the time functions for proper
time zone and locale support (in theory). More work is
still needed.
-Erik

Eric Andersen 24 лет назад
Родитель
Сommit
fd15708e64

+ 2 - 1
libc/misc/time/Makefile

@@ -26,7 +26,8 @@ include $(TOPDIR)Rules.mak
 
 CSRC=localtime.c gmtime.c asctime.c ctime.c asc_conv.c tm_conv.c mktime.c \
 	localtime_r.c gmtime_r.c asctime_r.c ctime_r.c utimes.c adjtime.c \
-	clock.c difftime.c time.c ftime.c strftime.c strptime.c __time_locale.c
+	clock.c difftime.c time.c ftime.c strftime.c strptime.c __time_locale.c \
+	__time_static.c
 
 COBJS=$(patsubst %.c,%.o, $(CSRC))
 OBJS=$(COBJS)

+ 3 - 0
libc/misc/time/__time_locale.c

@@ -1,3 +1,4 @@
+#ifndef __UCLIBC_HAS_LOCALE__
 char const __weekday_name[][10] =
   {
     "Sunday", "Monday", "Tuesday", "Wednesday",
@@ -25,3 +26,5 @@ const unsigned short int __mon_yday[2][13] =
     /* Leap years.  */
     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
   };
+#endif
+

+ 17 - 0
libc/misc/time/__time_static.c

@@ -0,0 +1,17 @@
+
+#include <time.h>
+
+/* These globals are exported by the C library */
+char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
+int __daylight = 0;
+long int __timezone = 0L;
+/* Grumble */
+weak_alias (__tzname, tzname);
+weak_alias (__daylight, daylight);
+weak_alias (__timezone, timezone);
+
+
+/* static data for gmtime() and localtime() */
+struct tm __tmb;
+
+

+ 22 - 11
libc/misc/time/asc_conv.c

@@ -1,4 +1,5 @@
 
+#include <features.h>
 #include <time.h>
 #include <string.h>
 /*
@@ -16,29 +17,39 @@
  *
  * Also fixed day conversion ... ANSI says no leading 0.
  *
+ * Modified	Erik Andersen	    May 2002
+ * Changed to optionally support real locales.
+ *
  */
 
-void __asctime(buffer, ptm)
-register char *buffer;
-struct tm *ptm;
+#ifdef __UCLIBC_HAS_LOCALE__
+/* This is defined in locale/C-time.c in the GNU libc.  */
+extern const struct locale_data _nl_C_LC_TIME;
+extern const unsigned short int __mon_yday[2][13];
+# define __ab_weekday_name \
+  (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
+# define __ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
+#else
+extern char const __ab_weekday_name[][4];
+extern char const __ab_month_name[][4];
+#endif
+
+void __asctime(register char *buffer, struct tm *ptm)
 {
-	static const char days[] = "SunMonTueWedThuFriSat";
-	static const char mons[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-	/*                              012345678901234567890123456 */
-	static const char template[] = "Err Err 00 00:00:00 0000\n";
-	int tm_field[4];
-	int tmp, i;
 	char *p;
+	int tmp, i, tm_field[4];
+	/*                              012345678901234567890123456 */
+	static const char template[] = "??? ??? 00 00:00:00 0000\n";
 
 	/* Since we need memcpy below, use it here instead of strcpy. */
 	memcpy(buffer, template, sizeof(template));
 
 	if ((ptm->tm_wday >= 0) && (ptm->tm_wday <= 6)) {
-		memcpy(buffer, days + 3 * (ptm->tm_wday), 3);
+		memcpy(buffer, __ab_weekday_name[ptm->tm_wday], 3);
 	}
 
 	if ((ptm->tm_mon >= 0) && (ptm->tm_mon <= 11)) {
-		memcpy(buffer + 4, mons + 3 * (ptm->tm_mon), 3);
+		memcpy(buffer + 4, __ab_month_name[ptm->tm_mon], 3);
 	}
 
 	tm_field[0] = ptm->tm_mday;

+ 11 - 8
libc/misc/time/asctime.c

@@ -1,15 +1,18 @@
 
 #include <time.h>
+#include <errno.h>
 
 extern void __asctime();
 
-char *asctime(timeptr)
-__const struct tm *timeptr;
-{
-	static char timebuf[26];
 
-	if (timeptr == 0)
-		return 0;
-	__asctime(timebuf, timeptr);
-	return timebuf;
+char * asctime (__const struct tm *timeptr)
+{
+    static char __time_buf[26];
+    if (timeptr == NULL) {
+	__set_errno (EINVAL);
+	return NULL;
+    }
+    __asctime(__time_buf, timeptr);
+    return __time_buf;
 }
+

+ 9 - 8
libc/misc/time/asctime_r.c

@@ -1,15 +1,16 @@
 
 #include <time.h>
+#include <errno.h>
 
 extern void __asctime();
 
-char *asctime_r(timeptr, buf)
-__const struct tm *timeptr;
-char *buf;
+char *asctime_r(__const struct tm *timeptr, char *buf)
 {
-
-	if (timeptr == 0)
-		return 0;
-	__asctime(buf, timeptr);
-	return buf;
+    if (timeptr == NULL || buf == NULL) {
+	__set_errno (EINVAL);
+	return NULL;
+    }
+    __asctime(buf, timeptr);
+    return buf;
 }
+

+ 7 - 19
libc/misc/time/ctime.c

@@ -2,25 +2,13 @@
 #include <time.h>
 #include <sys/time.h>
 
-extern void __tm_conv();
-extern void __asctime();
-
-char *ctime(timep)
-__const time_t *timep;
+char * ctime (const time_t *t)
 {
-	static char cbuf[26];
-	struct tm tmb;
-	struct timezone tz;
-	time_t offt;
-
-	gettimeofday((void *) 0, &tz);
-
-	offt = -tz.tz_minuteswest * 60L;
 
-	/* tmb.tm_isdst = ? */
-	__tm_conv(&tmb, timep, offt);
-
-	__asctime(cbuf, &tmb);
-
-	return cbuf;
+    /* According to IEEE Std 1003.1-2001: The ctime() function shall
+     * convert the time pointed to by clock, representing time in
+     * seconds since the Epoch, to local time in the form of a string.
+     * It shall be equivalent to: asctime(localtime(clock)) */
+    return asctime (localtime (t));
 }
+

+ 4 - 20
libc/misc/time/ctime_r.c

@@ -2,25 +2,9 @@
 #include <time.h>
 #include <sys/time.h>
 
-extern void __tm_conv();
-extern void __asctime();
-
-char *ctime_r(timep, buf)
-__const time_t *timep;
-char *buf;
+char * ctime_r(const time_t *t, char *buf)
 {
-	struct tm tmb;
-	struct timezone tz;
-	time_t offt;
-
-	gettimeofday((void *) 0, &tz);
-
-	offt = -tz.tz_minuteswest * 60L;
-
-	/* tmb.tm_isdst = ? */
-	__tm_conv(&tmb, timep, offt);
-
-	__asctime(buf, &tmb);
+    struct tm tm;
+    return asctime_r(localtime_r(t, &tm), buf);
+} 
 
-	return buf;
-}

+ 1 - 4
libc/misc/time/difftime.c

@@ -22,10 +22,7 @@
 
 
 /* Return the difference between TIME1 and TIME0.  */
-double
-difftime (time1, time0)
-     time_t time1;
-     time_t time0;
+double difftime (time_t time1, time_t time0)
 {
   /* Algorithm courtesy Paul Eggert (eggert@twinsun.com).  */
 

+ 6 - 7
libc/misc/time/gmtime.c

@@ -3,12 +3,11 @@
 
 extern void __tm_conv();
 
-struct tm *gmtime(timep)
-__const time_t *timep;
-{
-	static struct tm tmb;
-
-	__tm_conv(&tmb, timep, 0L);
+/* Our static data lives in __time_static.c */
+extern struct tm __tmb;
 
-	return &tmb;
+struct tm *gmtime(__const time_t *timep)
+{
+    __tm_conv(&__tmb, timep, 0L);
+    return &__tmb;
 }

+ 10 - 17
libc/misc/time/localtime.c

@@ -2,30 +2,23 @@
 #include <time.h>
 #include <sys/time.h>
 
-/* These globals are exported by the C library */
-char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
-int __daylight = 0;
-long int __timezone = 0L;
-weak_alias (__tzname, tzname);
-weak_alias (__daylight, daylight);
-weak_alias (__timezone, timezone);
+/* Our static data lives in __time_static.c */
+extern struct tm __tmb;
 
 
 extern void __tm_conv();
 
-struct tm *localtime(timep)
-__const time_t *timep;
+struct tm *localtime(__const time_t *timep)
 {
-	static struct tm tmb;
-	struct timezone tz;
-	time_t offt;
+    struct timezone tz;
+    time_t offt;
 
-	gettimeofday((void *) 0, &tz);
+    gettimeofday((void *) 0, &tz);
 
-	offt = -tz.tz_minuteswest * 60L;
+    offt = -tz.tz_minuteswest * 60L;
 
-	/* tmb.tm_isdst = ? */
-	__tm_conv(&tmb, timep, offt);
+    /* tmb.tm_isdst = ? */
+    __tm_conv(&__tmb, timep, offt);
 
-	return &tmb;
+    return &__tmb;
 }

+ 258 - 217
libc/misc/time/mktime.c

@@ -1,92 +1,90 @@
-
-/* This is adapted from glibc */
-/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. */
-
-
-/* Assume that leap seconds are possible, unless told otherwise.
-   If the host has a `zic' command with a -L leapsecondfilename' option,
-   then it supports leap seconds; otherwise it probably doesn't.  */
-#ifndef LEAP_SECONDS_POSSIBLE
-#define LEAP_SECONDS_POSSIBLE 1
-#endif
-
-#include <sys/types.h>			/* Some systems define `time_t' here.  */
+/* Convert a `struct tm' to a time_t value.
+   Copyright (C) 1993, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Paul Eggert (eggert@twinsun.com).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* Define this to have a standalone program to test this implementation of
+   mktime.  */
+
+#include <features.h>
+/* Assume that leap seconds are not possible */
+#undef LEAP_SECONDS_POSSIBLE 
+#include <sys/types.h>		/* Some systems define `time_t' here.  */
 #include <time.h>
-
-#if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
 #include <limits.h>
-#endif
-
-#if DEBUG
-#include <stdio.h>
-#if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
-#include <stdlib.h>
-#endif
-/* Make it work even if the system's libc has its own mktime routine.  */
-#define mktime my_mktime
-#endif							/* DEBUG */
-
-#ifndef __P
-#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
-#define __P(args) args
-#else
-#define __P(args) ()
-#endif							/* GCC.  */
-#endif							/* Not __P.  */
 
+#if 0
 #ifndef CHAR_BIT
-#define CHAR_BIT 8
+# define CHAR_BIT 8
 #endif
 
+/* The extra casts work around common compiler bugs.  */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
+   It is necessary at least when t == time_t.  */
+#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
+			      ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
+#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
+
 #ifndef INT_MIN
-#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
+# define INT_MIN TYPE_MINIMUM (int)
 #endif
 #ifndef INT_MAX
-#define INT_MAX (~0 - INT_MIN)
+# define INT_MAX TYPE_MAXIMUM (int)
 #endif
 
 #ifndef TIME_T_MIN
-#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
-                    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+# define TIME_T_MIN TYPE_MINIMUM (time_t)
 #endif
 #ifndef TIME_T_MAX
-#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+# define TIME_T_MAX TYPE_MAXIMUM (time_t)
 #endif
 
 #define TM_YEAR_BASE 1900
 #define EPOCH_YEAR 1970
 
-#ifndef __isleap
-/* Nonzero if YEAR is a leap year (every 4 years,
-   except every 100th isn't, and every 400th is).  */
-#define __isleap(year)  \
-  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-#endif
 
+/* How many days come before each month (0-12).  */
 extern const unsigned short int __mon_yday[2][13];
 
-static time_t ydhms_tm_diff
 
-__P((int, int, int, int, int, const struct tm *));
-time_t __mktime_internal
-__P((struct tm *, struct tm * (*)(const time_t *, struct tm *), time_t *));
 
 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
    measured in seconds, ignoring leap seconds.
    YEAR uses the same numbering as TM->tm_year.
    All values are in range, except possibly YEAR.
+   If TP is null, return a nonzero value.
    If overflow occurs, yield the low order bits of the correct answer.  */
-static time_t ydhms_tm_diff(year, yday, hour, min, sec, tp)
-int year, yday, hour, min, sec;
-const struct tm *tp;
+static time_t
+__ydhms_tm_diff (int year, int yday, int hour, int min, int sec,
+	       const struct tm *tp)
 {
+    if (!tp)
+	return 1;
+    else
+    {
 	/* Compute intervening leap days correctly even if year is negative.
 	   Take care to avoid int overflow.  time_t overflow is OK, since
 	   only the low order bits of the correct time_t answer are needed.
 	   Don't convert to time_t until after all divisions are done, since
 	   time_t might be unsigned.  */
-	int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - !(year & 3);
-	int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - !(tp->tm_year & 3);
+	int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
+	int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
 	int a100 = a4 / 25 - (a4 % 25 < 0);
 	int b100 = b4 / 25 - (b4 % 25 < 0);
 	int a400 = a100 >> 2;
@@ -94,193 +92,236 @@ const struct tm *tp;
 	int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
 	time_t years = year - (time_t) tp->tm_year;
 	time_t days = (365 * years + intervening_leap_days
-
-				   + (yday - tp->tm_yday));
+		+ (yday - tp->tm_yday));
 	return (60 * (60 * (24 * days + (hour - tp->tm_hour))
-				  + (min - tp->tm_min))
-			+ (sec - tp->tm_sec));
+		    + (min - tp->tm_min))
+		+ (sec - tp->tm_sec));
+    }
 }
 
-
-/* This structure contains all the information about a
-   timezone given in the POSIX standard TZ envariable.  */
-typedef struct
-  {
-    const char *name;
-
-    /* When to change.  */
-    enum { J0, J1, M } type;	/* Interpretation of:  */
-    unsigned short int m, n, d;	/* Month, week, day.  */
-    unsigned int secs;		/* Time of day.  */
-
-    long int offset;		/* Seconds east of GMT (west if < 0).  */
-
-    /* We cache the computed time of change for a
-       given year so we don't have to recompute it.  */
-    time_t change;	/* When to change to this zone.  */
-    int computed_for;	/* Year above is computed for.  */
-  } tz_rule;
-
-/* tz_rules[0] is standard, tz_rules[1] is daylight.  */
-static tz_rule tz_rules[2];
-
-/* Warning -- this function is a stub andd always does UTC
- * no matter what it is given */
-void tzset (void)
+/* Use CONVERT to convert *T to a broken down time in *TP.
+   If *T is out of range for conversion, adjust it so that
+   it is the nearest in-range value and then convert that.  */
+static struct tm *
+__ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
+		time_t *t, struct tm *tp)
 {
-    tz_rules[0].name = tz_rules[1].name = "UTC";
-    tz_rules[0].type = tz_rules[1].type = J0;
-    tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
-    tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
-    tz_rules[0].secs = tz_rules[1].secs = 0;
-    tz_rules[0].offset = tz_rules[1].offset = 0L;
-    tz_rules[0].change = tz_rules[1].change = (time_t) -1;
-    tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
-}
-
+    struct tm *r;
 
+    if (! (r = (*convert) (t, tp)) && *t)
+    {
+	time_t bad = *t;
+	time_t ok = 0;
+	struct tm tm;
 
-static time_t localtime_offset;
+	/* BAD is a known unconvertible time_t, and OK is a known good one.
+	   Use binary search to narrow the range between BAD and OK until
+	   they differ by 1.  */
+	while (bad != ok + (bad < 0 ? -1 : 1))
+	{
+	    time_t mid = *t = (bad < 0
+		    ? bad + ((ok - bad) >> 1)
+		    : ok + ((bad - ok) >> 1));
+	    if ((r = (*convert) (t, tp)))
+	    {
+		tm = *r;
+		ok = mid;
+	    }
+	    else
+		bad = mid;
+	}
 
-/* Convert *TP to a time_t value.  */
-time_t mktime(tp)
-struct tm *tp;
-{
-#ifdef _LIBC
-	/* POSIX.1 8.1.1 requires that whenever mktime() is called, the
-	   time zone names contained in the external variable `tzname' shall
-	   be set as if the tzset() function had been called.  */
-	tzset();
-#endif
+	if (!r && ok)
+	{
+	    /* The last conversion attempt failed;
+	       revert to the most recent successful attempt.  */
+	    *t = ok;
+	    *tp = tm;
+	    r = tp;
+	}
+    }
 
-	return __mktime_internal(tp, localtime_r, &localtime_offset);
+    return r;
 }
 
+
 /* Convert *TP to a time_t value, inverting
    the monotonic and mostly-unit-linear conversion function CONVERT.
    Use *OFFSET to keep track of a guess at the offset of the result,
    compared to what the result would be for UTC without leap seconds.
    If *OFFSET's guess is correct, only one CONVERT call is needed.  */
-time_t __mktime_internal(tp, convert, offset)
-struct tm *tp;
-struct tm *(*convert) __P((const time_t *, struct tm *));
-time_t *offset;
+time_t __mktime_internal (struct tm *tp, 
+	struct tm *(*convert) (const time_t *, struct tm *), time_t *offset)
 {
-	time_t t, dt, t0;
-	struct tm tm;
-
-	/* The maximum number of probes (calls to CONVERT) should be enough
-	   to handle any combinations of time zone rule changes, solar time,
-	   and leap seconds.  Posix.1 prohibits leap seconds, but some hosts
-	   have them anyway.  */
-	int remaining_probes = 4;
-
-	/* Time requested.  Copy it in case CONVERT modifies *TP; this can
-	   occur if TP is localtime's returned value and CONVERT is localtime.  */
-	int sec = tp->tm_sec;
-	int min = tp->tm_min;
-	int hour = tp->tm_hour;
-	int mday = tp->tm_mday;
-	int mon = tp->tm_mon;
-	int year_requested = tp->tm_year;
-	int isdst = tp->tm_isdst;
-
-	/* Ensure that mon is in range, and set year accordingly.  */
-	int mon_remainder = mon % 12;
-	int negative_mon_remainder = mon_remainder < 0;
-	int mon_years = mon / 12 - negative_mon_remainder;
-	int year = year_requested + mon_years;
-
-	/* The other values need not be in range:
-	   the remaining code handles minor overflows correctly,
-	   assuming int and time_t arithmetic wraps around.
-	   Major overflows are caught at the end.  */
-
-	/* Calculate day of year from year, month, and day of month.
-	   The result need not be in range.  */
-	int yday = ((__mon_yday[__isleap(year + TM_YEAR_BASE)]
-				 [mon_remainder + 12 * negative_mon_remainder])
-				+ mday - 1);
-
+    time_t t, dt, t0, t1, t2;
+    struct tm tm;
+
+    /* The maximum number of probes (calls to CONVERT) should be enough
+       to handle any combinations of time zone rule changes, solar time,
+       leap seconds, and oscillations around a spring-forward gap.
+       POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
+    int remaining_probes = 6;
+
+    /* Time requested.  Copy it in case CONVERT modifies *TP; this can
+       occur if TP is localtime's returned value and CONVERT is localtime.  */
+    int sec = tp->tm_sec;
+    int min = tp->tm_min;
+    int hour = tp->tm_hour;
+    int mday = tp->tm_mday;
+    int mon = tp->tm_mon;
+    int year_requested = tp->tm_year;
+    int isdst = tp->tm_isdst;
+
+    /* Ensure that mon is in range, and set year accordingly.  */
+    int mon_remainder = mon % 12;
+    int negative_mon_remainder = mon_remainder < 0;
+    int mon_years = mon / 12 - negative_mon_remainder;
+    int year = year_requested + mon_years;
+
+    /* The other values need not be in range:
+       the remaining code handles minor overflows correctly,
+       assuming int and time_t arithmetic wraps around.
+       Major overflows are caught at the end.  */
+
+    /* Calculate day of year from year, month, and day of month.
+       The result need not be in range.  */
+    int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
+		[mon_remainder + 12 * negative_mon_remainder])
+	    + mday - 1);
+
+    int sec_requested = sec;
 #if LEAP_SECONDS_POSSIBLE
-	/* Handle out-of-range seconds specially,
-	   since ydhms_tm_diff assumes every minute has 60 seconds.  */
-	int sec_requested = sec;
-
-	if (sec < 0)
-		sec = 0;
-	if (59 < sec)
-		sec = 59;
+    /* Handle out-of-range seconds specially,
+       since __ydhms_tm_diff assumes every minute has 60 seconds.  */
+    if (sec < 0)
+	sec = 0;
+    if (59 < sec)
+	sec = 59;
 #endif
 
-	/* Invert CONVERT by probing.  First assume the same offset as last time.
-	   Then repeatedly use the error to improve the guess.  */
-
-	tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
-	tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
-	t0 = ydhms_tm_diff(year, yday, hour, min, sec, &tm);
-
-	for (t = t0 + *offset;
-		 (dt =
-		  ydhms_tm_diff(year, yday, hour, min, sec, (*convert) (&t, &tm)));
-		 t += dt)
-		if (--remaining_probes == 0)
-			return -1;
-
-	/* Check whether tm.tm_isdst has the requested value, if any.  */
-	if (0 <= isdst && 0 <= tm.tm_isdst) {
-		int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
-
-		if (dst_diff) {
-			/* Move two hours in the direction indicated by the disagreement,
-			   probe some more, and switch to a new time if found.
-			   The largest known fallback due to daylight savings is two hours:
-			   once, in Newfoundland, 1988-10-30 02:00 -> 00:00.  */
-			time_t ot = t - 2 * 60 * 60 * dst_diff;
-
-			while (--remaining_probes != 0) {
-				struct tm otm;
-
-				if (!(dt = ydhms_tm_diff(year, yday, hour, min, sec,
-										 (*convert) (&ot, &otm)))) {
-					t = ot;
-					tm = otm;
-					break;
-				}
-				if ((ot += dt) == t)
-					break;		/* Avoid a redundant probe.  */
-			}
-		}
+    /* Invert CONVERT by probing.  First assume the same offset as last time.
+       Then repeatedly use the error to improve the guess.  */
+
+    tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
+    tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+    t0 = __ydhms_tm_diff (year, yday, hour, min, sec, &tm);
+
+    for (t = t1 = t2 = t0 + *offset;
+	    (dt = __ydhms_tm_diff (year, yday, hour, min, sec,
+				 __ranged_convert (convert, &t, &tm)));
+	    t1 = t2, t2 = t, t += dt)
+	if (t == t1 && t != t2
+		&& (isdst < 0 || tm.tm_isdst < 0
+		    || (isdst != 0) != (tm.tm_isdst != 0)))
+	    /* We can't possibly find a match, as we are oscillating
+	       between two values.  The requested time probably falls
+	       within a spring-forward gap of size DT.  Follow the common
+	       practice in this case, which is to return a time that is DT
+	       away from the requested time, preferring a time whose
+	       tm_isdst differs from the requested value.  In practice,
+	       this is more useful than returning -1.  */
+	    break;
+	else if (--remaining_probes == 0)
+	    return -1;
+
+    /* If we have a match, check whether tm.tm_isdst has the requested
+       value, if any.  */
+    if (dt == 0 && isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
+    {
+	/* tm.tm_isdst has the wrong value.  Look for a neighboring
+	   time with the right value, and use its UTC offset.
+Heuristic: probe the previous three calendar quarters (approximately),
+looking for the desired isdst.  This isn't perfect,
+but it's good enough in practice.  */
+	int quarter = 7889238; /* seconds per average 1/4 Gregorian year */
+	int i;
+
+	/* If we're too close to the time_t limit, look in future quarters.  */
+	if (t < TIME_T_MIN + 3 * quarter)
+	    quarter = -quarter;
+
+	for (i = 1; i <= 3; i++)
+	{
+	    time_t ot = t - i * quarter;
+	    struct tm otm;
+	    __ranged_convert (convert, &ot, &otm);
+	    if (otm.tm_isdst == isdst)
+	    {
+		/* We found the desired tm_isdst.
+		   Extrapolate back to the desired time.  */
+		t = ot + __ydhms_tm_diff (year, yday, hour, min, sec, &otm);
+		__ranged_convert (convert, &t, &tm);
+		break;
+	    }
 	}
+    }
 
-	*offset = t - t0;
+    *offset = t - t0;
 
 #if LEAP_SECONDS_POSSIBLE
-	if (sec_requested != tm.tm_sec) {
-		/* Adjust time to reflect the tm_sec requested, not the normalized value.
-		   Also, repair any damage from a false match due to a leap second.  */
-		t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
-		(*convert) (&t, &tm);
-	}
+    if (sec_requested != tm.tm_sec)
+    {
+	/* Adjust time to reflect the tm_sec requested, not the normalized value.
+	   Also, repair any damage from a false match due to a leap second.  */
+	t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
+	if (! (*convert) (&t, &tm))
+	    return -1;
+    }
 #endif
 
-#if 0
-	if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) {
-		/* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
-		   so check for major overflows.  A gross check suffices,
-		   since if t has overflowed, it is off by a multiple of
-		   TIME_T_MAX - TIME_T_MIN + 1.  So ignore any component of
-		   the difference that is bounded by a small value.  */
-
-		double dyear = (double) year_requested + mon_years - tm.tm_year;
-		double dday = 366 * dyear + mday;
-		double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
-
-		if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? -dsec : dsec))
-			return -1;
-	}
-#endif
+    if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
+    {
+	/* time_t isn't large enough to rule out overflows in __ydhms_tm_diff,
+	   so check for major overflows.  A gross check suffices,
+	   since if t has overflowed, it is off by a multiple of
+	   TIME_T_MAX - TIME_T_MIN + 1.  So ignore any component of
+	   the difference that is bounded by a small value.  */
+
+	double dyear = (double) year_requested + mon_years - tm.tm_year;
+	double dday = 366 * dyear + mday;
+	double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
+
+	/* On Irix4.0.5 cc, dividing TIME_T_MIN by 3 does not produce
+	   correct results, ie., it erroneously gives a positive value
+	   of 715827882.  Setting a variable first then doing math on it
+	   seems to work.  (ghazi@caip.rutgers.edu) */
+
+	const time_t time_t_max = TIME_T_MAX;
+	const time_t time_t_min = TIME_T_MIN;
+
+	if (time_t_max / 3 - time_t_min / 3 < (dsec < 0 ? - dsec : dsec))
+	    return -1;
+    }
 
-	*tp = tm;
-	return t;
+    *tp = tm;
+    return t;
 }
+
+
+
+/* Convert *TP to a time_t value.  */
+time_t mktime (struct tm *tp)
+{
+    static time_t localtime_offset;
+    /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
+       time zone names contained in the external variable `tzname' shall
+       be set as if the tzset() function had been called.  */
+    tzset ();
+
+    return __mktime_internal (tp, localtime_r, &localtime_offset);
+}
+#else
+
+/* Convert *TP to a time_t value.  */
+time_t mktime (struct tm *tp)
+{
+    time_t m_secs=tp->tm_min*60;
+    time_t h_secs=tp->tm_hour*3600;
+    time_t d_secs=tp->tm_yday*86400;
+    time_t y_secs=(tp->tm_year-70)*31536000;
+    time_t l_secs1=((tp->tm_year-69)/4)*86400;
+    time_t l_secs2=((tp->tm_year-1)/100)*86400;
+    time_t l_secs3=((tp->tm_year+299)/400)*86400;
+    return m_secs+h_secs+d_secs+y_secs+l_secs1-l_secs2+l_secs3+tp->tm_gmtoff;
+}
+#endif

+ 20 - 13
libc/misc/time/strftime.c

@@ -84,7 +84,7 @@ static unsigned int week(const struct tm *const tp , int starting_day , int max_
 		: base + 1 + dl / 7;
 }
 
-#ifndef _NL_CURRENT
+#ifndef __UCLIBC_HAS_LOCALE__
 extern char const __weekday_name[][10];
 extern char const __month_name[][10];
 #endif
@@ -98,7 +98,7 @@ extern char const __month_name[][10];
 size_t strftime( char *s , size_t maxsize , const char *format , register const struct tm *tp)
 {
   int hour12 = tp->tm_hour;
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
   const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
   const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
   const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
@@ -133,15 +133,17 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
   /* Initialize the buffer we will use for the sprintf format for numbers.  */
   number_fmt[0] = '%';
 
-  zone = 0;
-#if HAVE_TM_ZONE
+  /* The POSIX test suite assumes that setting
+     the environment variable TZ to a new value before calling strftime()
+     will influence the result (the %Z format) even if the information in
+     TP is computed with a totally different time zone.
+     This is bogus: though POSIX allows bad behavior like this,
+     POSIX does not require it.  Do the right thing instead.  */
   zone = (const char *) tp->tm_zone;
-#endif
-  if (!(zone && *zone) && tp->tm_isdst >= 0)
-    zone = tzname[tp->tm_isdst];
-  if (!(zone && *zone))
-    zone = "???";
-
+  /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
+     time zone names contained in the external variable `tzname' shall
+     be set as if the tzset() function had been called.  */
+  tzset ();
   zonelen = strlen (zone);
 
   if (hour12 > 12)
@@ -218,7 +220,7 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
 	  break;
 
 	case 'c':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 	  subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
 #else
 	  subfmt = "%a %b %d %H:%M:%S %Z %Y";
@@ -241,7 +243,7 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
 	  DO_NUMBER (2, (1900 + tp->tm_year) / 100);
 
 	case 'x':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 	  subfmt = _NL_CURRENT (LC_TIME, D_FMT);
 	  goto subformat;
 #endif
@@ -341,7 +343,7 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
 	  DO_NUMBER (2, tp->tm_sec);
 
 	case 'X':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 	  subfmt = _NL_CURRENT (LC_TIME, T_FMT);
 	  goto subformat;
 #endif
@@ -373,6 +375,11 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
 	  DO_NUMBER (2, tp->tm_year % 100);
 
 	case 'Z':
+	  /* The tzset() call might have changed the value.  */
+	  if (!(zone && *zone) && tp->tm_isdst >= 0)
+	    zone = tzname[tp->tm_isdst];
+	  if (! zone)
+	    zone = "";		/* POSIX.2 requires the empty string here.  */
 	  cpy(zonelen, zone);
 	  break;
 

+ 20 - 14
libc/misc/time/strptime.c

@@ -23,13 +23,13 @@
    some of them in the same format (such as year, week and weekday)
    this is enough information for determining the date.  */
 
+#include <features.h>
 #include <ctype.h>
 #include <langinfo.h>
 #include <limits.h>
 #include <string.h>
 #include <time.h>
 
-#undef _NL_CURRENT
 #undef ENABLE_ERA_JUNK
 
 
@@ -62,7 +62,7 @@
     if (val < from || val > to)						      \
       return NULL;							      \
   } while (0)
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 # define get_alt_number(from, to, n) \
   ({									      \
      __label__ do_normal;						      \
@@ -95,7 +95,7 @@
    && (rp = __strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
 
 
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 /* This is defined in locale/C-time.c in the GNU libc.  */
 extern const struct locale_data _nl_C_LC_TIME;
 extern const unsigned short int __mon_yday[2][13];
@@ -184,14 +184,16 @@ static char * __strptime_internal (rp, fmt, tm, decided, era_cnt)
     int have_mon, have_mday;
     int have_uweek, have_wweek;
     int week_no;
+#ifdef ENABLE_ERA_JUNK
     size_t num_eras;
     struct era_entry *era;
 
+    era = NULL;
+#endif
     have_I = is_pm = 0;
     century = -1;
     want_century = 0;
     want_era = 0;
-    era = NULL;
     week_no = 0;
 
     have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
@@ -218,7 +220,7 @@ static char * __strptime_internal (rp, fmt, tm, decided, era_cnt)
 	}
 
 	++fmt;
-#ifndef _NL_CURRENT
+#ifndef __UCLIBC_HAS_LOCALE__
 	/* We need this for handling the `E' modifier.  */
 start_over:
 #endif
@@ -237,7 +239,7 @@ start_over:
 		/* Match day of week.  */
 		for (cnt = 0; cnt < 7; ++cnt)
 		{
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		    if (*decided !=raw)
 		    {
 			if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
@@ -278,7 +280,7 @@ start_over:
 		/* Match month name.  */
 		for (cnt = 0; cnt < 12; ++cnt)
 		{
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		    if (*decided !=raw)
 		    {
 			if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
@@ -314,7 +316,7 @@ start_over:
 		break;
 	    case 'c':
 		/* Match locale's date and time format.  */
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
@@ -341,7 +343,9 @@ start_over:
 		break;
 	    case 'C':
 		/* Match century number.  */
+#ifdef __UCLIBC_HAS_LOCALE__
 match_century:
+#endif
 		get_number (0, 99, 2);
 		century = val;
 		want_xday = 1;
@@ -360,7 +364,7 @@ match_century:
 		want_xday = 1;
 		break;
 	    case 'x':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
@@ -429,7 +433,7 @@ match_century:
 		break;
 	    case 'p':
 		/* Match locale's equivalent of AM/PM.  */
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		{
 		    if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
@@ -455,7 +459,7 @@ match_century:
 			return NULL;
 		break;
 	    case 'r':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
@@ -511,7 +515,7 @@ match_century:
 		tm->tm_sec = val;
 		break;
 	    case 'X':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
@@ -575,7 +579,9 @@ match_century:
 		have_wday = 1;
 		break;
 	    case 'y':
+#ifdef __UCLIBC_HAS_LOCALE__
 match_year_in_century:
+#endif
 		/* Match year within century.  */
 		get_number (0, 99, 2);
 		/* The "Year 2000: The Millennium Rollover" paper suggests that
@@ -596,7 +602,7 @@ match_year_in_century:
 		/* XXX How to handle this?  */
 		break;
 	    case 'E':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		switch (*fmt++)
 		{
 		    case 'c':
@@ -968,7 +974,7 @@ char * strptime (buf, format, tm)
 {
     enum locale_status decided;
 
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
     decided = not;
 #else
     decided = raw;

+ 42 - 71
libc/misc/time/tm_conv.c

@@ -1,79 +1,50 @@
 
-#if 0
-#include <time.h>
-
-/* This is a translation from ALGOL in Collected Algorithms of CACM. */
-/* Copied from Algorithm 199, Author: Robert G. Tantzen */
-
-void __tm_conv(tmbuf, timep, offset)
-struct tm *tmbuf;
-time_t *timep;
-time_t offset;
-{
-	static int moffset[] =
-		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+/* This is adapted from glibc */
+/* Copyright (C) 1991, 1993 Free Software Foundation, Inc */
 
-	long s;
-	long j, d, m, y;
+#define SECS_PER_HOUR 3600L
+#define SECS_PER_DAY  86400L
 
-	offset += *timep;
+#include <features.h>
+#include <time.h>
+#include <sys/types.h>
 
-	tmbuf->tm_isdst = 0;		/* Someone else can set this */
+/* This structure contains all the information about a
+   timezone given in the POSIX standard TZ envariable.  */
+typedef struct
+{
+    const char *name;
 
-	j = offset / 86400L + 719469;
-	s = offset % 86400L;
+    /* When to change.  */
+    enum { J0, J1, M } type;	/* Interpretation of:  */
+    unsigned short int m, n, d;	/* Month, week, day.  */
+    unsigned int secs;		/* Time of day.  */
 
-	if (s < 0) {
-		s += 86400L;
-		j--;
-	}
+    long int offset;		/* Seconds east of GMT (west if < 0).  */
 
-	tmbuf->tm_sec = s % 60;
-	tmbuf->tm_min = (s / 60) % 60;
-	tmbuf->tm_hour = s / 3600;
-
-	tmbuf->tm_wday = (j + 2) % 7;
-
-	/*
-	 * Julian date converter. Takes a julian date (the number of days since
-	 * some distant epoch or other), and fills tmbuf.
-	 */
-
-	y = (4L * j - 1L) / 146097L;
-	j = 4L * j - 1L - 146097L * y;
-	d = j / 4L;
-	j = (4L * d + 3L) / 1461L;
-	d = 4L * d + 3L - 1461L * j;
-	d = (d + 4L) / 4L;
-	m = (5L * d - 3L) / 153L;
-	d = 5L * d - 3 - 153L * m;
-	d = (d + 5L) / 5L;
-	y = 100L * y + j;
-	if (m < 10)
-		m += 2;
-	else {
-		m -= 10;
-		++y;
-	}
+    /* We cache the computed time of change for a
+       given year so we don't have to recompute it.  */
+    time_t change;	/* When to change to this zone.  */
+    int computed_for;	/* Year above is computed for.  */
+} tz_rule;
 
-	tmbuf->tm_year = y - 1900;
-	tmbuf->tm_mon = m;
-	tmbuf->tm_mday = d;
+/* tz_rules[0] is standard, tz_rules[1] is daylight.  */
+static tz_rule tz_rules[2];
 
-	tmbuf->tm_yday = d + moffset[m];
-	if (m > 1 && ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)))
-		tmbuf->tm_yday++;
+/* Warning -- this function is a stub andd always does UTC
+ * no matter what it is given */
+void tzset (void)
+{
+    tz_rules[0].name = tz_rules[1].name = "UTC";
+    tz_rules[0].type = tz_rules[1].type = J0;
+    tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
+    tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
+    tz_rules[0].secs = tz_rules[1].secs = 0;
+    tz_rules[0].offset = tz_rules[1].offset = 0L;
+    tz_rules[0].change = tz_rules[1].change = (time_t) -1;
+    tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
 }
 
-#else
-
-/* This is adapted from glibc */
-/* Copyright (C) 1991, 1993 Free Software Foundation, Inc */
-
-#define SECS_PER_HOUR 3600L
-#define SECS_PER_DAY  86400L
-
-#include <time.h>
 
 static const unsigned short int __mon_lengths[2][12] = {
 	/* Normal years.  */
@@ -82,11 +53,9 @@ static const unsigned short int __mon_lengths[2][12] = {
 	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
 };
 
-void __tm_conv(tmbuf, t, offset)
-struct tm *tmbuf;
-time_t *t;
-time_t offset;
+void __tm_conv(struct tm *tmbuf, time_t *t, time_t offset)
 {
+	int isdst;
 	long days, rem;
 	register int y;
 	register const unsigned short int *ip;
@@ -128,7 +97,9 @@ time_t offset;
 		days -= ip[y];
 	tmbuf->tm_mon = y;
 	tmbuf->tm_mday = days + 1;
-	tmbuf->tm_isdst = -1;
+	isdst = (*t >= tz_rules[0].change && *t < tz_rules[1].change);
+	tmbuf->tm_isdst = isdst;
+	tmbuf->tm_zone = tzname[isdst];
+
 }
 
-#endif