Bläddra i källkod

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 år sedan
förälder
incheckning
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 \
 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 \
 	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))
 COBJS=$(patsubst %.c,%.o, $(CSRC))
 OBJS=$(COBJS)
 OBJS=$(COBJS)

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

@@ -1,3 +1,4 @@
+#ifndef __UCLIBC_HAS_LOCALE__
 char const __weekday_name[][10] =
 char const __weekday_name[][10] =
   {
   {
     "Sunday", "Monday", "Tuesday", "Wednesday",
     "Sunday", "Monday", "Tuesday", "Wednesday",
@@ -25,3 +26,5 @@ const unsigned short int __mon_yday[2][13] =
     /* Leap years.  */
     /* Leap years.  */
     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
     { 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 <time.h>
 #include <string.h>
 #include <string.h>
 /*
 /*
@@ -16,29 +17,39 @@
  *
  *
  * Also fixed day conversion ... ANSI says no leading 0.
  * 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;
 	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. */
 	/* Since we need memcpy below, use it here instead of strcpy. */
 	memcpy(buffer, template, sizeof(template));
 	memcpy(buffer, template, sizeof(template));
 
 
 	if ((ptm->tm_wday >= 0) && (ptm->tm_wday <= 6)) {
 	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)) {
 	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;
 	tm_field[0] = ptm->tm_mday;

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

@@ -1,15 +1,18 @@
 
 
 #include <time.h>
 #include <time.h>
+#include <errno.h>
 
 
 extern void __asctime();
 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 <time.h>
+#include <errno.h>
 
 
 extern void __asctime();
 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 <time.h>
 #include <sys/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 <time.h>
 #include <sys/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.  */
 /* 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).  */
   /* Algorithm courtesy Paul Eggert (eggert@twinsun.com).  */
 
 

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

@@ -3,12 +3,11 @@
 
 
 extern void __tm_conv();
 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 <time.h>
 #include <sys/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();
 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>
 #include <time.h>
-
-#if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
 #include <limits.h>
 #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
 #ifndef CHAR_BIT
-#define CHAR_BIT 8
+# define CHAR_BIT 8
 #endif
 #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
 #ifndef INT_MIN
-#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
+# define INT_MIN TYPE_MINIMUM (int)
 #endif
 #endif
 #ifndef INT_MAX
 #ifndef INT_MAX
-#define INT_MAX (~0 - INT_MIN)
+# define INT_MAX TYPE_MAXIMUM (int)
 #endif
 #endif
 
 
 #ifndef TIME_T_MIN
 #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
 #endif
 #ifndef TIME_T_MAX
 #ifndef TIME_T_MAX
-#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+# define TIME_T_MAX TYPE_MAXIMUM (time_t)
 #endif
 #endif
 
 
 #define TM_YEAR_BASE 1900
 #define TM_YEAR_BASE 1900
 #define EPOCH_YEAR 1970
 #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];
 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),
 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
    measured in seconds, ignoring leap seconds.
    measured in seconds, ignoring leap seconds.
    YEAR uses the same numbering as TM->tm_year.
    YEAR uses the same numbering as TM->tm_year.
    All values are in range, except possibly 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.  */
    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.
 	/* Compute intervening leap days correctly even if year is negative.
 	   Take care to avoid int overflow.  time_t overflow is OK, since
 	   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.
 	   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
 	   Don't convert to time_t until after all divisions are done, since
 	   time_t might be unsigned.  */
 	   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 a100 = a4 / 25 - (a4 % 25 < 0);
 	int b100 = b4 / 25 - (b4 % 25 < 0);
 	int b100 = b4 / 25 - (b4 % 25 < 0);
 	int a400 = a100 >> 2;
 	int a400 = a100 >> 2;
@@ -94,193 +92,236 @@ const struct tm *tp;
 	int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
 	int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
 	time_t years = year - (time_t) tp->tm_year;
 	time_t years = year - (time_t) tp->tm_year;
 	time_t days = (365 * years + intervening_leap_days
 	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))
 	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
 /* Convert *TP to a time_t value, inverting
    the monotonic and mostly-unit-linear conversion function CONVERT.
    the monotonic and mostly-unit-linear conversion function CONVERT.
    Use *OFFSET to keep track of a guess at the offset of the result,
    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.
    compared to what the result would be for UTC without leap seconds.
    If *OFFSET's guess is correct, only one CONVERT call is needed.  */
    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
 #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
 #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 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
 #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;
 		: base + 1 + dl / 7;
 }
 }
 
 
-#ifndef _NL_CURRENT
+#ifndef __UCLIBC_HAS_LOCALE__
 extern char const __weekday_name[][10];
 extern char const __weekday_name[][10];
 extern char const __month_name[][10];
 extern char const __month_name[][10];
 #endif
 #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)
 size_t strftime( char *s , size_t maxsize , const char *format , register const struct tm *tp)
 {
 {
   int hour12 = tp->tm_hour;
   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 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 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);
   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.  */
   /* Initialize the buffer we will use for the sprintf format for numbers.  */
   number_fmt[0] = '%';
   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;
   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);
   zonelen = strlen (zone);
 
 
   if (hour12 > 12)
   if (hour12 > 12)
@@ -218,7 +220,7 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
 	  break;
 	  break;
 
 
 	case 'c':
 	case 'c':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 	  subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
 	  subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
 #else
 #else
 	  subfmt = "%a %b %d %H:%M:%S %Z %Y";
 	  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);
 	  DO_NUMBER (2, (1900 + tp->tm_year) / 100);
 
 
 	case 'x':
 	case 'x':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 	  subfmt = _NL_CURRENT (LC_TIME, D_FMT);
 	  subfmt = _NL_CURRENT (LC_TIME, D_FMT);
 	  goto subformat;
 	  goto subformat;
 #endif
 #endif
@@ -341,7 +343,7 @@ size_t strftime( char *s , size_t maxsize , const char *format , register const
 	  DO_NUMBER (2, tp->tm_sec);
 	  DO_NUMBER (2, tp->tm_sec);
 
 
 	case 'X':
 	case 'X':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 	  subfmt = _NL_CURRENT (LC_TIME, T_FMT);
 	  subfmt = _NL_CURRENT (LC_TIME, T_FMT);
 	  goto subformat;
 	  goto subformat;
 #endif
 #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);
 	  DO_NUMBER (2, tp->tm_year % 100);
 
 
 	case 'Z':
 	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);
 	  cpy(zonelen, zone);
 	  break;
 	  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)
    some of them in the same format (such as year, week and weekday)
    this is enough information for determining the date.  */
    this is enough information for determining the date.  */
 
 
+#include <features.h>
 #include <ctype.h>
 #include <ctype.h>
 #include <langinfo.h>
 #include <langinfo.h>
 #include <limits.h>
 #include <limits.h>
 #include <string.h>
 #include <string.h>
 #include <time.h>
 #include <time.h>
 
 
-#undef _NL_CURRENT
 #undef ENABLE_ERA_JUNK
 #undef ENABLE_ERA_JUNK
 
 
 
 
@@ -62,7 +62,7 @@
     if (val < from || val > to)						      \
     if (val < from || val > to)						      \
       return NULL;							      \
       return NULL;							      \
   } while (0)
   } while (0)
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 # define get_alt_number(from, to, n) \
 # define get_alt_number(from, to, n) \
   ({									      \
   ({									      \
      __label__ do_normal;						      \
      __label__ do_normal;						      \
@@ -95,7 +95,7 @@
    && (rp = __strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
    && (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.  */
 /* This is defined in locale/C-time.c in the GNU libc.  */
 extern const struct locale_data _nl_C_LC_TIME;
 extern const struct locale_data _nl_C_LC_TIME;
 extern const unsigned short int __mon_yday[2][13];
 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_mon, have_mday;
     int have_uweek, have_wweek;
     int have_uweek, have_wweek;
     int week_no;
     int week_no;
+#ifdef ENABLE_ERA_JUNK
     size_t num_eras;
     size_t num_eras;
     struct era_entry *era;
     struct era_entry *era;
 
 
+    era = NULL;
+#endif
     have_I = is_pm = 0;
     have_I = is_pm = 0;
     century = -1;
     century = -1;
     want_century = 0;
     want_century = 0;
     want_era = 0;
     want_era = 0;
-    era = NULL;
     week_no = 0;
     week_no = 0;
 
 
     have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 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;
 	++fmt;
-#ifndef _NL_CURRENT
+#ifndef __UCLIBC_HAS_LOCALE__
 	/* We need this for handling the `E' modifier.  */
 	/* We need this for handling the `E' modifier.  */
 start_over:
 start_over:
 #endif
 #endif
@@ -237,7 +239,7 @@ start_over:
 		/* Match day of week.  */
 		/* Match day of week.  */
 		for (cnt = 0; cnt < 7; ++cnt)
 		for (cnt = 0; cnt < 7; ++cnt)
 		{
 		{
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		    if (*decided !=raw)
 		    if (*decided !=raw)
 		    {
 		    {
 			if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
 			if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
@@ -278,7 +280,7 @@ start_over:
 		/* Match month name.  */
 		/* Match month name.  */
 		for (cnt = 0; cnt < 12; ++cnt)
 		for (cnt = 0; cnt < 12; ++cnt)
 		{
 		{
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		    if (*decided !=raw)
 		    if (*decided !=raw)
 		    {
 		    {
 			if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
 			if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
@@ -314,7 +316,7 @@ start_over:
 		break;
 		break;
 	    case 'c':
 	    case 'c':
 		/* Match locale's date and time format.  */
 		/* Match locale's date and time format.  */
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		if (*decided != raw)
 		{
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
 		    if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
@@ -341,7 +343,9 @@ start_over:
 		break;
 		break;
 	    case 'C':
 	    case 'C':
 		/* Match century number.  */
 		/* Match century number.  */
+#ifdef __UCLIBC_HAS_LOCALE__
 match_century:
 match_century:
+#endif
 		get_number (0, 99, 2);
 		get_number (0, 99, 2);
 		century = val;
 		century = val;
 		want_xday = 1;
 		want_xday = 1;
@@ -360,7 +364,7 @@ match_century:
 		want_xday = 1;
 		want_xday = 1;
 		break;
 		break;
 	    case 'x':
 	    case 'x':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		if (*decided != raw)
 		{
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
 		    if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
@@ -429,7 +433,7 @@ match_century:
 		break;
 		break;
 	    case 'p':
 	    case 'p':
 		/* Match locale's equivalent of AM/PM.  */
 		/* Match locale's equivalent of AM/PM.  */
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		if (*decided != raw)
 		{
 		{
 		    if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
 		    if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
@@ -455,7 +459,7 @@ match_century:
 			return NULL;
 			return NULL;
 		break;
 		break;
 	    case 'r':
 	    case 'r':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		if (*decided != raw)
 		{
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
 		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
@@ -511,7 +515,7 @@ match_century:
 		tm->tm_sec = val;
 		tm->tm_sec = val;
 		break;
 		break;
 	    case 'X':
 	    case 'X':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		if (*decided != raw)
 		if (*decided != raw)
 		{
 		{
 		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
 		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
@@ -575,7 +579,9 @@ match_century:
 		have_wday = 1;
 		have_wday = 1;
 		break;
 		break;
 	    case 'y':
 	    case 'y':
+#ifdef __UCLIBC_HAS_LOCALE__
 match_year_in_century:
 match_year_in_century:
+#endif
 		/* Match year within century.  */
 		/* Match year within century.  */
 		get_number (0, 99, 2);
 		get_number (0, 99, 2);
 		/* The "Year 2000: The Millennium Rollover" paper suggests that
 		/* The "Year 2000: The Millennium Rollover" paper suggests that
@@ -596,7 +602,7 @@ match_year_in_century:
 		/* XXX How to handle this?  */
 		/* XXX How to handle this?  */
 		break;
 		break;
 	    case 'E':
 	    case 'E':
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
 		switch (*fmt++)
 		switch (*fmt++)
 		{
 		{
 		    case 'c':
 		    case 'c':
@@ -968,7 +974,7 @@ char * strptime (buf, format, tm)
 {
 {
     enum locale_status decided;
     enum locale_status decided;
 
 
-#ifdef _NL_CURRENT
+#ifdef __UCLIBC_HAS_LOCALE__
     decided = not;
     decided = not;
 #else
 #else
     decided = raw;
     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] = {
 static const unsigned short int __mon_lengths[2][12] = {
 	/* Normal years.  */
 	/* 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}
 	{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;
 	long days, rem;
 	register int y;
 	register int y;
 	register const unsigned short int *ip;
 	register const unsigned short int *ip;
@@ -128,7 +97,9 @@ time_t offset;
 		days -= ip[y];
 		days -= ip[y];
 	tmbuf->tm_mon = y;
 	tmbuf->tm_mon = y;
 	tmbuf->tm_mday = days + 1;
 	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