From 10d002160949c985e6f99fb8d647d5e3c67ef554 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Wed, 17 Apr 2002 21:23:31 +0000 Subject: [PATCH] 2002-04-17 Jeff Johnston * libc/include/time.h (tzset, _tzset_r): Added prototypes. (strptime): Moved prototype to be within !__STRICT_ANSI__. (_tzname, _daylight, _timezone): No long __CYGWIN__ only. (tzname): Defined for all platforms. (daylight, timezone): Defined only for CYGWIN. * libc/sys/linux/machine/i386/crt0.c: Add call to tzset() after environment set up. * libc/stdlib/setenv_r.c (_setenv_r): Call tzset() if the TZ environment variable is set. * libc/time/Makefile.am: Add support for tzset.c, tzlock.c, and tzset_r.c. * libc/time/Makefile.in: Regenerated. * libc/time/gmtime.c (gmtime): Changed to call gmtime_r. * libc/time/gmtime_r.c (gmtime_r): Changed to call _mktm_r. * libc/time/lcltime_r.c (lcltime_r): Ditto. * libc/time/local.h: New local header file. * libc/time/mktime.c (mktime): Add timezone support. * libc/time/mktm_r.c: New file which is the common engine for gmtime_r and lcltime_r. This code has timezone support. * libc/time/strftime.c (strftime): Add %Z timezone support. * libc/time/tzlock.c: New file containing timezone lock stubs. * libc/time/tzset.c: New file containing tzset() routine. * libc/time/tzset_r.c: New file containing _tzset_r and internal routine for calculating timezone changes for specified year. --- newlib/ChangeLog | 27 +++ newlib/libc/include/time.h | 18 +- newlib/libc/stdlib/setenv_r.c | 12 +- newlib/libc/sys/linux/machine/i386/crt0.c | 2 + newlib/libc/time/Makefile.am | 12 +- newlib/libc/time/Makefile.in | 21 +- newlib/libc/time/gmtime.c | 5 +- newlib/libc/time/gmtime_r.c | 7 +- newlib/libc/time/lcltime_r.c | 90 +------- newlib/libc/time/local.h | 54 +++++ newlib/libc/time/mktime.c | 48 ++++- newlib/libc/time/mktm_r.c | 248 ++++++++++++++++++++++ newlib/libc/time/strftime.c | 24 ++- newlib/libc/time/tzlock.c | 44 ++++ newlib/libc/time/tzset.c | 72 +++++++ newlib/libc/time/tzset_r.c | 204 ++++++++++++++++++ 16 files changed, 770 insertions(+), 118 deletions(-) create mode 100644 newlib/libc/time/local.h create mode 100644 newlib/libc/time/mktm_r.c create mode 100644 newlib/libc/time/tzlock.c create mode 100644 newlib/libc/time/tzset.c create mode 100644 newlib/libc/time/tzset_r.c diff --git a/newlib/ChangeLog b/newlib/ChangeLog index d9d773e7a..c92650712 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,30 @@ +2002-04-17 Jeff Johnston + + * libc/include/time.h (tzset, _tzset_r): Added prototypes. + (strptime): Moved prototype to be within !__STRICT_ANSI__. + (_tzname, _daylight, _timezone): No long __CYGWIN__ only. + (tzname): Defined for all platforms. + (daylight, timezone): Defined only for CYGWIN. + * libc/sys/linux/machine/i386/crt0.c: Add call to tzset() after + environment set up. + * libc/stdlib/setenv_r.c (_setenv_r): Call tzset() if the TZ + environment variable is set. + * libc/time/Makefile.am: Add support for tzset.c, tzlock.c, and + tzset_r.c. + * libc/time/Makefile.in: Regenerated. + * libc/time/gmtime.c (gmtime): Changed to call gmtime_r. + * libc/time/gmtime_r.c (gmtime_r): Changed to call _mktm_r. + * libc/time/lcltime_r.c (lcltime_r): Ditto. + * libc/time/local.h: New local header file. + * libc/time/mktime.c (mktime): Add timezone support. + * libc/time/mktm_r.c: New file which is the common engine + for gmtime_r and lcltime_r. This code has timezone support. + * libc/time/strftime.c (strftime): Add %Z timezone support. + * libc/time/tzlock.c: New file containing timezone lock stubs. + * libc/time/tzset.c: New file containing tzset() routine. + * libc/time/tzset_r.c: New file containing _tzset_r and + internal routine for calculating timezone changes for specified year. + 2002-04-17 Thomas Fitzsimmons * configure.in (CRT0_DIR): Set to libc/. diff --git a/newlib/libc/include/time.h b/newlib/libc/include/time.h index fe43c285e..c45be5484 100644 --- a/newlib/libc/include/time.h +++ b/newlib/libc/include/time.h @@ -8,6 +8,7 @@ #define _TIME_H_ #include "_ansi.h" +#include #ifdef __cplusplus extern "C" { @@ -55,22 +56,29 @@ struct tm *_EXFUN(gmtime, (const time_t *_timer)); struct tm *_EXFUN(localtime,(const time_t *_timer)); #endif size_t _EXFUN(strftime, (char *_s, size_t _maxsize, const char *_fmt, const struct tm *_t)); -char *_EXFUN(strptime, (const char *, const char *, struct tm *)); char *_EXFUN(asctime_r, (const struct tm *, char *)); char *_EXFUN(ctime_r, (const time_t *, char *)); struct tm *_EXFUN(gmtime_r, (const time_t *, struct tm *)); struct tm *_EXFUN(localtime_r, (const time_t *, struct tm *)); -#ifdef __CYGWIN__ #ifndef __STRICT_ANSI__ +char *_EXFUN(strptime, (const char *, const char *, struct tm *)); +_VOID _EXFUN(tzset, (_VOID)); +_VOID _EXFUN(_tzset_r, (struct _reent *)); + +/* defines for the opengroup specifications Derived from Issue 1 of the SVID. */ extern __IMPORT time_t _timezone; extern __IMPORT int _daylight; extern __IMPORT char *_tzname[2]; -/* defines for the opengroup specifications Derived from Issue 1 of the SVID. */ + +/* POSIX defines the external tzname being defined in time.h */ #ifndef tzname #define tzname _tzname #endif + +/* CYGWIN also exposes daylight and timezone in the name space */ +#ifdef __CYGWIN__ #ifndef daylight #define daylight _daylight #endif @@ -81,13 +89,11 @@ extern __IMPORT char *_tzname[2]; #else char *_EXFUN(timezone, (void)); #endif -void _EXFUN(tzset, (void)); -#endif #endif /* __CYGWIN__ */ +#endif /* !__STRICT_ANSI__ */ #include - #if defined(_POSIX_TIMERS) #include diff --git a/newlib/libc/stdlib/setenv_r.c b/newlib/libc/stdlib/setenv_r.c index 6cc18a868..f06dc6772 100644 --- a/newlib/libc/stdlib/setenv_r.c +++ b/newlib/libc/stdlib/setenv_r.c @@ -1,5 +1,5 @@ /* This file may have been modified by DJ Delorie (Jan 1991). If so, -** these modifications are Copyright (C) 1991 DJ Delorie +** these modifications are Copyright (C) 1991 DJ Delorie. */ /* @@ -26,6 +26,7 @@ #include #include #include +#include #include "envlock.h" extern char **environ; @@ -69,8 +70,11 @@ _DEFUN (_setenv_r, (reent_ptr, name, value, rewrite), } if (strlen (C) >= l_value) { /* old larger; copy over */ - while ((*C++ = *value++) != 0); + while ((*C++ = *value++) != 0); ENV_UNLOCK; + /* if we are changing the TZ environment variable, update timezone info */ + if (strcmp (name, "TZ") == 0) + tzset (); return 0; } } @@ -117,6 +121,10 @@ _DEFUN (_setenv_r, (reent_ptr, name, value, rewrite), ENV_UNLOCK; + /* if we are setting the TZ environment variable, update timezone info */ + if (strcmp (name, "TZ") == 0) + tzset (); + return 0; } diff --git a/newlib/libc/sys/linux/machine/i386/crt0.c b/newlib/libc/sys/linux/machine/i386/crt0.c index 420a2b92c..01ae7a397 100644 --- a/newlib/libc/sys/linux/machine/i386/crt0.c +++ b/newlib/libc/sys/linux/machine/i386/crt0.c @@ -8,6 +8,7 @@ #include +#include extern char **environ; @@ -28,5 +29,6 @@ void _start(int args) char **argv = (char **) (params+1); environ = argv+argc+1; + tzset(); /* initialize timezone info */ exit(main(argc,argv,environ)); } diff --git a/newlib/libc/time/Makefile.am b/newlib/libc/time/Makefile.am index 3a7fbdb19..3735def7c 100644 --- a/newlib/libc/time/Makefile.am +++ b/newlib/libc/time/Makefile.am @@ -16,9 +16,13 @@ LIB_SOURCES = \ lcltime.c \ lcltime_r.c \ mktime.c \ + mktm_r.c \ strftime.c \ - strptime.c \ - time.c + strptime.c \ + time.c \ + tzlock.c \ + tzset.c \ + tzset_r.c libtime_la_LDFLAGS = -Xcompiler -nostdlib @@ -43,7 +47,9 @@ CHEWOUT_FILES = \ lcltime.def \ mktime.def \ strftime.def \ - time.def + time.def \ + tzlock.def \ + tzset.def SUFFIXES = .def diff --git a/newlib/libc/time/Makefile.in b/newlib/libc/time/Makefile.in index a6fb0ba8c..dcbac1878 100644 --- a/newlib/libc/time/Makefile.in +++ b/newlib/libc/time/Makefile.in @@ -107,9 +107,13 @@ LIB_SOURCES = \ lcltime.c \ lcltime_r.c \ mktime.c \ + mktm_r.c \ strftime.c \ - strptime.c \ - time.c + strptime.c \ + time.c \ + tzlock.c \ + tzset.c \ + tzset_r.c libtime_la_LDFLAGS = -Xcompiler -nostdlib @@ -130,7 +134,9 @@ CHEWOUT_FILES = \ lcltime.def \ mktime.def \ strftime.def \ - time.def + time.def \ + tzlock.def \ + tzset.def SUFFIXES = .def @@ -151,15 +157,16 @@ LIBS = @LIBS@ lib_a_LIBADD = @USE_LIBTOOL_FALSE@lib_a_OBJECTS = asctime.o asctime_r.o clock.o \ @USE_LIBTOOL_FALSE@ctime.o ctime_r.o difftime.o gmtime.o gmtime_r.o \ -@USE_LIBTOOL_FALSE@lcltime.o lcltime_r.o mktime.o strftime.o strptime.o \ -@USE_LIBTOOL_FALSE@time.o +@USE_LIBTOOL_FALSE@lcltime.o lcltime_r.o mktime.o mktm_r.o strftime.o \ +@USE_LIBTOOL_FALSE@strptime.o time.o tzlock.o tzset.o tzset_r.o LTLIBRARIES = $(noinst_LTLIBRARIES) libtime_la_LIBADD = @USE_LIBTOOL_TRUE@libtime_la_OBJECTS = asctime.lo asctime_r.lo clock.lo \ @USE_LIBTOOL_TRUE@ctime.lo ctime_r.lo difftime.lo gmtime.lo gmtime_r.lo \ -@USE_LIBTOOL_TRUE@lcltime.lo lcltime_r.lo mktime.lo strftime.lo \ -@USE_LIBTOOL_TRUE@strptime.lo time.lo +@USE_LIBTOOL_TRUE@lcltime.lo lcltime_r.lo mktime.lo mktm_r.lo \ +@USE_LIBTOOL_TRUE@strftime.lo strptime.lo time.lo tzlock.lo tzset.lo \ +@USE_LIBTOOL_TRUE@tzset_r.lo CFLAGS = @CFLAGS@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) diff --git a/newlib/libc/time/gmtime.c b/newlib/libc/time/gmtime.c index 4f5bbe403..d8e687559 100644 --- a/newlib/libc/time/gmtime.c +++ b/newlib/libc/time/gmtime.c @@ -59,9 +59,8 @@ struct tm * _DEFUN (gmtime, (tim_p), _CONST time_t * tim_p) { - time_t tim = *tim_p + _GMT_OFFSET; - - return (localtime (&tim)); + _REENT_CHECK_TM(_REENT); + return gmtime_r (tim_p, (struct tm *)_REENT_TM(_REENT)); } #endif diff --git a/newlib/libc/time/gmtime_r.c b/newlib/libc/time/gmtime_r.c index 7afa021c5..fb39238d3 100644 --- a/newlib/libc/time/gmtime_r.c +++ b/newlib/libc/time/gmtime_r.c @@ -3,15 +3,12 @@ */ #include - -#define _GMT_OFFSET 0 +#include "local.h" struct tm * _DEFUN (gmtime_r, (tim_p, res), _CONST time_t * tim_p _AND struct tm *res) { - time_t tim = *tim_p + _GMT_OFFSET; - - return (localtime_r (&tim, res)); + return (_mktm_r (tim_p, res, 1)); } diff --git a/newlib/libc/time/lcltime_r.c b/newlib/libc/time/lcltime_r.c index 1b4269ee1..cf386719c 100644 --- a/newlib/libc/time/lcltime_r.c +++ b/newlib/libc/time/lcltime_r.c @@ -1,104 +1,18 @@ /* * localtime_r.c - * Original Author: Adapted from tzcode maintained by Arthur David Olson. * * Converts the calendar time pointed to by tim_p into a broken-down time * expressed as local time. Returns a pointer to a structure containing the * broken-down time. */ -#include #include - -#define SECSPERMIN 60L -#define MINSPERHOUR 60L -#define HOURSPERDAY 24L -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) -#define DAYSPERWEEK 7 -#define MONSPERYEAR 12 - -#define YEAR_BASE 1900 -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY 4 - -#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) - -static _CONST int mon_lengths[2][MONSPERYEAR] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -} ; - -static _CONST int year_lengths[2] = { - 365, - 366 -} ; +#include "local.h" struct tm * _DEFUN (localtime_r, (tim_p, res), _CONST time_t * tim_p _AND struct tm *res) { - long days, rem; - int y; - int yleap; - _CONST int *ip; - - days = ((long) *tim_p) / SECSPERDAY; - rem = ((long) *tim_p) % SECSPERDAY; - while (rem < 0) - { - rem += SECSPERDAY; - --days; - } - while (rem >= SECSPERDAY) - { - rem -= SECSPERDAY; - ++days; - } - - /* compute hour, min, and sec */ - res->tm_hour = (int) (rem / SECSPERHOUR); - rem %= SECSPERHOUR; - res->tm_min = (int) (rem / SECSPERMIN); - res->tm_sec = (int) (rem % SECSPERMIN); - - /* compute day of week */ - if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) - res->tm_wday += DAYSPERWEEK; - - /* compute year & day of year */ - y = EPOCH_YEAR; - if (days >= 0) - { - for (;;) - { - yleap = isleap(y); - if (days < year_lengths[yleap]) - break; - y++; - days -= year_lengths[yleap]; - } - } - else - { - do - { - --y; - yleap = isleap(y); - days += year_lengths[yleap]; - } while (days < 0); - } - - res->tm_year = y - YEAR_BASE; - res->tm_yday = days; - ip = mon_lengths[yleap]; - for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) - days -= ip[res->tm_mon]; - res->tm_mday = days + 1; - - /* set daylight saving time flag */ - res->tm_isdst = -1; - - return (res); + return _mktm_r (tim_p, res, 0); } diff --git a/newlib/libc/time/local.h b/newlib/libc/time/local.h new file mode 100644 index 000000000..722808715 --- /dev/null +++ b/newlib/libc/time/local.h @@ -0,0 +1,54 @@ +/* local header used by libc/time routines */ +#include <_ansi.h> +#include + +#define SECSPERMIN 60L +#define MINSPERHOUR 60L +#define HOURSPERDAY 24L +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) +#define DAYSPERWEEK 7 +#define MONSPERYEAR 12 + +#define YEAR_BASE 1900 +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY 4 +#define EPOCH_YEARS_SINCE_LEAP 2 +#define EPOCH_YEARS_SINCE_CENTURY 70 +#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370 + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + +extern time_t __tzstart_std; +extern time_t __tzstart_dst; +extern int __tznorth; +extern int __tzyear; + +typedef struct __tzrule_struct +{ + char ch; + int m; + int n; + int d; + int s; + time_t change; + int offset; +} __tzrule_type; + +extern __tzrule_type __tzrule[2]; + +struct tm * _EXFUN (_mktm_r, (_CONST time_t *, struct tm *, int __is_gmtime)); +int _EXFUN (__tzcalc_limits, (int __year)); + +/* locks for multi-threading */ +#ifdef __SINGLE_THREAD__ +#define TZ_LOCK +#define TZ_UNLOCK +#else +#define TZ_LOCK __tz_lock() +#define TZ_UNLOCK __tz_unlock() +#endif + +void _EXFUN(__tz_lock,(_VOID)); +void _EXFUN(__tz_unlock,(_VOID)); + diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/mktime.c index 80478dae9..f6f080d84 100644 --- a/newlib/libc/time/mktime.c +++ b/newlib/libc/time/mktime.c @@ -47,6 +47,7 @@ ANSI C requires <>. #include #include +#include "local.h" #define _SEC_IN_MINUTE 60L #define _SEC_IN_HOUR 3600L @@ -156,7 +157,7 @@ mktime (tim_p) { time_t tim = 0; long days = 0; - int year; + int year, isdst; /* validate structure */ validate_structure (tim_p); @@ -200,5 +201,50 @@ mktime (tim_p) /* compute total seconds */ tim += (days * _SEC_IN_DAY); + isdst = tim_p->tm_isdst; + + if (_daylight) + { + int y = tim_p->tm_year + YEAR_BASE; + if (y == __tzyear || __tzcalc_limits (y)) + { + /* calculate start of dst in dst local time and + start of std in both std local time and dst local time */ + time_t startdst_dst = __tzrule[0].change - __tzrule[1].offset; + time_t startstd_dst = __tzrule[1].change - __tzrule[1].offset; + time_t startstd_std = __tzrule[1].change - __tzrule[0].offset; + /* if the time is in the overlap between dst and std local times */ + if (tim >= startstd_std && tim < startstd_dst) + ; /* we let user decide or leave as -1 */ + else + { + isdst = (__tznorth + ? (tim >= startdst_dst && tim < startstd_std) + : (tim >= startdst_dst || tim < startstd_std)); + /* if user committed and was wrong, perform correction */ + if ((isdst ^ tim_p->tm_isdst) == 1) + { + /* we either subtract or add the difference between + time zone offsets, depending on which way the user got it wrong */ + int diff = __tzrule[0].offset - __tzrule[1].offset; + if (!isdst) + diff = -diff; + tim_p->tm_sec += diff; + validate_structure (tim_p); + tim += diff; /* we also need to correct our current time calculation */ + } + } + } + } + + /* add appropriate offset to put time in gmt format */ + if (isdst == 1) + tim += __tzrule[1].offset; + else /* otherwise assume std time */ + tim += __tzrule[0].offset; + + /* reset isdst flag to what we have calculated */ + tim_p->tm_isdst = isdst; + return tim; } diff --git a/newlib/libc/time/mktm_r.c b/newlib/libc/time/mktm_r.c new file mode 100644 index 000000000..4fdfb9b7b --- /dev/null +++ b/newlib/libc/time/mktm_r.c @@ -0,0 +1,248 @@ +/* + * mktm_r.c + * Original Author: Adapted from tzcode maintained by Arthur David Olson. + * Modifications: Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston + * + * Converts the calendar time pointed to by tim_p into a broken-down time + * expressed as local time. Returns a pointer to a structure containing the + * broken-down time. + */ + +#include +#include +#include "local.h" + +static _CONST int mon_lengths[2][MONSPERYEAR] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +} ; + +static _CONST int year_lengths[2] = { + 365, + 366 +} ; + +struct tm * +_DEFUN (_mktm_r, (tim_p, res, is_gmtime), + _CONST time_t * tim_p _AND + struct tm *res _AND + int is_gmtime) +{ + long days, rem; + time_t lcltime; + int i; + int y; + int yleap; + _CONST int *ip; + + /* base decision about std/dst time on current time */ + lcltime = *tim_p; + + days = ((long)lcltime) / SECSPERDAY; + rem = ((long)lcltime) % SECSPERDAY; + while (rem < 0) + { + rem += SECSPERDAY; + --days; + } + while (rem >= SECSPERDAY) + { + rem -= SECSPERDAY; + ++days; + } + + /* compute hour, min, and sec */ + res->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + res->tm_min = (int) (rem / SECSPERMIN); + res->tm_sec = (int) (rem % SECSPERMIN); + + /* compute day of week */ + if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) + res->tm_wday += DAYSPERWEEK; + + /* compute year & day of year */ + y = EPOCH_YEAR; + if (days >= 0) + { + for (;;) + { + yleap = isleap(y); + if (days < year_lengths[yleap]) + break; + y++; + days -= year_lengths[yleap]; + } + } + else + { + do + { + --y; + yleap = isleap(y); + days += year_lengths[yleap]; + } while (days < 0); + } + + res->tm_year = y - YEAR_BASE; + res->tm_yday = days; + ip = mon_lengths[yleap]; + for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) + days -= ip[res->tm_mon]; + res->tm_mday = days + 1; + + if (!is_gmtime) + { + int offset; + int hours, mins, secs; + + TZ_LOCK; + if (_daylight) + { + if (y == __tzyear || __tzcalc_limits (y)) + res->tm_isdst = (__tznorth + ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change) + : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change)); + else + res->tm_isdst = -1; + } + else + res->tm_isdst = 0; + + offset = (res->tm_isdst == 1 ? __tzrule[1].offset : __tzrule[0].offset); + + hours = offset / SECSPERHOUR; + offset = offset % SECSPERHOUR; + + mins = offset / SECSPERMIN; + secs = offset % SECSPERMIN; + + res->tm_sec -= secs; + res->tm_min -= mins; + res->tm_hour -= hours; + + if (res->tm_sec >= SECSPERMIN) + { + res->tm_min += 1; + res->tm_sec -= SECSPERMIN; + } + else if (res->tm_sec < 0) + { + res->tm_min -= 1; + res->tm_sec += SECSPERMIN; + } + if (res->tm_min >= MINSPERHOUR) + { + res->tm_hour += 1; + res->tm_min -= MINSPERHOUR; + } + else if (res->tm_min < 0) + { + res->tm_hour -= 1; + res->tm_min += MINSPERHOUR; + } + if (res->tm_hour >= HOURSPERDAY) + { + ++res->tm_yday; + ++res->tm_wday; + if (res->tm_wday > 6) + res->tm_wday = 0; + ++res->tm_mday; + res->tm_hour -= HOURSPERDAY; + if (res->tm_mday >= ip[res->tm_mon]) + { + res->tm_mday -= ip[res->tm_mon] - 1; + res->tm_mon += 1; + if (res->tm_mon == 12) + { + res->tm_mon = 0; + res->tm_year += 1; + res->tm_yday = 0; + } + } + } + else if (res->tm_hour < 0) + { + res->tm_yday -= 1; + res->tm_wday -= 1; + if (res->tm_wday < 0) + res->tm_wday = 6; + res->tm_mday -= 1; + res->tm_hour += 24; + if (res->tm_mday == 0) + { + res->tm_mon -= 1; + if (res->tm_mon < 0) + { + res->tm_mon = 11; + res->tm_year -= 1; + res->tm_yday = 365 + isleap(res->tm_year); + } + res->tm_mday = ip[res->tm_mon]; + } + } + TZ_UNLOCK; + } + else + res->tm_isdst = 0; + + return (res); +} + +int +_DEFUN (__tzcalc_limits, (year), + int year) +{ + int days, year_days, years; + int i, j; + + if (year < EPOCH_YEAR) + return 0; + + __tzyear = year; + + years = (year - EPOCH_YEAR); + + year_days = years * 365 + + (years - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - (years - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 + + (years - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400; + + for (i = 0; i < 2; ++i) + { + if (__tzrule[i].ch == 'J') + days = year_days + __tzrule[i].d + (isleap(year) && __tzrule[i].d >= 60); + else if (__tzrule[i].ch == 'D') + days = year_days + __tzrule[i].d; + else + { + int yleap = isleap(year); + int m_day, m_wday, wday_diff; + _CONST int *ip = mon_lengths[yleap]; + + days = year_days; + + for (j = 1; j < __tzrule[i].m; ++j) + days += ip[j-1]; + + m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK; + + wday_diff = __tzrule[i].d - m_wday; + if (wday_diff < 0) + wday_diff += DAYSPERWEEK; + m_day = (__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff; + + while (m_day >= ip[j]) + m_day -= DAYSPERWEEK; + + days += m_day; + } + + /* store the change-over time in GMT form by adding offset */ + __tzrule[i].change = days * SECSPERDAY + __tzrule[i].s + __tzrule[i].offset; + } + + __tznorth = (__tzrule[0].change < __tzrule[1].change); + + return 1; +} + diff --git a/newlib/libc/time/strftime.c b/newlib/libc/time/strftime.c index 1b44e1db9..57fe74eae 100644 --- a/newlib/libc/time/strftime.c +++ b/newlib/libc/time/strftime.c @@ -115,9 +115,9 @@ o %Y The full year, formatted with four digits to include the century. o %Z -Defined by ANSI C as eliciting the time zone if available; it is not -available in this implementation (which accepts `<<%Z>>' but generates -no output for it). +The time zone name. If tm_isdst is -1, no output is generated. +Otherwise, the time zone name based on the TZ environment variable +is used. o %% A single character, `<<%>>'. @@ -142,6 +142,7 @@ ANSI C requires <>, but does not specify the contents of #include #include #include +#include "local.h" static _CONST int dname_len[7] = {6, 6, 7, 9, 8, 6, 8}; @@ -426,6 +427,23 @@ _DEFUN (strftime, (s, maxsize, format, tim_p), return 0; break; case 'Z': + if (tim_p->tm_isdst >= 0) + { + int size; + TZ_LOCK; + size = strlen(_tzname[tim_p->tm_isdst]); + for (i = 0; i < size; i++) + { + if (count < maxsize - 1) + s[count++] = _tzname[tim_p->tm_isdst][i]; + else + { + TZ_UNLOCK; + return 0; + } + } + TZ_UNLOCK; + } break; case '%': if (count < maxsize - 1) diff --git a/newlib/libc/time/tzlock.c b/newlib/libc/time/tzlock.c new file mode 100644 index 000000000..66fa1c366 --- /dev/null +++ b/newlib/libc/time/tzlock.c @@ -0,0 +1,44 @@ +/* +FUNCTION +<<__tz_lock>>, <<__tz_unlock>>--lock time zone global variables + +INDEX + __tz_lock +INDEX + __tz_unlock + +ANSI_SYNOPSIS + #include "local.h" + void __tz_lock (void); + void __tz_unlock (void); + +TRAD_SYNOPSIS + void __tz_lock(); + void __tz_unlock(); + +DESCRIPTION +The <> facility functions call these functions when they need +to ensure the values of global variables. The version of these routines supplied +in the library do not do anything. If multiple threads of execution +can call the time functions and give up scheduling in the middle, then you +you need to define your own versions of these functions in order to +safely lock the time zone variables during a call. If you do not, the results +of <>, <>, <>, and <> are undefined. + +The lock <<__tz_lock>> may not be called recursively; that is, +a call <<__tz_lock>> will always lock all subsequent <<__tz_lock>> calls +until the corresponding <<__tz_unlock>> call on the same thread is made. +*/ + +#include <_ansi.h> +#include "local.h" + +_VOID +_DEFUN_VOID (__tz_lock) +{ +} + +_VOID +_DEFUN_VOID (__tz_unlock) +{ +} diff --git a/newlib/libc/time/tzset.c b/newlib/libc/time/tzset.c new file mode 100644 index 000000000..c70e3ee6e --- /dev/null +++ b/newlib/libc/time/tzset.c @@ -0,0 +1,72 @@ +/* +FUNCTION +<>---set timezone characteristics from TZ environment variable + +INDEX + tzset + +ANSI_SYNOPSIS + #include + void tzset(void); + void _tzset_r (struct _reent *); + +TRAD_SYNOPSIS + #include + void tzset(); + void _tzset_r (reent_ptr) + struct _reent *reent_ptr; + +DESCRIPTION +<> examines the TZ environment variable and sets up the three +external variables: <<_timezone>>, <<_daylight>>, and <>. The +value of <<_timezone>> shall be the offset from the current time zone +to GMT. The value of <<_daylight>> shall be 0 if there is no daylight +savings time for the current time zone, otherwise it will be non-zero. +The <> array has two entries: the first is the name of the +standard time zone, the second is the name of the daylight-savings time +zone. + +The TZ environment variable is expected to be in the following POSIX +format: + + stdoffset1[dst[offset2][,start[/time1],end[/time2]]] + +where: std is the name of the standard time-zone (minimum 3 chars) + offset1 is the value to add to local time to arrive at Universal time + it has the form: hh[:mm[:ss]] + dst is the name of the alternate (daylight-savings) time-zone (min 3 chars) + offset2 is the value to add to local time to arrive at Universal time + it has the same format as the std offset + start is the day that the alternate time-zone starts + time1 is the optional time that the alternate time-zone starts + (this is in local time and defaults to 02:00:00 if not specified) + end is the day that the alternate time-zone ends + time2 is the time that the alternate time-zone ends + (it is in local time and defaults to 02:00:00 if not specified) + +Note that there is no white-space padding between fields. Also note that +if TZ is null, the default is Universal GMT which has no daylight-savings +time. If TZ is empty, the default EST5EDT is used. + +The function <<_tzset_r>> is identical to <> only it is reentrant +and is used for applications that use multiple threads. + +RETURNS +There is no return value. + +PORTABILITY +<> is part of the POSIX standard. + +Supporting OS subroutine required: None +*/ + +#include <_ansi.h> +#include +#include +#include "local.h" + +_VOID +_DEFUN_VOID (tzset) +{ + _tzset_r (_REENT); +} diff --git a/newlib/libc/time/tzset_r.c b/newlib/libc/time/tzset_r.c new file mode 100644 index 000000000..a083e5b80 --- /dev/null +++ b/newlib/libc/time/tzset_r.c @@ -0,0 +1,204 @@ +#include <_ansi.h> +#include +#include +#include +#include +#include +#include +#include "local.h" + +static char __tzname_std[11]; +static char __tzname_dst[11]; +static char *prev_tzenv = NULL; + +/* default to GMT */ +char *_tzname[2] = {"GMT" "GMT"}; +int _daylight = 0; +time_t _timezone = (time_t)0; + +int __tzyear = 0; + +int __tznorth = 1; + +__tzrule_type __tzrule[2] = { {'J', 0, 0, 0, 0, (time_t)0, 0 }, + {'J', 0, 0, 0, 0, (time_t)0, 0 } }; + +_VOID +_DEFUN (_tzset_r, (reent_ptr), + struct _reent *reent_ptr) +{ + char *tzenv; + int hh, mm, ss, sign, m, w, d, n; + int i, ch; + + if ((tzenv = _getenv_r (reent_ptr, "TZ")) == NULL) + { + TZ_LOCK; + _timezone = (time_t)0; + _daylight = 0; + _tzname[0] = "GMT"; + _tzname[1] = "GMT"; + TZ_UNLOCK; + return; + } + + TZ_LOCK; + + if (prev_tzenv != NULL && strcmp(tzenv, prev_tzenv) == 0) + { + TZ_UNLOCK; + return; + } + + free(prev_tzenv); + prev_tzenv = _strdup_r (reent_ptr, tzenv); + + /* ignore implementation-specific format specifier */ + if (*tzenv == ':') + ++tzenv; + + if (sscanf (tzenv, "%10[^0-9,+-]%n", __tzname_std, &n) <= 0) + { + TZ_UNLOCK; + return; + } + + tzenv += n; + + sign = 1; + if (*tzenv == '-') + { + sign = -1; + ++tzenv; + } + else if (*tzenv == '+') + ++tzenv; + + mm = 0; + ss = 0; + + if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) < 1) + { + TZ_UNLOCK; + return; + } + + __tzrule[0].offset = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh); + _tzname[0] = __tzname_std; + tzenv += n; + + if (sscanf (tzenv, "%10[^0-9,+-]%n", __tzname_dst, &n) <= 0) + { + _tzname[1] = _tzname[0]; + TZ_UNLOCK; + return; + } + else + _tzname[1] = __tzname_dst; + + tzenv += n; + + /* otherwise we have a dst name, look for the offset */ + sign = 1; + if (*tzenv == '-') + { + sign = -1; + ++tzenv; + } + else if (*tzenv == '+') + ++tzenv; + + hh = 0; + mm = 0; + ss = 0; + + if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0) + __tzrule[1].offset = __tzrule[0].offset - 3600; + else + __tzrule[1].offset = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh); + + tzenv += n; + + if (*tzenv == ',') + ++tzenv; + + for (i = 0; i < 2; ++i) + { + if (*tzenv == 'M') + { + if (sscanf (tzenv, "M%hu%n.%hu%n.%hu%n", &m, &n, &w, &n, &d, &n) != 3 || + m < 1 || m > 12 || w < 1 || w > 5 || d > 6) + { + TZ_UNLOCK; + return; + } + + __tzrule[i].ch = 'M'; + __tzrule[i].m = m; + __tzrule[i].n = w; + __tzrule[i].d = d; + + tzenv += n; + } + else + { + char *end; + if (*tzenv == 'J') + { + ch = 'J'; + ++tzenv; + } + else + ch = 'D'; + + d = strtoul (tzenv, &end, 10); + + /* if unspecified, default to US settings */ + if (end == tzenv) + { + if (i == 0) + { + __tzrule[0].ch = 'M'; + __tzrule[0].m = 4; + __tzrule[0].n = 1; + __tzrule[0].d = 0; + } + else + { + __tzrule[1].ch = 'M'; + __tzrule[1].m = 10; + __tzrule[1].n = 5; + __tzrule[1].d = 0; + } + } + else + { + __tzrule[i].ch = ch; + __tzrule[i].d = d; + } + + tzenv = end; + } + + /* default time is 02:00:00 am */ + hh = 2; + mm = 0; + ss = 0; + + if (*tzenv == '/') + sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n); + + __tzrule[i].s = ss + SECSPERMIN * mm + SECSPERHOUR * hh; + } + + __tzcalc_limits (__tzyear); + _timezone = (time_t)(__tzrule[0].offset); + _daylight = __tzrule[0].offset != __tzrule[1].offset; + + TZ_UNLOCK; +} + + + + +