summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMark Wiebe <mwwiebe@gmail.com>2011-09-30 14:55:58 -0700
committerCharles Harris <charlesr.harris@gmail.com>2011-10-01 09:43:10 -0600
commitb1e5fa05ad4803856ec3272a900707eaf4a7f92e (patch)
treeb739b4a819bb3e120a9ed105abe7484255b90266 /numpy
parent6a3ca96df7179e745c76dc09a67bf37af3d818ce (diff)
downloadnumpy-b1e5fa05ad4803856ec3272a900707eaf4a7f92e.tar.gz
BUG: Attempt to fix the Y2038 problem parsing dates >= 2038
Also incorporated part of Han Genuit's pull request #23, to try and reduce the difficulty dealing with the merge conflict that will result from these concurrent changes
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/datetime_strings.c237
1 files changed, 167 insertions, 70 deletions
diff --git a/numpy/core/src/multiarray/datetime_strings.c b/numpy/core/src/multiarray/datetime_strings.c
index 692270f4b..a3964c000 100644
--- a/numpy/core/src/multiarray/datetime_strings.c
+++ b/numpy/core/src/multiarray/datetime_strings.c
@@ -24,6 +24,166 @@
#include "_datetime.h"
#include "datetime_strings.h"
+/* Platform-specific time_t typedef */
+typedef time_t NPY_TIME_T;
+
+/*
+ * Wraps `localtime` functionality for multiple platforms. This
+ * converts a time value to a time structure in the local timezone.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+get_localtime(NPY_TIME_T *ts, struct tm *tms)
+{
+ char *func_name = "<unknown>";
+#if defined(_WIN32)
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400)
+ if (localtime_s(tms, ts) != 0) {
+ func_name = "localtime_s";
+ goto fail;
+ }
+ #elif defined(__GNUC__) && defined(NPY_MINGW_USE_CUSTOM_MSVCR)
+ if (_localtime64_s(tms, ts) != 0) {
+ func_name = "_localtime64_s";
+ goto fail;
+ }
+ #else
+ struct tm *tms_tmp;
+ tms_tmp = localtime(ts);
+ if (tms_tmp == NULL) {
+ func_name = "localtime";
+ goto fail;
+ }
+ memcpy(tms, tms_tmp, sizeof(struct tm));
+ #endif
+#else
+ if (localtime_r(ts, tms) == NULL) {
+ func_name = "localtime_r";
+ goto fail;
+ }
+#endif
+
+ return 0;
+
+fail:
+ PyErr_Format(PyExc_OSError, "Failed to use '%s' to convert "
+ "to a local time", func_name);
+ return -1;
+}
+
+/*
+ * Wraps `gmtime` functionality for multiple platforms. This
+ * converts a time value to a time structure in UTC.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+get_gmtime(NPY_TIME_T *ts, struct tm *tms)
+{
+ char *func_name = "<unknown>";
+#if defined(_WIN32)
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400)
+ if (gmtime_s(tms, ts) != 0) {
+ func_name = "gmtime_s";
+ goto fail;
+ }
+ #elif defined(__GNUC__) && defined(NPY_MINGW_USE_CUSTOM_MSVCR)
+ if (_gmtime64_s(tms, ts) != 0) {
+ func_name = "_gmtime64_s";
+ goto fail;
+ }
+ #else
+ struct tm *tms_tmp;
+ tms_tmp = gmtime(ts);
+ if (tms_tmp == NULL) {
+ func_name = "gmtime";
+ goto fail;
+ }
+ memcpy(tms, tms_tmp, sizeof(struct tm));
+ #endif
+#else
+ if (gmtime_r(ts, tms) == NULL) {
+ func_name = "gmtime_r";
+ goto fail;
+ }
+#endif
+
+ return 0;
+
+fail:
+ PyErr_Format(PyExc_OSError, "Failed to use '%s' to convert "
+ "to a UTC time", func_name);
+ return -1;
+}
+
+/*
+ * Converts a datetimestruct in UTC to a datetimestruct in local time,
+ * also returning the timezone offset applied.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+convert_datetimestruct_utc_to_local(npy_datetimestruct *out_dts_local,
+ const npy_datetimestruct *dts_utc, int *out_timezone_offset)
+{
+ NPY_TIME_T rawtime = 0, localrawtime;
+ struct tm tm_;
+ npy_int64 year_correction = 0;
+
+ /* Make a copy of the input 'dts' to modify */
+ *out_dts_local = *dts_utc;
+
+ /* HACK: Use a year < 2038 for later years for small time_t */
+ if (sizeof(NPY_TIME_T) == 4 && out_dts_local->year >= 2038) {
+ if (is_leapyear(out_dts_local->year)) {
+ /* 2036 is a leap year */
+ year_correction = out_dts_local->year - 2036;
+ out_dts_local->year -= year_correction;
+ }
+ else {
+ /* 2037 is not a leap year */
+ year_correction = out_dts_local->year - 2037;
+ out_dts_local->year -= year_correction;
+ }
+ }
+
+ /*
+ * Convert everything in 'dts' to a time_t, to minutes precision.
+ * This is POSIX time, which skips leap-seconds, but because
+ * we drop the seconds value from the npy_datetimestruct, everything
+ * is ok for this operation.
+ */
+ rawtime = (time_t)get_datetimestruct_days(out_dts_local) * 24 * 60 * 60;
+ rawtime += dts_utc->hour * 60 * 60;
+ rawtime += dts_utc->min * 60;
+
+ /* localtime converts a 'time_t' into a local 'struct tm' */
+ if (get_localtime(&rawtime, &tm_) < 0) {
+ return -1;
+ }
+
+ /* Copy back all the values except seconds */
+ out_dts_local->min = tm_.tm_min;
+ out_dts_local->hour = tm_.tm_hour;
+ out_dts_local->day = tm_.tm_mday;
+ out_dts_local->month = tm_.tm_mon + 1;
+ out_dts_local->year = tm_.tm_year + 1900;
+
+ /* Extract the timezone offset that was applied */
+ rawtime /= 60;
+ localrawtime = (time_t)get_datetimestruct_days(out_dts_local) * 24 * 60;
+ localrawtime += out_dts_local->hour * 60;
+ localrawtime += out_dts_local->min;
+
+ *out_timezone_offset = localrawtime - rawtime;
+
+ /* Reapply the year 2038 year correction HACK */
+ out_dts_local->year += year_correction;
+
+ return 0;
+}
+
/*
* Parses (almost) standard ISO 8601 date strings. The differences are:
*
@@ -127,24 +287,13 @@ parse_iso_8601_datetime(char *str, int len,
tolower(str[2]) == 'd' &&
tolower(str[3]) == 'a' &&
tolower(str[4]) == 'y') {
- time_t rawtime = 0;
+ NPY_TIME_T rawtime = 0;
struct tm tm_;
time(&rawtime);
-#if defined(_WIN32)
- if (localtime_s(&tm_, &rawtime) != 0) {
- PyErr_SetString(PyExc_OSError, "Failed to obtain local time "
- "from localtime_s");
+ if (get_localtime(&rawtime, &tm_) < 0) {
return -1;
}
-#else
- /* Other platforms may require something else */
- if (localtime_r(&rawtime, &tm_) == NULL) {
- PyErr_SetString(PyExc_OSError, "Failed obtain local time "
- "from localtime_r");
- return -1;
- }
-#endif
out->year = tm_.tm_year + 1900;
out->month = tm_.tm_mon + 1;
out->day = tm_.tm_mday;
@@ -182,7 +331,7 @@ parse_iso_8601_datetime(char *str, int len,
if (len == 3 && tolower(str[0]) == 'n' &&
tolower(str[1]) == 'o' &&
tolower(str[2]) == 'w') {
- time_t rawtime = 0;
+ NPY_TIME_T rawtime = 0;
PyArray_DatetimeMetaData meta;
time(&rawtime);
@@ -512,7 +661,7 @@ parse_timezone:
* Only do this timezone adjustment for recent and future years.
*/
if (out->year > 1900 && out->year < 10000) {
- time_t rawtime = 0;
+ NPY_TIME_T rawtime = 0;
struct tm tm_;
tm_.tm_sec = out->sec;
@@ -532,20 +681,9 @@ parse_timezone:
}
/* gmtime converts a 'time_t' into a UTC 'struct tm' */
-#if defined(_WIN32)
- if (gmtime_s(&tm_, &rawtime) != 0) {
- PyErr_SetString(PyExc_OSError, "Failed to use gmtime_s to "
- "get a UTC time");
- goto error;
- }
-#else
- /* Other platforms may require something else */
- if (gmtime_r(&rawtime, &tm_) == NULL) {
- PyErr_SetString(PyExc_OSError, "Failed to use gmtime_r to "
- "get a UTC time");
+ if (get_gmtime(&rawtime, &tm_) < 0) {
goto error;
}
-#endif
out->sec = tm_.tm_sec;
out->min = tm_.tm_min;
out->hour = tm_.tm_hour;
@@ -870,51 +1008,10 @@ make_iso_8601_datetime(npy_datetimestruct *dts, char *outstr, int outlen,
/* Use the C API to convert from UTC to local time */
if (local && tzoffset == -1) {
- time_t rawtime = 0, localrawtime;
- struct tm tm_;
-
- /*
- * Convert everything in 'dts' to a time_t, to minutes precision.
- * This is POSIX time, which skips leap-seconds, but because
- * we drop the seconds value from the npy_datetimestruct, everything
- * is ok for this operation.
- */
- rawtime = (time_t)get_datetimestruct_days(dts) * 24 * 60 * 60;
- rawtime += dts->hour * 60 * 60;
- rawtime += dts->min * 60;
-
- /* localtime converts a 'time_t' into a local 'struct tm' */
-#if defined(_WIN32)
- if (localtime_s(&tm_, &rawtime) != 0) {
- PyErr_SetString(PyExc_OSError, "Failed to use localtime_s to "
- "get a local time");
- return -1;
- }
-#else
- /* Other platforms may require something else */
- if (localtime_r(&rawtime, &tm_) == NULL) {
- PyErr_SetString(PyExc_OSError, "Failed to use localtime_r to "
- "get a local time");
+ if (convert_datetimestruct_utc_to_local(&dts_local, dts,
+ &timezone_offset) < 0) {
return -1;
}
-#endif
- /* Make a copy of the npy_datetimestruct we can modify */
- dts_local = *dts;
-
- /* Copy back all the values except seconds */
- dts_local.min = tm_.tm_min;
- dts_local.hour = tm_.tm_hour;
- dts_local.day = tm_.tm_mday;
- dts_local.month = tm_.tm_mon + 1;
- dts_local.year = tm_.tm_year + 1900;
-
- /* Extract the timezone offset that was applied */
- rawtime /= 60;
- localrawtime = (time_t)get_datetimestruct_days(&dts_local) * 24 * 60;
- localrawtime += dts_local.hour * 60;
- localrawtime += dts_local.min;
-
- timezone_offset = localrawtime - rawtime;
/* Set dts to point to our local time instead of the UTC time */
dts = &dts_local;