@@ -51,6 +51,77 @@ This file implements string parsing and creation for NumPy datetime.
51
51
typedef time_t NPY_TIME_T ;
52
52
#endif
53
53
54
+ /*
55
+ * Wraps `localtime` functionality for multiple platforms. This
56
+ * converts a time value to a time structure in the local timezone.
57
+ * If size(NPY_TIME_T) == 4, then years must be between 1970 and 2038. If
58
+ * size(NPY_TIME_T) == 8, then years must be later than 1970. If the years are
59
+ * not in this range, then get_localtime() will fail on some platforms.
60
+ *
61
+ * Returns 0 on success, -1 on failure.
62
+ *
63
+ * Notes:
64
+ * 1) If NPY_TIME_T is 32 bit (i.e. sizeof(NPY_TIME_T) == 4), then the
65
+ * maximum year it can represent is 2038 (see [1] for more details). Trying
66
+ * to use a higher date like 2041 in the 32 bit "ts" variable below will
67
+ * typically result in "ts" being a negative number (corresponding roughly
68
+ * to a year ~ 1905). If NPY_TIME_T is 64 bit, then there is no such
69
+ * problem in practice.
70
+ * 2) If the "ts" argument to localtime() is negative, it represents
71
+ * years < 1970 both for 32 and 64 bits (for 32 bits the earliest year it can
72
+ * represent is 1901, while 64 bits can represent much earlier years).
73
+ * 3) On Linux, localtime() works for negative "ts". On Windows and in Wine,
74
+ * localtime() as well as the localtime_s() and _localtime64_s() functions
75
+ * will fail for any negative "ts" and return a nonzero exit number
76
+ * (localtime_s, _localtime64_s) or NULL (localtime). This behavior is the
77
+ * same for both 32 and 64 bits.
78
+ *
79
+ * From this it follows that get_localtime() is only guaranteed to work
80
+ * correctly on all platforms for years between 1970 and 2038 for 32bit
81
+ * NPY_TIME_T and years higher than 1970 for 64bit NPY_TIME_T. For
82
+ * multiplatform code, get_localtime() should never be used outside of this
83
+ * range.
84
+ *
85
+ * [1] https://en.wikipedia.org/wiki/Year_2038_problem
86
+ */
87
+ static int
88
+ get_localtime (NPY_TIME_T * ts , struct tm * tms )
89
+ {
90
+ char * func_name = "<unknown>" ;
91
+ #if defined(_WIN32 )
92
+ #if defined(_MSC_VER ) && (_MSC_VER >= 1400 )
93
+ if (localtime_s (tms , ts ) != 0 ) {
94
+ func_name = "localtime_s" ;
95
+ goto fail ;
96
+ }
97
+ #elif defined(NPY_MINGW_USE_CUSTOM_MSVCR )
98
+ if (_localtime64_s (tms , ts ) != 0 ) {
99
+ func_name = "_localtime64_s" ;
100
+ goto fail ;
101
+ }
102
+ #else
103
+ struct tm * tms_tmp ;
104
+ tms_tmp = localtime (ts );
105
+ if (tms_tmp == NULL ) {
106
+ func_name = "localtime" ;
107
+ goto fail ;
108
+ }
109
+ memcpy (tms , tms_tmp , sizeof (struct tm ));
110
+ #endif
111
+ #else
112
+ if (localtime_r (ts , tms ) == NULL ) {
113
+ func_name = "localtime_r" ;
114
+ goto fail ;
115
+ }
116
+ #endif
117
+
118
+ return 0 ;
119
+
120
+ fail :
121
+ PyErr_Format (PyExc_OSError , "Failed to use '%s' to convert "
122
+ "to a local time" , func_name );
123
+ return -1 ;
124
+ }
54
125
55
126
/*
56
127
* Parses (almost) standard ISO 8601 date strings. The differences are:
0 commit comments