From 1badc2c1afd3589741f22a1a5021cc7ca9baf796 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Fri, 22 Sep 2017 09:37:10 +0500 Subject: [PATCH 01/19] Call native Windows APIs to get the timezone name. --- Modules/timemodule.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 36a95bbcedd6fe..ddd0037b873a4c 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1222,8 +1222,17 @@ PyInit_timezone(PyObject *m) { PyModule_AddIntConstant(m, "altzone", timezone-3600); #endif PyModule_AddIntConstant(m, "daylight", daylight); + /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid + wrong encoding of time zone names.*/ +#ifdef MS_WINDOWS + TIME_ZONE_INFORMATION tzi; + GetTimeZoneInformation(&tzi); + otz0 = PyUnicode_FromWideChar(tzi.StandardName, -1); + otz1 = PyUnicode_FromWideChar(tzi.DaylightName, -1); +#else otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape"); otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape"); +#endif PyModule_AddObject(m, "tzname", Py_BuildValue("(NN)", otz0, otz1)); #else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ { From ed05218bde3b716a2ccb2f705748834391f19fe0 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Fri, 22 Sep 2017 10:51:02 +0500 Subject: [PATCH 02/19] Call native Windows APIs to get the timezone name for time.localtime(). Related changes in tmtotuple() and time.gmtime() --- Modules/timemodule.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index ddd0037b873a4c..8ef9e309ecfcc3 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -278,7 +278,12 @@ static PyTypeObject StructTimeType; static PyObject * tmtotuple(struct tm *p #ifndef HAVE_STRUCT_TM_TM_ZONE - , const char *zone, time_t gmtoff +#ifdef MS_WINDOWS + , const wchar_t *zone +#else + , const char *zone +#endif + , time_t gmtoff #endif ) { @@ -301,9 +306,14 @@ tmtotuple(struct tm *p PyStructSequence_SET_ITEM(v, 9, PyUnicode_DecodeLocale(p->tm_zone, "surrogateescape")); SET(10, p->tm_gmtoff); +#else +#ifdef MS_WINDOWS + PyStructSequence_SET_ITEM(v, 9, + PyUnicode_FromWideChar(zone, -1)); #else PyStructSequence_SET_ITEM(v, 9, PyUnicode_DecodeLocale(zone, "surrogateescape")); +#endif PyStructSequence_SET_ITEM(v, 10, _PyLong_FromTime_t(gmtoff)); #endif /* HAVE_STRUCT_TM_TM_ZONE */ #undef SET @@ -352,9 +362,13 @@ time_gmtime(PyObject *self, PyObject *args) return NULL; #ifdef HAVE_STRUCT_TM_TM_ZONE return tmtotuple(&buf); +#else +#ifdef MS_WINDOWS + return tmtotuple(&buf, L"UTC", 0); #else return tmtotuple(&buf, "UTC", 0); #endif +#endif } #ifndef HAVE_TIMEGM @@ -395,9 +409,24 @@ time_localtime(PyObject *self, PyObject *args) #else { struct tm local = buf; +#ifdef MS_WINDOWS + wchar_t *zone; +#else char zone[100]; +#endif time_t gmtoff; +#ifdef MS_WINDOWS + TIME_ZONE_INFORMATION tzi; + int tzid = GetTimeZoneInformation(&tzi); + if (tzid < 2) { + zone = tzi.StandardName; + } + else { + zone = tzi.DaylightName; + } +#else strftime(zone, sizeof(zone), "%Z", &buf); +#endif gmtoff = timegm(&buf) - when; return tmtotuple(&local, zone, gmtoff); } From f1a7b9200c41d3800b8a644d53edd46a8e2fced7 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Fri, 22 Sep 2017 11:26:58 +0500 Subject: [PATCH 03/19] Add comment. --- Modules/timemodule.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 8ef9e309ecfcc3..0d1dbd0aa27550 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -415,6 +415,8 @@ time_localtime(PyObject *self, PyObject *args) char zone[100]; #endif time_t gmtoff; + /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid + wrong encoding of time zone names.*/ #ifdef MS_WINDOWS TIME_ZONE_INFORMATION tzi; int tzid = GetTimeZoneInformation(&tzi); From 1639cad795ef88d4107042a64f90a0a82f2ec096 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Fri, 22 Sep 2017 20:46:30 +0500 Subject: [PATCH 04/19] Back to wcsftime. Convert tabs to space. --- Modules/timemodule.c | 66 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 0d1dbd0aa27550..1920652e295688 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -279,11 +279,11 @@ static PyObject * tmtotuple(struct tm *p #ifndef HAVE_STRUCT_TM_TM_ZONE #ifdef MS_WINDOWS - , const wchar_t *zone + , const wchar_t *zone #else - , const char *zone + , const char *zone #endif - , time_t gmtoff + , time_t gmtoff #endif ) { @@ -308,8 +308,8 @@ tmtotuple(struct tm *p SET(10, p->tm_gmtoff); #else #ifdef MS_WINDOWS - PyStructSequence_SET_ITEM(v, 9, - PyUnicode_FromWideChar(zone, -1)); + PyStructSequence_SET_ITEM(v, 9, + PyUnicode_FromWideChar(zone, -1)); #else PyStructSequence_SET_ITEM(v, 9, PyUnicode_DecodeLocale(zone, "surrogateescape")); @@ -364,7 +364,7 @@ time_gmtime(PyObject *self, PyObject *args) return tmtotuple(&buf); #else #ifdef MS_WINDOWS - return tmtotuple(&buf, L"UTC", 0); + return tmtotuple(&buf, L"UTC", 0); #else return tmtotuple(&buf, "UTC", 0); #endif @@ -410,22 +410,22 @@ time_localtime(PyObject *self, PyObject *args) { struct tm local = buf; #ifdef MS_WINDOWS - wchar_t *zone; + wchar_t *zone; #else char zone[100]; #endif time_t gmtoff; - /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid - wrong encoding of time zone names.*/ + /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid + wrong encoding of time zone names.*/ #ifdef MS_WINDOWS - TIME_ZONE_INFORMATION tzi; - int tzid = GetTimeZoneInformation(&tzi); - if (tzid < 2) { - zone = tzi.StandardName; - } - else { - zone = tzi.DaylightName; - } + TIME_ZONE_INFORMATION tzi; + int tzid = GetTimeZoneInformation(&tzi); + if (tzid < 2) { + zone = tzi.StandardName; + } + else { + zone = tzi.DaylightName; + } #else strftime(zone, sizeof(zone), "%Z", &buf); #endif @@ -551,10 +551,6 @@ checktm(struct tm* buf) return 1; } -#ifdef MS_WINDOWS - /* wcsftime() doesn't format correctly time zones, see issue #10653 */ -# undef HAVE_WCSFTIME -#endif #define STRFTIME_FORMAT_CODES \ "Commonly used format codes:\n\ \n\ @@ -653,27 +649,27 @@ time_strftime(PyObject *self, PyObject *args) fmt = PyBytes_AS_STRING(format); #endif -#if defined(MS_WINDOWS) && !defined(HAVE_WCSFTIME) +#if defined(MS_WINDOWS) && defined(HAVE_WCSFTIME) /* check that the format string contains only valid directives */ - for (outbuf = strchr(fmt, '%'); + for (outbuf = wcschr(fmt, L'%'); outbuf != NULL; - outbuf = strchr(outbuf+2, '%')) + outbuf = wcschr(outbuf + 2, L'%')) { - if (outbuf[1] == '#') + if (outbuf[1] == L'#') ++outbuf; /* not documented by python, */ - if (outbuf[1] == '\0') + if (outbuf[1] == L'\0') break; - if ((outbuf[1] == 'y') && buf.tm_year < 0) { + if ((outbuf[1] == L'y') && buf.tm_year < 0) { PyErr_SetString(PyExc_ValueError, "format %y requires year >= 1900 on Windows"); - Py_DECREF(format); + PyMem_Free(format); return NULL; } } #elif (defined(_AIX) || defined(sun)) && defined(HAVE_WCSFTIME) for (outbuf = wcschr(fmt, '%'); outbuf != NULL; - outbuf = wcschr(outbuf+2, '%')) + outbuf = wcschr(outbuf + 2, '%')) { if (outbuf[1] == L'\0') break; @@ -1253,13 +1249,13 @@ PyInit_timezone(PyObject *m) { PyModule_AddIntConstant(m, "altzone", timezone-3600); #endif PyModule_AddIntConstant(m, "daylight", daylight); - /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid - wrong encoding of time zone names.*/ + /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid + wrong encoding of time zone names.*/ #ifdef MS_WINDOWS - TIME_ZONE_INFORMATION tzi; - GetTimeZoneInformation(&tzi); - otz0 = PyUnicode_FromWideChar(tzi.StandardName, -1); - otz1 = PyUnicode_FromWideChar(tzi.DaylightName, -1); + TIME_ZONE_INFORMATION tzi; + GetTimeZoneInformation(&tzi); + otz0 = PyUnicode_FromWideChar(tzi.StandardName, -1); + otz1 = PyUnicode_FromWideChar(tzi.DaylightName, -1); #else otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape"); otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape"); From fb417ffcaa3e95e0bbea76ee695df4bf42e0a8e8 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sat, 23 Sep 2017 22:34:45 +0500 Subject: [PATCH 05/19] Add time zone directive's replacement --- Modules/timemodule.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 1920652e295688..db2ec1ba28b16f 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -683,6 +683,48 @@ time_strftime(PyObject *self, PyObject *args) } #endif +#ifdef MS_WINDOWS + //Get time zone names with Windows API + wchar_t *zone; + TIME_ZONE_INFORMATION tzi; + int tzid = GetTimeZoneInformation(&tzi); + if (tzid < 2) { + zone = tzi.StandardName; + } + else { + zone = tzi.DaylightName; + } + + wchar_t *result, *tmp; + const wchar_t *ins; // the next insert point + + size_t len_zone = wcslen(zone); + size_t len_front; // distance between new occurance of %Z and end of the last one + size_t count; + + // Count the number of %Z occurences + ins = fmt; + for (count = 0; tmp = wcsstr(ins, L"%Z"); ++count) { + ins = tmp + 2; + } + + //Replace %Z with time zone name + if (count) + { + size_t l = wcslen(fmt) + (len_zone - 2) * count + 1; + tmp = result = (time_char *)PyMem_Malloc(l * sizeof(time_char)); + while (count--) { + ins = wcsstr(fmt, L"%Z"); + len_front = ins - fmt; + tmp = wcsncpy(tmp, fmt, len_front) + len_front; + tmp = wcscpy(tmp, zone) + len_zone; + fmt += len_front + 2; // move to next "end of rep" + } + wcscpy(tmp, fmt); + fmt = result; + } +#endif + fmtlen = time_strlen(fmt); /* I hate these functions that presume you know how big the output @@ -727,6 +769,12 @@ time_strftime(PyObject *self, PyObject *args) } #ifdef HAVE_WCSFTIME PyMem_Free(format); +#ifdef MS_WINDOWS + if (count > 0) + { + PyMem_Free(result); + } +#endif #else Py_DECREF(format); #endif From 90b6cdb235b9fe26e72e60a35b4d5a88eed62e26 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sun, 24 Sep 2017 09:46:19 +0500 Subject: [PATCH 06/19] Add function to get time zone name. --- Modules/timemodule.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index db2ec1ba28b16f..b912fb6ba4f234 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -109,6 +109,18 @@ win_perf_counter(_Py_clock_info_t *info) } return PyFloat_FromDouble(diff / (double)cpu_frequency); } + +static void get_windows_zone(wchar_t *zname) +{ + TIME_ZONE_INFORMATION tzi; + int tzid = GetTimeZoneInformation(&tzi); + if (tzid < 2) { + wcscpy(zname, tzi.StandardName); + } + else { + wcscpy(zname, tzi.DaylightName); + } +} #endif /* MS_WINDOWS */ #if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK) @@ -410,7 +422,7 @@ time_localtime(PyObject *self, PyObject *args) { struct tm local = buf; #ifdef MS_WINDOWS - wchar_t *zone; + wchar_t zone[128]; #else char zone[100]; #endif @@ -418,14 +430,7 @@ time_localtime(PyObject *self, PyObject *args) /*bpo-16322, bpo-27426: For Windows use GetTimeZoneInformation() to avoid wrong encoding of time zone names.*/ #ifdef MS_WINDOWS - TIME_ZONE_INFORMATION tzi; - int tzid = GetTimeZoneInformation(&tzi); - if (tzid < 2) { - zone = tzi.StandardName; - } - else { - zone = tzi.DaylightName; - } + get_windows_zone(zone); #else strftime(zone, sizeof(zone), "%Z", &buf); #endif @@ -684,20 +689,12 @@ time_strftime(PyObject *self, PyObject *args) #endif #ifdef MS_WINDOWS - //Get time zone names with Windows API - wchar_t *zone; - TIME_ZONE_INFORMATION tzi; - int tzid = GetTimeZoneInformation(&tzi); - if (tzid < 2) { - zone = tzi.StandardName; - } - else { - zone = tzi.DaylightName; - } - + /*For Windows firstly replace %Z with time zone name from Windows API + and not use format string containing %Z with wcsftime*/ wchar_t *result, *tmp; const wchar_t *ins; // the next insert point - + wchar_t zone[128]; + get_windows_zone(zone); size_t len_zone = wcslen(zone); size_t len_front; // distance between new occurance of %Z and end of the last one size_t count; From 4bacd1eb55146387caac016e10c11acfc47b9296 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sun, 24 Sep 2017 20:43:05 +0500 Subject: [PATCH 07/19] Add handling with '%%Z' --- Modules/timemodule.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index b912fb6ba4f234..a6b496abf60cbb 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -690,7 +690,7 @@ time_strftime(PyObject *self, PyObject *args) #ifdef MS_WINDOWS /*For Windows firstly replace %Z with time zone name from Windows API - and not use format string containing %Z with wcsftime*/ + to not use format string containing %Z with wcsftime().*/ wchar_t *result, *tmp; const wchar_t *ins; // the next insert point wchar_t zone[128]; @@ -713,9 +713,18 @@ time_strftime(PyObject *self, PyObject *args) while (count--) { ins = wcsstr(fmt, L"%Z"); len_front = ins - fmt; - tmp = wcsncpy(tmp, fmt, len_front) + len_front; - tmp = wcscpy(tmp, zone) + len_zone; - fmt += len_front + 2; // move to next "end of rep" + if (wcsncmp(ins - 1, L"%", 1) == 0) + { + len_front += 2; + tmp = wcsncpy(tmp, fmt, len_front) + len_front; + fmt += len_front; + } + else + { + tmp = wcsncpy(tmp, fmt, len_front) + len_front; + tmp = wcscpy(tmp, zone) + len_zone; + fmt += len_front + 2; + } } wcscpy(tmp, fmt); fmt = result; From 92b2f45ece81824c3175fc99578fdad266b75802 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sun, 24 Sep 2017 22:04:58 +0500 Subject: [PATCH 08/19] Try to follow PEP 7 --- Modules/timemodule.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index a6b496abf60cbb..9750b2d678b3eb 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -110,15 +110,17 @@ win_perf_counter(_Py_clock_info_t *info) return PyFloat_FromDouble(diff / (double)cpu_frequency); } -static void get_windows_zone(wchar_t *zname) +// Function to get time zone name with Windows API +static void get_windows_zone(wchar_t *out) { TIME_ZONE_INFORMATION tzi; - int tzid = GetTimeZoneInformation(&tzi); + DWORD tzid = GetTimeZoneInformation(&tzi); + if (tzid < 2) { - wcscpy(zname, tzi.StandardName); + wcscpy(out, tzi.StandardName); } else { - wcscpy(zname, tzi.DaylightName); + wcscpy(out, tzi.DaylightName); } } #endif /* MS_WINDOWS */ @@ -692,12 +694,12 @@ time_strftime(PyObject *self, PyObject *args) /*For Windows firstly replace %Z with time zone name from Windows API to not use format string containing %Z with wcsftime().*/ wchar_t *result, *tmp; - const wchar_t *ins; // the next insert point + const wchar_t *ins; wchar_t zone[128]; + size_t len_zone, len_copy, count; + get_windows_zone(zone); - size_t len_zone = wcslen(zone); - size_t len_front; // distance between new occurance of %Z and end of the last one - size_t count; + len_zone = wcslen(zone); // Count the number of %Z occurences ins = fmt; @@ -706,24 +708,21 @@ time_strftime(PyObject *self, PyObject *args) } //Replace %Z with time zone name - if (count) - { + if (count) { size_t l = wcslen(fmt) + (len_zone - 2) * count + 1; tmp = result = (time_char *)PyMem_Malloc(l * sizeof(time_char)); while (count--) { ins = wcsstr(fmt, L"%Z"); - len_front = ins - fmt; - if (wcsncmp(ins - 1, L"%", 1) == 0) - { - len_front += 2; - tmp = wcsncpy(tmp, fmt, len_front) + len_front; - fmt += len_front; + len_copy = ins - fmt; + if (wcsncmp(ins - 1, L"%", 1) == 0) { + len_copy += 2; + tmp = wcsncpy(tmp, fmt, len_copy) + len_copy; + fmt += len_copy; } - else - { - tmp = wcsncpy(tmp, fmt, len_front) + len_front; + else { + tmp = wcsncpy(tmp, fmt, len_copy) + len_copy; tmp = wcscpy(tmp, zone) + len_zone; - fmt += len_front + 2; + fmt += len_copy + 2; } } wcscpy(tmp, fmt); @@ -776,8 +775,7 @@ time_strftime(PyObject *self, PyObject *args) #ifdef HAVE_WCSFTIME PyMem_Free(format); #ifdef MS_WINDOWS - if (count > 0) - { + if (count > 0) { PyMem_Free(result); } #endif From 8ecb7742b5dea76f4965e83b0069abf0aa3addfc Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Mon, 25 Sep 2017 11:09:03 +0500 Subject: [PATCH 09/19] Minor changes (one row less). --- Modules/timemodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 9750b2d678b3eb..fbffca5ada918e 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -715,9 +715,8 @@ time_strftime(PyObject *self, PyObject *args) ins = wcsstr(fmt, L"%Z"); len_copy = ins - fmt; if (wcsncmp(ins - 1, L"%", 1) == 0) { - len_copy += 2; - tmp = wcsncpy(tmp, fmt, len_copy) + len_copy; - fmt += len_copy; + tmp = wcsncpy(tmp, fmt, len_copy + 2) + len_copy + 2; + fmt += len_copy + 2; } else { tmp = wcsncpy(tmp, fmt, len_copy) + len_copy; From 52dc479c141e3ac6323fba2002cff5b513de5005 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Mon, 25 Sep 2017 14:03:14 +0500 Subject: [PATCH 10/19] Add news entry in Misc/NEWS.d --- .../NEWS.d/next/Windows/2017-09-25-14-01-08.bpo-16322.hIaWkb.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Windows/2017-09-25-14-01-08.bpo-16322.hIaWkb.rst diff --git a/Misc/NEWS.d/next/Windows/2017-09-25-14-01-08.bpo-16322.hIaWkb.rst b/Misc/NEWS.d/next/Windows/2017-09-25-14-01-08.bpo-16322.hIaWkb.rst new file mode 100644 index 00000000000000..e4d266e9d26dcb --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2017-09-25-14-01-08.bpo-16322.hIaWkb.rst @@ -0,0 +1 @@ +Use Windows APIs to avoid wrong encoding of time zone names. From 14927761e5468069a7ada61147a19fccf7825aa2 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Tue, 26 Sep 2017 18:12:41 +0500 Subject: [PATCH 11/19] Move 'static void' on a separate line. --- Modules/timemodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index fbffca5ada918e..835c6ed2bfc613 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -111,7 +111,8 @@ win_perf_counter(_Py_clock_info_t *info) } // Function to get time zone name with Windows API -static void get_windows_zone(wchar_t *out) +static void +get_windows_zone(wchar_t *out) { TIME_ZONE_INFORMATION tzi; DWORD tzid = GetTimeZoneInformation(&tzi); From b515a623fa94632411b08595ed2488feaef7221b Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Tue, 26 Sep 2017 21:32:33 +0500 Subject: [PATCH 12/19] Use named constants. Add error handling for TIME_ZONE_ID_INVALID. --- Modules/timemodule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 835c6ed2bfc613..e20520ea4a5ed4 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -117,12 +117,15 @@ get_windows_zone(wchar_t *out) TIME_ZONE_INFORMATION tzi; DWORD tzid = GetTimeZoneInformation(&tzi); - if (tzid < 2) { - wcscpy(out, tzi.StandardName); + if (tzid == TIME_ZONE_ID_INVALID) { + PyErr_SetFromWindowsErr(0); } - else { + else if (tzid == TIME_ZONE_ID_DAYLIGHT) { wcscpy(out, tzi.DaylightName); } + else { + wcscpy(out, tzi.StandardName); + } } #endif /* MS_WINDOWS */ From a898b3fcc22654203abcb606de65985619c0b8ce Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Wed, 27 Sep 2017 09:27:45 +0500 Subject: [PATCH 13/19] Add PyMem_Free call if error occured. --- Modules/timemodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index e20520ea4a5ed4..0383a3c664d7bc 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -703,6 +703,10 @@ time_strftime(PyObject *self, PyObject *args) size_t len_zone, len_copy, count; get_windows_zone(zone); + if (PyErr_Occurred()) { + PyMem_Free(format); + return NULL; + } len_zone = wcslen(zone); // Count the number of %Z occurences From b129f62daa0186baa9f1e110fb1ab4cdd811bfb6 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Wed, 27 Sep 2017 10:21:30 +0500 Subject: [PATCH 14/19] Merge format checking for Windows, AIX and Solaris --- Modules/timemodule.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 0383a3c664d7bc..59269037181871 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -660,7 +660,8 @@ time_strftime(PyObject *self, PyObject *args) fmt = PyBytes_AS_STRING(format); #endif -#if defined(MS_WINDOWS) && defined(HAVE_WCSFTIME) +#if (defined(_AIX) || defined(sun) || defined(MS_WINDOWS)) && \ + defined(HAVE_WCSFTIME) /* check that the format string contains only valid directives */ for (outbuf = wcschr(fmt, L'%'); outbuf != NULL; @@ -670,28 +671,15 @@ time_strftime(PyObject *self, PyObject *args) ++outbuf; /* not documented by python, */ if (outbuf[1] == L'\0') break; + /* Issue #19634: On AIX, wcsftime("y", (1899, 1, 1, 0, 0, 0, 0, 0, 0)) + returns "0/" instead of "99" */ if ((outbuf[1] == L'y') && buf.tm_year < 0) { PyErr_SetString(PyExc_ValueError, - "format %y requires year >= 1900 on Windows"); + "format %y requires year >= 1900 on Windows and AIX"); PyMem_Free(format); return NULL; } } -#elif (defined(_AIX) || defined(sun)) && defined(HAVE_WCSFTIME) - for (outbuf = wcschr(fmt, '%'); - outbuf != NULL; - outbuf = wcschr(outbuf + 2, '%')) - { - if (outbuf[1] == L'\0') - break; - /* Issue #19634: On AIX, wcsftime("y", (1899, 1, 1, 0, 0, 0, 0, 0, 0)) - returns "0/" instead of "99" */ - if (outbuf[1] == L'y' && buf.tm_year < 0) { - PyErr_SetString(PyExc_ValueError, - "format %y requires year >= 1900 on AIX"); - return NULL; - } - } #endif #ifdef MS_WINDOWS From fb68cbff4cf759aefd229b9b74d17297a5a3f5af Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Thu, 28 Sep 2017 08:51:49 +0500 Subject: [PATCH 15/19] Move %Z count to format checking block. Check for integer overflow. --- Modules/timemodule.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 59269037181871..f74b262ee8a9ec 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -662,6 +662,9 @@ time_strftime(PyObject *self, PyObject *args) #if (defined(_AIX) || defined(sun) || defined(MS_WINDOWS)) && \ defined(HAVE_WCSFTIME) +#ifdef MS_WINDOWS + size_t count = 0; +#endif /* check that the format string contains only valid directives */ for (outbuf = wcschr(fmt, L'%'); outbuf != NULL; @@ -679,6 +682,12 @@ time_strftime(PyObject *self, PyObject *args) PyMem_Free(format); return NULL; } +#ifdef MS_WINDOWS + // Count the number of %Z occurences + if (outbuf[1] == L'Z') { + ++count; + } +#endif } #endif @@ -688,7 +697,7 @@ time_strftime(PyObject *self, PyObject *args) wchar_t *result, *tmp; const wchar_t *ins; wchar_t zone[128]; - size_t len_zone, len_copy, count; + size_t len_zone, len_copy; get_windows_zone(zone); if (PyErr_Occurred()) { @@ -697,14 +706,11 @@ time_strftime(PyObject *self, PyObject *args) } len_zone = wcslen(zone); - // Count the number of %Z occurences - ins = fmt; - for (count = 0; tmp = wcsstr(ins, L"%Z"); ++count) { - ins = tmp + 2; - } - //Replace %Z with time zone name if (count) { + if (len_zone - 2 > (PY_SSIZE_T_MAX / sizeof(time_char) - 1 - wcslen(fmt)) / count) { + return PyErr_NoMemory(); + } size_t l = wcslen(fmt) + (len_zone - 2) * count + 1; tmp = result = (time_char *)PyMem_Malloc(l * sizeof(time_char)); while (count--) { From 2231d7240bf57ba0d8e7e42a0c400121fed3268e Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Fri, 29 Sep 2017 19:56:09 +0500 Subject: [PATCH 16/19] PyMem_Free(format) added. Rewrite part replacing %Z. --- Modules/timemodule.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index f74b262ee8a9ec..36bc6602c07417 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -709,22 +709,24 @@ time_strftime(PyObject *self, PyObject *args) //Replace %Z with time zone name if (count) { if (len_zone - 2 > (PY_SSIZE_T_MAX / sizeof(time_char) - 1 - wcslen(fmt)) / count) { + PyMem_Free(format); return PyErr_NoMemory(); } size_t l = wcslen(fmt) + (len_zone - 2) * count + 1; tmp = result = (time_char *)PyMem_Malloc(l * sizeof(time_char)); - while (count--) { - ins = wcsstr(fmt, L"%Z"); + + while (count) { + ins = wcschr(fmt, L'%'); len_copy = ins - fmt; - if (wcsncmp(ins - 1, L"%", 1) == 0) { - tmp = wcsncpy(tmp, fmt, len_copy + 2) + len_copy + 2; - fmt += len_copy + 2; - } - else { + if (ins[1] == L'Z') { tmp = wcsncpy(tmp, fmt, len_copy) + len_copy; tmp = wcscpy(tmp, zone) + len_zone; - fmt += len_copy + 2; + count--; + } + else { + tmp = wcsncpy(tmp, fmt, len_copy + 2) + len_copy + 2; } + fmt += len_copy + 2;; } wcscpy(tmp, fmt); fmt = result; From 2c1e468641b62b3e5513d65ddd6c0d25edd9a923 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sat, 30 Sep 2017 16:20:47 +0500 Subject: [PATCH 17/19] Changed code for %Z replacement. --- Modules/timemodule.c | 45 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 36bc6602c07417..d8a3a2ebca3874 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -663,7 +663,10 @@ time_strftime(PyObject *self, PyObject *args) #if (defined(_AIX) || defined(sun) || defined(MS_WINDOWS)) && \ defined(HAVE_WCSFTIME) #ifdef MS_WINDOWS + // Create an array of %Z occurences for subsequent replacement size_t count = 0; + size_t n = wcslen(format) / 2; + size_t *points = (size_t *)PyMem_Malloc(n * sizeof(size_t)); #endif /* check that the format string contains only valid directives */ for (outbuf = wcschr(fmt, L'%'); @@ -683,8 +686,9 @@ time_strftime(PyObject *self, PyObject *args) return NULL; } #ifdef MS_WINDOWS - // Count the number of %Z occurences + // Count the number of %Z occurences and fill the array if (outbuf[1] == L'Z') { + *(points + count)= outbuf - fmt; ++count; } #endif @@ -695,19 +699,19 @@ time_strftime(PyObject *self, PyObject *args) /*For Windows firstly replace %Z with time zone name from Windows API to not use format string containing %Z with wcsftime().*/ wchar_t *result, *tmp; - const wchar_t *ins; - wchar_t zone[128]; - size_t len_zone, len_copy; - - get_windows_zone(zone); - if (PyErr_Occurred()) { - PyMem_Free(format); - return NULL; - } - len_zone = wcslen(zone); //Replace %Z with time zone name if (count) { + wchar_t zone[128]; + size_t len_zone; + + get_windows_zone(zone); + if (PyErr_Occurred()) { + PyMem_Free(format); + return NULL; + } + len_zone = wcslen(zone); + if (len_zone - 2 > (PY_SSIZE_T_MAX / sizeof(time_char) - 1 - wcslen(fmt)) / count) { PyMem_Free(format); return PyErr_NoMemory(); @@ -715,22 +719,19 @@ time_strftime(PyObject *self, PyObject *args) size_t l = wcslen(fmt) + (len_zone - 2) * count + 1; tmp = result = (time_char *)PyMem_Malloc(l * sizeof(time_char)); - while (count) { - ins = wcschr(fmt, L'%'); - len_copy = ins - fmt; - if (ins[1] == L'Z') { - tmp = wcsncpy(tmp, fmt, len_copy) + len_copy; - tmp = wcscpy(tmp, zone) + len_zone; - count--; - } - else { - tmp = wcsncpy(tmp, fmt, len_copy + 2) + len_copy + 2; + for (size_t i = 0, k = points[0]; i < count; ++i) { + if (i > 0) { + k = *(points + i) - *(points + i - 1) - 2; } - fmt += len_copy + 2;; + tmp = wcsncpy(tmp, fmt, k) + k; + tmp = wcscpy(tmp, zone) + len_zone; + fmt += k + 2; } wcscpy(tmp, fmt); + fmt = result; } + PyMem_Free(points); #endif fmtlen = time_strlen(fmt); From 3d64b76d89efd6ecff8e49fc66248f418a346413 Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sat, 30 Sep 2017 17:17:43 +0500 Subject: [PATCH 18/19] Handle error in PyInit_timezone(). --- Modules/timemodule.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index d8a3a2ebca3874..e5e2cba01689a8 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1309,7 +1309,9 @@ PyInit_timezone(PyObject *m) { wrong encoding of time zone names.*/ #ifdef MS_WINDOWS TIME_ZONE_INFORMATION tzi; - GetTimeZoneInformation(&tzi); + if (GetTimeZoneInformation(&tzi) == TIME_ZONE_ID_INVALID) { + PyErr_SetFromWindowsErr(0); + } otz0 = PyUnicode_FromWideChar(tzi.StandardName, -1); otz1 = PyUnicode_FromWideChar(tzi.DaylightName, -1); #else From 4ae8592d7e322ae6847b07bfafab895cf2ab83ef Mon Sep 17 00:00:00 2001 From: Denis Osipov Date: Sat, 30 Sep 2017 17:47:25 +0500 Subject: [PATCH 19/19] Remove one empty line. --- Modules/timemodule.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index e5e2cba01689a8..c7463b8b5da933 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -728,7 +728,6 @@ time_strftime(PyObject *self, PyObject *args) fmt += k + 2; } wcscpy(tmp, fmt); - fmt = result; } PyMem_Free(points);