diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 460c9892b31..4459f3e02b1 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -48,11 +48,92 @@ class TimeTestCase(unittest.TestCase): except ValueError: self.fail('conversion specifier: %r failed.' % format) - def test_asctime(self): time.asctime(time.gmtime(self.t)) self.assertRaises(TypeError, time.asctime, 0) + def test_tzset(self): + from os import environ + + # Epoch time of midnight Dec 25th 2002. Never DST in northern + # hemisphere. + xmas2002 = 1040774400.0 + + org_TZ = environ.get('TZ',None) + try: + + # Make sure we can switch to UTC time and results are correct + # Note that unknown timezones default to UTC. + for tz in ('UTC','GMT','Luna/Tycho'): + environ['TZ'] = 'US/Eastern' + time.tzset() + environ['TZ'] = tz + time.tzset() + self.failUnlessEqual( + time.gmtime(xmas2002),time.localtime(xmas2002) + ) + self.failUnlessEqual(time.timezone,time.altzone) + self.failUnlessEqual(time.daylight,0) + self.failUnlessEqual(time.timezone,0) + self.failUnlessEqual(time.altzone,0) + self.failUnlessEqual(time.localtime(xmas2002).tm_isdst,0) + + # Make sure we can switch to US/Eastern + environ['TZ'] = 'US/Eastern' + time.tzset() + self.failIfEqual(time.gmtime(xmas2002),time.localtime(xmas2002)) + self.failUnlessEqual(time.tzname,('EST','EDT')) + self.failUnlessEqual(len(time.tzname),2) + self.failUnlessEqual(time.daylight,1) + self.failUnlessEqual(time.timezone,18000) + self.failUnlessEqual(time.altzone,14400) + self.failUnlessEqual(time.localtime(xmas2002).tm_isdst,0) + self.failUnlessEqual(len(time.tzname),2) + + # Now go to the southern hemisphere. We want somewhere all OS's + # know about that has DST. + environ['TZ'] = 'Australia/Melbourne' + time.tzset() + self.failIfEqual(time.gmtime(xmas2002),time.localtime(xmas2002)) + self.failUnless(time.tzname[0] in ('EST','AEST')) + self.failUnless(time.tzname[1] in ('EST','EDT','AEDT')) + self.failUnlessEqual(len(time.tzname),2) + self.failUnlessEqual(time.daylight,1) + self.failUnlessEqual(time.timezone,-36000) + self.failUnlessEqual(time.altzone,-39600) + self.failUnlessEqual(time.localtime(xmas2002).tm_isdst,1) + + # Get some times from a timezone that isn't wallclock timezone + del environ['TZ'] + time.tzset() + if time.timezone == 0: + environ['TZ'] = 'US/Eastern' + else: + environ['TZ'] = 'UTC' + time.tzset() + nonlocal = time.localtime(xmas2002) + + # Then the same time in wallclock timezone + del environ['TZ'] + time.tzset() + local = time.localtime(xmas2002) + + # And make sure they arn't the same + self.failIfEqual(local,nonlocal) + + # Do some basic sanity checking after wallclock time set + self.failUnlessEqual(len(time.tzname),2) + time.daylight + time.timezone + time.altzone + finally: + # Repair TZ environment variable in case any other tests + # rely on it. + if org_TZ is not None: + environ['TZ'] = org_TZ + elif environ.has_key('TZ'): + del environ['TZ'] + def test_main(): test_support.run_unittest(TimeTestCase) diff --git a/Misc/ACKS b/Misc/ACKS index abc1c75995d..e968bc71116 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -50,6 +50,7 @@ Eric Beser Stephen Bevan Ron Bickers Dominic Binks +Stuart Bishop Roy Bixler Martin Bless Pablo Bleyer diff --git a/Misc/NEWS b/Misc/NEWS index 877fb9e3b3f..0158c0f349a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -32,6 +32,9 @@ Core and builtins Extension modules ----------------- +- New function time.tzset() provides access to the C library tzet() + function, if supported. (SF patch #675422.) + - Using createfilehandler, deletefilehandler, createtimerhandler functions on Tkinter.tkinter (_tkinter module) no longer crashes the interpreter. See SF bug #692416. diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 745ba8c8fbc..fc81ca4069a 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -554,85 +554,63 @@ PyDoc_STRVAR(mktime_doc, Convert a time tuple in local time to seconds since the Epoch."); #endif /* HAVE_MKTIME */ -static PyMethodDef time_methods[] = { - {"time", time_time, METH_VARARGS, time_doc}, -#ifdef HAVE_CLOCK - {"clock", time_clock, METH_VARARGS, clock_doc}, -#endif - {"sleep", time_sleep, METH_VARARGS, sleep_doc}, - {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, - {"localtime", time_localtime, METH_VARARGS, localtime_doc}, - {"asctime", time_asctime, METH_VARARGS, asctime_doc}, - {"ctime", time_ctime, METH_VARARGS, ctime_doc}, -#ifdef HAVE_MKTIME - {"mktime", time_mktime, METH_VARARGS, mktime_doc}, -#endif -#ifdef HAVE_STRFTIME - {"strftime", time_strftime, METH_VARARGS, strftime_doc}, -#endif - {"strptime", time_strptime, METH_VARARGS, strptime_doc}, - {NULL, NULL} /* sentinel */ -}; +#ifdef HAVE_WORKING_TZSET +void inittimezone(PyObject *module); - -PyDoc_STRVAR(module_doc, -"This module provides various functions to manipulate time values.\n\ -\n\ -There are two standard representations of time. One is the number\n\ -of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer\n\ -or a floating point number (to represent fractions of seconds).\n\ -The Epoch is system-defined; on Unix, it is generally January 1st, 1970.\n\ -The actual value can be retrieved by calling gmtime(0).\n\ -\n\ -The other representation is a tuple of 9 integers giving local time.\n\ -The tuple items are:\n\ - year (four digits, e.g. 1998)\n\ - month (1-12)\n\ - day (1-31)\n\ - hours (0-23)\n\ - minutes (0-59)\n\ - seconds (0-59)\n\ - weekday (0-6, Monday is 0)\n\ - Julian day (day in the year, 1-366)\n\ - DST (Daylight Savings Time) flag (-1, 0 or 1)\n\ -If the DST flag is 0, the time is given in the regular time zone;\n\ -if it is 1, the time is given in the DST time zone;\n\ -if it is -1, mktime() should guess based on the date and time.\n\ -\n\ -Variables:\n\ -\n\ -timezone -- difference in seconds between UTC and local standard time\n\ -altzone -- difference in seconds between UTC and local DST time\n\ -daylight -- whether local time should reflect DST\n\ -tzname -- tuple of (standard time zone name, DST time zone name)\n\ -\n\ -Functions:\n\ -\n\ -time() -- return current time in seconds since the Epoch as a float\n\ -clock() -- return CPU time since process start as a float\n\ -sleep() -- delay for a number of seconds given as a float\n\ -gmtime() -- convert seconds since Epoch to UTC tuple\n\ -localtime() -- convert seconds since Epoch to local time tuple\n\ -asctime() -- convert time tuple to string\n\ -ctime() -- convert time in seconds to string\n\ -mktime() -- convert local time tuple to seconds since Epoch\n\ -strftime() -- convert time tuple to string according to format specification\n\ -strptime() -- parse string to time tuple according to format specification"); - - -PyMODINIT_FUNC -inittime(void) +static PyObject * +time_tzset(PyObject *self, PyObject *args) { - PyObject *m; - char *p; - m = Py_InitModule3("time", time_methods, module_doc); + PyObject* m; - /* Accept 2-digit dates unless PYTHONY2K is set and non-empty */ - p = Py_GETENV("PYTHONY2K"); - PyModule_AddIntConstant(m, "accept2dyear", (long) (!p || !*p)); - /* Squirrel away the module's dictionary for the y2k check */ - moddict = PyModule_GetDict(m); - Py_INCREF(moddict); + if (!PyArg_ParseTuple(args, ":tzset")) + return NULL; + + m = PyImport_ImportModule("time"); + if (m == NULL) { + return NULL; + } + + tzset(); + + /* Reset timezone, altzone, daylight and tzname */ + inittimezone(m); + Py_DECREF(m); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(tzset_doc, +"tzset(zone)\n\ +\n\ +Initialize, or reinitialize, the local timezone to the value stored in\n\ +os.environ['TZ']. The TZ environment variable should be specified in\n\ +standard Uniz timezone format as documented in the tzset man page\n\ +(eg. 'US/Eastern', 'Europe/Amsterdam'). Unknown timezones will silently\n\ +fall back to UTC. If the TZ environment variable is not set, the local\n\ +timezone is set to the systems best guess of wallclock time.\n\ +Changing the TZ environment variable without calling tzset *may* change\n\ +the local timezone used by methods such as localtime, but this behaviour\n\ +should not be relied on."); +#endif /* HAVE_WORKING_TZSET */ + +void inittimezone(PyObject *m) { + /* This code moved from inittime wholesale to allow calling it from + time_tzset. In the future, some parts of it can be moved back + (for platforms that don't HAVE_WORKING_TZSET, when we know what they + are), and the extranious calls to tzset(3) should be removed. + I havn't done this yet, as I don't want to change this code as + little as possible when introducing the time.tzset and time.tzsetwall + methods. This should simply be a method of doing the following once, + at the top of this function and removing the call to tzset() from + time_tzset(): + + #ifdef HAVE_TZSET + tzset() + #endif + + And I'm lazy and hate C so nyer. + */ #if defined(HAVE_TZNAME) && !defined(__GLIBC__) && !defined(__CYGWIN__) tzset(); #ifdef PYOS_OS2 @@ -712,6 +690,96 @@ inittime(void) Py_BuildValue("(zz)", _tzname[0], _tzname[1])); #endif /* __CYGWIN__ */ #endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ +} + + +static PyMethodDef time_methods[] = { + {"time", time_time, METH_VARARGS, time_doc}, +#ifdef HAVE_CLOCK + {"clock", time_clock, METH_VARARGS, clock_doc}, +#endif + {"sleep", time_sleep, METH_VARARGS, sleep_doc}, + {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, + {"localtime", time_localtime, METH_VARARGS, localtime_doc}, + {"asctime", time_asctime, METH_VARARGS, asctime_doc}, + {"ctime", time_ctime, METH_VARARGS, ctime_doc}, +#ifdef HAVE_MKTIME + {"mktime", time_mktime, METH_VARARGS, mktime_doc}, +#endif +#ifdef HAVE_STRFTIME + {"strftime", time_strftime, METH_VARARGS, strftime_doc}, +#endif + {"strptime", time_strptime, METH_VARARGS, strptime_doc}, +#ifdef HAVE_WORKING_TZSET + {"tzset", time_tzset, METH_VARARGS, tzset_doc}, +#endif + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module provides various functions to manipulate time values.\n\ +\n\ +There are two standard representations of time. One is the number\n\ +of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer\n\ +or a floating point number (to represent fractions of seconds).\n\ +The Epoch is system-defined; on Unix, it is generally January 1st, 1970.\n\ +The actual value can be retrieved by calling gmtime(0).\n\ +\n\ +The other representation is a tuple of 9 integers giving local time.\n\ +The tuple items are:\n\ + year (four digits, e.g. 1998)\n\ + month (1-12)\n\ + day (1-31)\n\ + hours (0-23)\n\ + minutes (0-59)\n\ + seconds (0-59)\n\ + weekday (0-6, Monday is 0)\n\ + Julian day (day in the year, 1-366)\n\ + DST (Daylight Savings Time) flag (-1, 0 or 1)\n\ +If the DST flag is 0, the time is given in the regular time zone;\n\ +if it is 1, the time is given in the DST time zone;\n\ +if it is -1, mktime() should guess based on the date and time.\n\ +\n\ +Variables:\n\ +\n\ +timezone -- difference in seconds between UTC and local standard time\n\ +altzone -- difference in seconds between UTC and local DST time\n\ +daylight -- whether local time should reflect DST\n\ +tzname -- tuple of (standard time zone name, DST time zone name)\n\ +\n\ +Functions:\n\ +\n\ +time() -- return current time in seconds since the Epoch as a float\n\ +clock() -- return CPU time since process start as a float\n\ +sleep() -- delay for a number of seconds given as a float\n\ +gmtime() -- convert seconds since Epoch to UTC tuple\n\ +localtime() -- convert seconds since Epoch to local time tuple\n\ +asctime() -- convert time tuple to string\n\ +ctime() -- convert time in seconds to string\n\ +mktime() -- convert local time tuple to seconds since Epoch\n\ +strftime() -- convert time tuple to string according to format specification\n\ +strptime() -- parse string to time tuple according to format specification\n\ +tzset() -- change the local timezone"); + + +PyMODINIT_FUNC +inittime(void) +{ + PyObject *m; + char *p; + m = Py_InitModule3("time", time_methods, module_doc); + + /* Accept 2-digit dates unless PYTHONY2K is set and non-empty */ + p = Py_GETENV("PYTHONY2K"); + PyModule_AddIntConstant(m, "accept2dyear", (long) (!p || !*p)); + /* Squirrel away the module's dictionary for the y2k check */ + moddict = PyModule_GetDict(m); + Py_INCREF(moddict); + + /* Set, or reset, module variables like time.timezone */ + inittimezone(m); + #ifdef MS_WINDOWS /* Helper to allow interrupts for Windows. If Ctrl+C event delivered while not sleeping @@ -901,3 +969,5 @@ floatsleep(double secs) return 0; } + + diff --git a/configure b/configure index 2e9574bb515..09337d81ad9 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 1.391 . +# From configure.in Revision: 1.392 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.53 for python 2.3. # @@ -16550,6 +16550,74 @@ _ACEOF fi +# tzset(3) exists and works like we expect it to +echo "$as_me:$LINENO: checking for working tzset()" >&5 +echo $ECHO_N "checking for working tzset()... $ECHO_C" >&6 +if test "${ac_cv_working_tzset+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + +if test "$cross_compiling" = yes; then + ac_cv_working_tzset=no +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +#include +#include +int main() +{ + int gmt_hour; + int eastern_hour; + time_t now; + now = time((time_t*)NULL); + putenv("TZ=GMT"); + tzset(); + gmt_hour = localtime(&now)->tm_hour; + putenv("TZ=US/Eastern"); + tzset(); + eastern_hour = localtime(&now)->tm_hour; + if (eastern_hour == gmt_hour) + exit(1); + exit(0); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_working_tzset=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +( exit $ac_status ) +ac_cv_working_tzset=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi + +echo "$as_me:$LINENO: result: $ac_cv_working_tzset" >&5 +echo "${ECHO_T}$ac_cv_working_tzset" >&6 +if test "$ac_cv_working_tzset" = yes +then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_TZSET 1 +_ACEOF + +fi + # Look for subsecond timestamps in struct stat echo "$as_me:$LINENO: checking for tv_nsec in struct stat" >&5 echo $ECHO_N "checking for tv_nsec in struct stat... $ECHO_C" >&6 diff --git a/configure.in b/configure.in index f73df536837..bcde551048a 100644 --- a/configure.in +++ b/configure.in @@ -2500,6 +2500,39 @@ then [Define if nice() returns success/failure instead of the new priority.]) fi +# tzset(3) exists and works like we expect it to +AC_MSG_CHECKING(for working tzset()) +AC_CACHE_VAL(ac_cv_working_tzset, [ +AC_TRY_RUN([ +#include +#include +int main() +{ + int gmt_hour; + int eastern_hour; + time_t now; + now = time((time_t*)NULL); + putenv("TZ=GMT"); + tzset(); + gmt_hour = localtime(&now)->tm_hour; + putenv("TZ=US/Eastern"); + tzset(); + eastern_hour = localtime(&now)->tm_hour; + if (eastern_hour == gmt_hour) + exit(1); + exit(0); +} +], +ac_cv_working_tzset=yes, +ac_cv_working_tzset=no, +ac_cv_working_tzset=no)]) +AC_MSG_RESULT($ac_cv_working_tzset) +if test "$ac_cv_working_tzset" = yes +then + AC_DEFINE(HAVE_WORKING_TZSET, 1, + [Define if tzset() actually switches the local timezone in a meaningful way.]) +fi + # Look for subsecond timestamps in struct stat AC_MSG_CHECKING(for tv_nsec in struct stat) AC_CACHE_VAL(ac_cv_stat_tv_nsec, diff --git a/pyconfig.h.in b/pyconfig.h.in index e2dd4d28ec5..37fd5fcbe96 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -613,6 +613,10 @@ /* Define to 1 if you have the `wcscoll' function. */ #undef HAVE_WCSCOLL +/* Define if tzset() actually switches the local timezone in a meaningful way. + */ +#undef HAVE_WORKING_TZSET + /* Define to 1 if you have the `_getpty' function. */ #undef HAVE__GETPTY