diff -r c2217813dbab Lib/_strptime.py --- a/Lib/_strptime.py Fri Feb 23 12:05:41 2007 +0100 +++ b/Lib/_strptime.py Sun Mar 11 17:33:33 2007 +0100 @@ -203,6 +203,7 @@ class TimeRE(dict): #XXX: Does 'Y' need to worry about having less or more than # 4 digits? 'Y': r"(?P\d\d\d\d)", + 'z': r"(?P[+-]\d\d[0-5]\d)", 'A': self.__seqToRE(self.locale_time.f_weekday, 'A'), 'a': self.__seqToRE(self.locale_time.a_weekday, 'a'), 'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'), @@ -299,6 +300,7 @@ def strptime(data_string, format="%a %b month = day = 1 hour = minute = second = 0 tz = -1 + tzoffset = None # Default to -1 to signify that values not known; not critical to have, # though week_of_year = -1 @@ -374,6 +376,8 @@ def strptime(data_string, format="%a %b else: # W starts week on Monday week_of_year_start = 0 + elif group_key == 'z': + tzoffset = int(found_dict['z'][1:3]) * 60 + int(found_dict['z'][3:5]) elif group_key == 'Z': # Since -1 is default value only need to worry about setting tz if # it can be something other than -1. @@ -432,6 +436,19 @@ def strptime(data_string, format="%a %b day = datetime_result.day if weekday == -1: weekday = datetime_date(year, month, day).weekday() + # Add extra time structure named attributes. + if tz != -1: + tzname = found_dict.get("Z") + if found_zone in ("utc", "gmt"): + gmtoff = 0 + else: + gmtoff = [-time.timezone, -time.altzone][tz] + elif tzoffset is not None: + gmtoff = tzoffset * 60 + tzname = None + else: + gmtoff = 0 + tzname = None return time.struct_time((year, month, day, hour, minute, second, - weekday, julian, tz)) + weekday, julian, tz, gmtoff, tzname)) diff -r c2217813dbab Lib/test/test_time.py --- a/Lib/test/test_time.py Fri Feb 23 12:05:41 2007 +0100 +++ b/Lib/test/test_time.py Sun Mar 11 17:33:33 2007 +0100 @@ -202,6 +202,34 @@ class TimeTestCase(unittest.TestCase): t1 = time.mktime(time.localtime(None)) self.assert_(0 <= (t1-t0) < 0.2) + def test_localtime_timezone(self): + # Get the localtime and examine it for the offset and zone. + lt = time.localtime() + self.assert_(hasattr(lt, "tm_gmtoff")) + self.assert_(hasattr(lt, "tm_zone")) + # See if the offset and zone are similar to the module + # attributes. + self.assert_(lt.tm_gmtoff is None and not hasattr(time, "timezone") or lt.tm_gmtoff == -time.timezone) + self.assert_(lt.tm_zone is None and not hasattr(time, "tzname") or lt.tm_zone == time.tzname[lt.tm_isdst]) + # Try and make UNIX times from the localtime and a 9-tuple + # created from the localtime. Test to see that the times are + # the same. + t = time.mktime(lt); t9 = time.mktime(lt[:9]) + self.assert_(t == t9) + # Make localtimes from the UNIX times and compare them to + # the original localtime, thus making a round trip. + new_lt = time.localtime(t); new_lt9 = time.localtime(t9) + self.assert_(new_lt == lt) + self.assert_(new_lt.tm_gmtoff is None and not hasattr(time, "timezone") or new_lt.tm_gmtoff == lt.tm_gmtoff) + self.assert_(new_lt.tm_zone is None and not hasattr(time, "tzname") or new_lt.tm_zone == lt.tm_zone) + self.assert_(new_lt9 == lt) + self.assert_(new_lt9.tm_gmtoff is None and not hasattr(time, "timezone") or new_lt.tm_gmtoff == lt.tm_gmtoff) + self.assert_(new_lt9.tm_zone is None and not hasattr(time, "tzname") or new_lt9.tm_zone == lt.tm_zone) + + def test_timegm(self): + self.assert_(time.timegm(time.gmtime(0)) == 0) + self.assert_(time.timegm(time.localtime(0)) == -time.mktime(time.gmtime(0))) + def test_main(): test_support.run_unittest(TimeTestCase) diff -r c2217813dbab Modules/timemodule.c --- a/Modules/timemodule.c Fri Feb 23 12:05:41 2007 +0100 +++ b/Modules/timemodule.c Sun Mar 11 17:33:33 2007 +0100 @@ -231,6 +231,8 @@ static PyStructSequence_Field struct_tim {"tm_wday", NULL}, {"tm_yday", NULL}, {"tm_isdst", NULL}, + {"tm_gmtoff", NULL}, + {"tm_zone", NULL}, {0} }; @@ -244,8 +246,11 @@ static PyTypeObject StructTimeType; static PyTypeObject StructTimeType; static PyObject * -tmtotuple(struct tm *p) -{ +tmtotuple(struct tm *p, int isgmt) +{ +#ifndef HAVE_STRUCT_TM_TM_ZONE + PyObject *timezone, *altzone, *tzname; +#endif PyObject *v = PyStructSequence_New(&StructTimeType); if (v == NULL) return NULL; @@ -261,6 +266,41 @@ tmtotuple(struct tm *p) SET(6, (p->tm_wday + 6) % 7); /* Want Monday == 0 */ SET(7, p->tm_yday + 1); /* Want January, 1 == 1 */ SET(8, p->tm_isdst); +#ifdef HAVE_STRUCT_TM_TM_ZONE + SET(9, p->tm_gmtoff); + PyStructSequence_SET_ITEM(v, 10, PyString_FromString(p->tm_zone)); +#else + if (isgmt) { + SET(9, 0); + PyStructSequence_SET_ITEM(v, 10, PyString_FromString("GMT")); + } else { + timezone = PyDict_GetItemString(moddict, "timezone"); + altzone = PyDict_GetItemString(moddict, "altzone"); + if ((timezone != NULL) && PyInt_Check(timezone) && (altzone != NULL) && PyInt_Check(altzone)) { + switch (p->tm_isdst) { + case 0: + SET(9, -PyInt_AsLong(timezone)); + break; + case 1: + SET(9, -PyInt_AsLong(altzone)); + break; + default: + SET(9, 0); + break; + } + } else { + PyStructSequence_SET_ITEM(v, 9, Py_None); + Py_INCREF(Py_None); + } + tzname = PyDict_GetItemString(moddict, "tzname"); + if ((tzname != NULL) && PyTuple_Check(tzname) && (p->tm_isdst != -1)) { + PyStructSequence_SET_ITEM(v, 10, PyString_FromString(PyString_AsString(PyTuple_GetItem(tzname, p->tm_isdst)))); + } else { + PyStructSequence_SET_ITEM(v, 10, Py_None); + Py_INCREF(Py_None); + } + } +#endif #undef SET if (PyErr_Occurred()) { Py_XDECREF(v); @@ -271,7 +311,7 @@ tmtotuple(struct tm *p) } static PyObject * -time_convert(double when, struct tm * (*function)(const time_t *)) +time_convert(double when, struct tm * (*function)(const time_t *), int isgmt) { struct tm *p; time_t whent = _PyTime_DoubleToTimet(when); @@ -287,7 +327,7 @@ time_convert(double when, struct tm * (* #endif return PyErr_SetFromErrno(PyExc_ValueError); } - return tmtotuple(p); + return tmtotuple(p, isgmt); } /* Parse arg tuple that can contain an optional float-or-None value; @@ -318,7 +358,7 @@ time_gmtime(PyObject *self, PyObject *ar double when; if (!parse_time_double_args(args, "|O:gmtime", &when)) return NULL; - return time_convert(when, gmtime); + return time_convert(when, gmtime, 1); } PyDoc_STRVAR(gmtime_doc, @@ -334,14 +374,23 @@ time_localtime(PyObject *self, PyObject double when; if (!parse_time_double_args(args, "|O:localtime", &when)) return NULL; - return time_convert(when, localtime); + return time_convert(when, localtime, 0); } PyDoc_STRVAR(localtime_doc, "localtime([seconds]) -> (tm_year,tm_mon,tm_day,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst)\n\ \n\ Convert seconds since the Epoch to a time tuple expressing local time.\n\ -When 'seconds' is not passed in, convert the current time instead."); +When 'seconds' is not passed in, convert the current time instead.\n\ +\n\ +If time zone information is available, the tm_gmtoff and tm_zone\n\ +attributes may be consulted on the time tuple to obtain, respectively,\n\ +the location of the associated time zone in seconds east of GMT/UTC\n\ +and the name of the zone. Note that the value of tm_gmtoff may be\n\ +negative for time zones west of the prime meridian, for example.\n\ +\n\ +Where no time zone information is available, tm_gmtoff and tm_zone\n\ +will both be None."); static int gettmarg(PyObject *args, struct tm *p) @@ -360,6 +409,17 @@ gettmarg(PyObject *args, struct tm *p) &p->tm_yday, &p->tm_isdst)) return 0; +#ifdef HAVE_STRUCT_TM_TM_ZONE + /* Add GMT/UTC offset where a time structure is provided. */ + if (PyType_IsSubtype(args->ob_type, &StructTimeType)) { + PyObject *gmtoff = ((PyStructSequence *)(args))->ob_item[9]; + PyObject *zone = ((PyStructSequence *)(args))->ob_item[10]; + if (gmtoff != Py_None) + p->tm_gmtoff = PyInt_AsLong(gmtoff); + if (zone != Py_None) + p->tm_zone = PyString_AsString(zone); + } +#endif if (y < 1900) { PyObject *accept = PyDict_GetItemString(moddict, "accept2dyear"); @@ -575,6 +635,7 @@ time_mktime(PyObject *self, PyObject *ar time_t tt; if (!PyArg_ParseTuple(args, "O:mktime", &tup)) return NULL; + /* Seemingly useless calls to make things "NeXT robust" (rev. 6061). */ tt = time(&tt); buf = *localtime(&tt); if (!gettmarg(tup, &buf)) @@ -591,8 +652,60 @@ PyDoc_STRVAR(mktime_doc, PyDoc_STRVAR(mktime_doc, "mktime(tuple) -> floating point number\n\ \n\ -Convert a time tuple in local time to seconds since the Epoch."); +Convert a time tuple in local time to seconds since the Epoch.\n\ +Note that mktime(gmtime(0)) will not generally return zero for most\n\ +time zones; instead the returned value will either be equal to that\n\ +of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ + +static PyObject * +time_timegm(PyObject *self, PyObject *args) +{ +#ifndef HAVE_STRUCT_TM_TM_ZONE + PyObject *timezone, *altzone; +#endif + PyObject *tup; + struct tm buf; + time_t tt; + if (!PyArg_ParseTuple(args, "O:timegm", &tup)) + return NULL; + /* Seemingly useless calls to make things "NeXT robust" (rev. 6061). */ + tt = time(&tt); + buf = *localtime(&tt); + if (!gettmarg(tup, &buf)) + return NULL; + tt = mktime(&buf); + if (tt == (time_t)(-1)) { + PyErr_SetString(PyExc_OverflowError, + "mktime argument out of range"); + return NULL; + } + /* Adjust the result using tm_gmtoff or the timezone attributes. */ +#ifdef HAVE_STRUCT_TM_TM_ZONE + tt += buf.tm_gmtoff; +#else + timezone = PyDict_GetItemString(moddict, "timezone"); + altzone = PyDict_GetItemString(moddict, "altzone"); + if ((timezone != NULL) && PyInt_Check(timezone) && (altzone != NULL) && PyInt_Check(altzone)) + switch (buf.tm_isdst) { + case 0: + tt -= PyInt_AsLong(timezone); + break; + case 1: + tt -= PyInt_AsLong(altzone); + break; + default: + break; + } +#endif + return PyFloat_FromDouble((double)tt); +} + +PyDoc_STRVAR(timegm_doc, +"timegm(tuple) -> floating point number\n\ +\n\ +Convert a time tuple to seconds since the Epoch in the GMT/UTC time\n\ +zone. Note that timegm(gmtime(0)) should always return zero."); #ifdef HAVE_WORKING_TZSET void inittimezone(PyObject *module); @@ -735,6 +848,7 @@ static PyMethodDef time_methods[] = { #ifdef HAVE_MKTIME {"mktime", time_mktime, METH_VARARGS, mktime_doc}, #endif + {"timegm", time_timegm, METH_VARARGS, timegm_doc}, #ifdef HAVE_STRFTIME {"strftime", time_strftime, METH_VARARGS, strftime_doc}, #endif