summaryrefslogtreecommitdiff
path: root/libc/misc/time/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/misc/time/time.c')
-rw-r--r--libc/misc/time/time.c231
1 files changed, 145 insertions, 86 deletions
diff --git a/libc/misc/time/time.c b/libc/misc/time/time.c
index 8176e071a..ac2fe5926 100644
--- a/libc/misc/time/time.c
+++ b/libc/misc/time/time.c
@@ -1,31 +1,10 @@
-/* Copyright (C) 2002 Manuel Novoa III
+/* Copyright (C) 2002-2004 Manuel Novoa III <mjn3@codepoet.org>
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * GNU Library General Public License (LGPL) version 2 or later.
*
- * This 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
*/
-/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
- *
- * Besides uClibc, I'm using this code in my libc for elks, which is
- * a 16-bit environment with a fairly limited compiler. It would make
- * things much easier for me if this file isn't modified unnecessarily.
- * In particular, please put any new or replacement functions somewhere
- * else, and modify the makefile to use your version instead.
- * Thanks. Manuel
- *
- * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
-
/* June 15, 2002 Initial Notes:
*
* Note: It is assumed throught that time_t is either long or unsigned long.
@@ -132,6 +111,13 @@
* Dec 14, 2003 Fix some dst issues in _time_mktime().
* Normalize the tm_isdst value to -1, 0, or 1.
* If no dst for this timezone, then reset tm_isdst to 0.
+ *
+ * May 7, 2004
+ * Change clock() to allow wrapping.
+ * Add timegm() function.
+ * Make lookup_tzname() static (as it should have been).
+ * Have strftime() get timezone information from the passed struct
+ * for the %z and %Z conversions when using struct tm extensions.
*/
#define _GNU_SOURCE
@@ -219,6 +205,13 @@ extern struct tm *_time_t2tm(const time_t *__restrict timer,
extern time_t _time_mktime(struct tm *timeptr, int store_on_success);
+extern struct tm *__time_localtime_tzi(const time_t *__restrict timer,
+ struct tm *__restrict result,
+ rule_struct *tzi);
+
+extern time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success,
+ rule_struct *tzi);
+
/**********************************************************************/
#ifdef L_asctime
@@ -376,51 +369,63 @@ char *asctime_r(register const struct tm *__restrict ptm,
#include <sys/times.h>
-/* Note: According to glibc...
- * CAE XSH, Issue 4, Version 2: <time.h>
- * The value of CLOCKS_PER_SEC is required to be 1 million on all
- * XSI-conformant systems.
- */
-
#ifndef __BCC__
#if CLOCKS_PER_SEC != 1000000L
#error unexpected value for CLOCKS_PER_SEC!
#endif
#endif
+#ifdef __UCLIBC_CLK_TCK_CONST
+# if __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC
+# error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
+# elif __UCLIBC_CLK_TCK_CONST < 1
+# error __UCLIBC_CLK_TCK_CONST < 1!
+# endif
+#endif
+
+/* Note: SUSv3 notes
+ *
+ * On XSI-conformant systems, CLOCKS_PER_SEC is defined to be one million.
+ *
+ * The value returned by clock() may wrap around on some implementations.
+ * For example, on a machine with 32-bit values for clock_t, it wraps
+ * after 2147 seconds.
+ *
+ * This implies that we should bitwise and with LONG_MAX.
+ */
+
clock_t clock(void)
{
struct tms xtms;
unsigned long t;
times(&xtms);
+
t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
#ifndef __UCLIBC_CLK_TCK_CONST
-#error __UCLIBC_CLK_TCK_CONST not defined!
-#endif
-#undef CLK_TCK
-#define CLK_TCK __UCLIBC_CLK_TCK_CONST
+# error __UCLIBC_CLK_TCK_CONST not defined!
-#if CLK_TCK > CLOCKS_PER_SEC
-#error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
-#elif CLK_TCK < 1
-#error __UCLIBC_CLK_TCK_CONST < 1!
-#endif
+#elif ((CLOCKS_PER_SEC % __UCLIBC_CLK_TCK_CONST) == 0)
+
+ /* CLOCKS_PER_SEC == k * __UCLIBC_CLK_TCK_CONST for some integer k >= 1. */
+ return ((t * (CLOCKS_PER_SEC/__UCLIBC_CLK_TCK_CONST)) & LONG_MAX);
-#if (CLK_TCK == CLOCKS_PER_SEC)
- return (t <= LONG_MAX) ? t : -1;
-#elif (CLOCKS_PER_SEC % CLK_TCK) == 0
- return (t <= (LONG_MAX / (CLOCKS_PER_SEC/CLK_TCK)))
- ? t * (CLOCKS_PER_SEC/CLK_TCK)
- : -1;
#else
- return (t <= ((LONG_MAX / CLOCKS_PER_SEC) * CLK_TCK
- + ((LONG_MAX % CLOCKS_PER_SEC) * CLK_TCK) / CLOCKS_PER_SEC))
- ? (((t / CLK_TCK) * CLOCKS_PER_SEC)
- + (((t % CLK_TCK) * CLOCKS_PER_SEC) / CLK_TCK))
- : -1;
+
+ /* Unlike the previous case, the scaling factor is not an integer.
+ * So when tms_utime, tms_stime, or their sum wraps, some of the
+ * "visible" bits in the return value are affected. Nothing we
+ * can really do about this though other than handle tms_utime and
+ * tms_stime seperately and then sum. But since that doesn't really
+ * buy us much, we don't bother. */
+
+ return ((((t / __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
+ + ((((t % __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
+ / __UCLIBC_CLK_TCK_CONST))
+ ) & LONG_MAX);
+
#endif
}
@@ -525,6 +530,24 @@ struct tm *localtime(const time_t *timer)
/**********************************************************************/
#ifdef L_localtime_r
+struct tm *localtime_r(register const time_t *__restrict timer,
+ register struct tm *__restrict result)
+{
+ TZLOCK;
+
+ tzset();
+
+ __time_localtime_tzi(timer, result, _time_tzinfo);
+
+ TZUNLOCK;
+
+ return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L__time_localtime_tzi
+
#ifdef __UCLIBC_HAS_TM_EXTENSIONS__
struct ll_tzname_item;
@@ -539,7 +562,7 @@ static ll_tzname_item_t ll_tzname[] = {
{ NULL, "???" } /* Always 2nd. (invalid or out-of-memory) */
};
-const char *lookup_tzname(const char *key)
+static const char *lookup_tzname(const char *key)
{
ll_tzname_item_t *p;
@@ -574,9 +597,9 @@ static const unsigned char day_cor[] = { /* non-leap */
/* Note: timezone locking is done by localtime_r. */
-static int tm_isdst(register const struct tm *__restrict ptm)
+static int tm_isdst(register const struct tm *__restrict ptm,
+ register rule_struct *r)
{
- register rule_struct *r = _time_tzinfo;
long sec;
int i, isdst, isleap, day, day0, monlen, mday;
int oday; /* Note: oday can be uninitialized. */
@@ -647,21 +670,18 @@ static int tm_isdst(register const struct tm *__restrict ptm)
return (isdst & 1);
}
-struct tm *localtime_r(register const time_t *__restrict timer,
- register struct tm *__restrict result)
+struct tm *__time_localtime_tzi(register const time_t *__restrict timer,
+ register struct tm *__restrict result,
+ rule_struct *tzi)
{
time_t x[1];
long offset;
int days, dst;
- TZLOCK;
-
- tzset();
-
dst = 0;
do {
days = -7;
- offset = 604800L - _time_tzinfo[dst].gmt_offset;
+ offset = 604800L - tzi[dst].gmt_offset;
if (*timer > (LONG_MAX - 604800L)) {
days = -days;
offset = -offset;
@@ -671,12 +691,11 @@ struct tm *localtime_r(register const time_t *__restrict timer,
_time_t2tm(x, days, result);
result->tm_isdst = dst;
#ifdef __UCLIBC_HAS_TM_EXTENSIONS__
- result->tm_gmtoff = - _time_tzinfo[dst].gmt_offset;
- result->tm_zone = lookup_tzname(_time_tzinfo[dst].tzname);
+ result->tm_gmtoff = - tzi[dst].gmt_offset;
+ result->tm_zone = lookup_tzname(tzi[dst].tzname);
#endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
- } while ((++dst < 2) && ((result->tm_isdst = tm_isdst(result)) != 0));
-
- TZUNLOCK;
+ } while ((++dst < 2)
+ && ((result->tm_isdst = tm_isdst(result, tzi)) != 0));
return result;
}
@@ -694,6 +713,20 @@ time_t mktime(struct tm *timeptr)
return _time_mktime(timeptr, 1);
}
+#endif
+/**********************************************************************/
+#ifdef L_timegm
+/* Like `mktime' but timeptr represents Universal Time, not local time. */
+
+time_t timegm(struct tm *timeptr)
+{
+ rule_struct gmt_tzinfo[2];
+
+ memset(gmt_tzinfo, 0, sizeof(gmt_tzinfo));
+ strcpy(gmt_tzinfo[0].tzname, "GMT"); /* Match glibc behavior here. */
+
+ return _time_mktime_tzi(timeptr, 1, gmt_tzinfo);
+}
#endif
/**********************************************************************/
@@ -913,7 +946,9 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize,
long tzo;
register const char *p;
register const char *o;
+#ifndef __UCLIBC_HAS_TM_EXTENSIONS__
const rule_struct *rsp;
+#endif
const char *stack[MAX_PUSH];
size_t count;
size_t o_count;
@@ -1027,15 +1062,28 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize,
goto OUTPUT;
}
+#ifdef __UCLIBC_HAS_TM_EXTENSIONS__
+
+#define RSP_TZUNLOCK ((void) 0)
+#define RSP_TZNAME timeptr->tm_zone
+#define RSP_GMT_OFFSET timeptr->tm_gmtoff
+
+#else
+
+#define RSP_TZUNLOCK TZUNLOCK
+#define RSP_TZNAME rsp->tzname
+#define RSP_GMT_OFFSET rsp->gmt_offset
+
TZLOCK;
rsp = _time_tzinfo;
if (timeptr->tm_isdst > 0) {
++rsp;
}
+#endif
if (*p == 'Z') {
- o = rsp->tzname;
+ o = RSP_TZNAME;
assert(o != NULL);
#if 0
if (!o) { /* PARANOIA */
@@ -1043,15 +1091,15 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize,
}
#endif
o_count = SIZE_MAX;
- TZUNLOCK;
+ RSP_TZUNLOCK;
goto OUTPUT;
} else { /* z */
*s = '+';
- if ((tzo = -rsp->gmt_offset) < 0) {
+ if ((tzo = -RSP_GMT_OFFSET) < 0) {
tzo = -tzo;
*s = '-';
}
- TZUNLOCK;
+ RSP_TZUNLOCK;
++s;
--count;
@@ -1060,6 +1108,7 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize,
i = 16 + 6; /* 0-fill, width = 4 */
}
+
} else {
/* TODO: don't need year for U, W */
for (i=0 ; i < 3 ; i++) {
@@ -2087,12 +2136,32 @@ struct tm __time_tm; /* Global shared by gmtime() and localtime(). */
/**********************************************************************/
#ifdef L__time_mktime
+time_t _time_mktime(struct tm *timeptr, int store_on_success)
+{
+ time_t t;
+
+ TZLOCK;
+
+ tzset();
+
+ t = _time_mktime_tzi(timeptr, store_on_success, _time_tzinfo);
+
+ TZUNLOCK;
+
+ return t;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L__time_mktime_tzi
+
static const unsigned char vals[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
29,
};
-time_t _time_mktime(struct tm *timeptr, int store_on_success)
+time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success,
+ rule_struct *tzi)
{
#ifdef __BCC__
long days, secs;
@@ -2106,13 +2175,9 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success)
register const unsigned char *s;
int d, default_dst;
- TZLOCK;
-
- tzset();
-
memcpy(p, timeptr, sizeof(struct tm));
- if (!_time_tzinfo[1].tzname[0]) { /* No dst in this timezone, */
+ if (!tzi[1].tzname[0]) { /* No dst in this timezone, */
p[8] = 0; /* so set tm_isdst to 0. */
}
@@ -2150,7 +2215,7 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success)
d = p[5] - 1;
days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
secs = p[0] + 60*( p[1] + 60*((long)(p[2])) )
- + _time_tzinfo[default_dst].gmt_offset;
+ + tzi[default_dst].gmt_offset;
DST_CORRECT:
if (secs < 0) {
secs += 120009600L;
@@ -2165,7 +2230,7 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success)
d = p[5] - 1;
d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
secs = p[0]
- + _time_tzinfo[default_dst].gmt_offset
+ + tzi[default_dst].gmt_offset
+ 60*( p[1]
+ 60*(p[2]
+ 24*(((146073L * ((long long)(p[6])) + d)
@@ -2183,7 +2248,7 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success)
d = ((struct tm *)p)->tm_isdst;
t = secs;
- localtime_r(&t, (struct tm *)p);
+ __time_localtime_tzi(&t, (struct tm *)p, tzi);
if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */
goto DONE;
@@ -2193,8 +2258,8 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success)
#ifdef __BCC__
secs -= (days * 86400L);
#endif
- secs += (_time_tzinfo[1-default_dst].gmt_offset
- - _time_tzinfo[default_dst].gmt_offset);
+ secs += (tzi[1-default_dst].gmt_offset
+ - tzi[default_dst].gmt_offset);
goto DST_CORRECT;
}
@@ -2205,8 +2270,6 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success)
DONE:
- TZUNLOCK;
-
return t;
}
@@ -2254,7 +2317,3 @@ int dysize(int year)
#endif
/**********************************************************************/
-/* Like `mktime', but for TP represents Universal Time, not local time. */
-/* time_t timegm(struct tm *tp) */
-
-