Skip to content

Commit c8eab29

Browse files
committed
init
1 parent 04ace41 commit c8eab29

File tree

1 file changed

+106
-20
lines changed

1 file changed

+106
-20
lines changed

Python/pytime.c

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,86 @@ _PyTime_AsCLong(PyTime_t t, long *t2)
273273
*t2 = (long)t;
274274
return 0;
275275
}
276+
277+
// 369 years + 89 leap days
278+
#define SECS_BETWEEN_EPOCHS 11644473600LL /* Seconds between 1601-01-01 and 1970-01-01 */
279+
#define HUNDRED_NS_PER_SEC 10000000LL
280+
281+
// Convert time_t to struct tm using Windows FILETIME API.
282+
// If is_local is true, convert to local time. */
283+
// Fallback for negative timestamps that localtime_s/gmtime_s cannot handle.
284+
// Return 0 on success. Return -1 on error.
285+
static int
286+
_PyTime_windows_filetime(time_t timer, struct tm *tm, int is_local)
287+
{
288+
/* Check for underflow - FILETIME epoch is 1601-01-01 */
289+
if (timer < -SECS_BETWEEN_EPOCHS) {
290+
PyErr_SetString(PyExc_OverflowError, "timestamp out of range for Windows FILETIME");
291+
return -1;
292+
}
293+
294+
/* Convert time_t to FILETIME (100-nanosecond intervals since 1601-01-01) */
295+
ULONGLONG ticks = ((ULONGLONG)timer + SECS_BETWEEN_EPOCHS) * HUNDRED_NS_PER_SEC;
296+
FILETIME ft;
297+
ft.dwLowDateTime = (DWORD)(ticks); // cast to DWORD truncates to low 32 bits
298+
ft.dwHighDateTime = (DWORD)(ticks >> 32);
299+
300+
/* Convert FILETIME to SYSTEMTIME */
301+
SYSTEMTIME st_result;
302+
if (is_local) {
303+
/* Convert to local time */
304+
FILETIME ft_local;
305+
if (!FileTimeToLocalFileTime(&ft, &ft_local) ||
306+
!FileTimeToSystemTime(&ft_local, &st_result)) {
307+
PyErr_SetFromWindowsErr(0);
308+
return -1;
309+
}
310+
}
311+
else {
312+
/* Convert to UTC */
313+
if (!FileTimeToSystemTime(&ft, &st_result)) {
314+
PyErr_SetFromWindowsErr(0);
315+
return -1;
316+
}
317+
}
318+
319+
/* Convert SYSTEMTIME to struct tm */
320+
tm->tm_year = st_result.wYear - 1900;
321+
tm->tm_mon = st_result.wMonth - 1; /* SYSTEMTIME: 1-12, tm: 0-11 */
322+
tm->tm_mday = st_result.wDay;
323+
tm->tm_hour = st_result.wHour;
324+
tm->tm_min = st_result.wMinute;
325+
tm->tm_sec = st_result.wSecond;
326+
tm->tm_wday = st_result.wDayOfWeek; /* 0=Sunday */
327+
328+
/* Calculate day of year using Windows FILETIME difference */
329+
// SYSTEMTIME st_jan1 = {st_result.wYear, 1, 0, 1, 0, 0, 0, 0};
330+
// FILETIME ft_jan1, ft_date;
331+
// if (!SystemTimeToFileTime(&st_jan1, &ft_jan1) ||
332+
// !SystemTimeToFileTime(&st_result, &ft_date)) {
333+
// PyErr_SetFromWindowsErr(0);
334+
// return -1;
335+
// }
336+
// ULARGE_INTEGER jan1, date;
337+
// jan1.LowPart = ft_jan1.dwLowDateTime;
338+
// jan1.HighPart = ft_jan1.dwHighDateTime;
339+
// date.LowPart = ft_date.dwLowDateTime;
340+
// date.HighPart = ft_date.dwHighDateTime;
341+
// /* Convert 100-nanosecond intervals to days */
342+
// LONGLONG days_diff = (date.QuadPart - jan1.QuadPart) / (24LL * 60 * 60 * HUNDRED_NS_PER_SEC);
343+
344+
// tm->tm_yday = (int)days_diff;
345+
346+
// datetime doesn't rely on tm_yday, so set invalid value and skip calculation
347+
// time.gmtime / time.localtime will return struct_time with out of range tm_yday
348+
// time.mktime doesn't support pre-epoch struct_time on windows anyway
349+
tm->tm_yday = -1;
350+
351+
/* DST flag: -1 (unknown) for local time on historical dates, 0 for UTC */
352+
tm->tm_isdst = is_local ? -1 : 0;
353+
354+
return 0;
355+
}
276356
#endif
277357

278358

@@ -882,10 +962,8 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
882962
GetSystemTimePreciseAsFileTime(&system_time);
883963
large.u.LowPart = system_time.dwLowDateTime;
884964
large.u.HighPart = system_time.dwHighDateTime;
885-
/* 11,644,473,600,000,000,000: number of nanoseconds between
886-
the 1st january 1601 and the 1st january 1970 (369 years + 89 leap
887-
days). */
888-
PyTime_t ns = (large.QuadPart - 116444736000000000) * 100;
965+
966+
PyTime_t ns = (large.QuadPart - SECS_BETWEEN_EPOCHS * HUNDRED_NS_PER_SEC) * 100;
889967
*tp = ns;
890968
if (info) {
891969
// GetSystemTimePreciseAsFileTime() is implemented using
@@ -1242,15 +1320,19 @@ int
12421320
_PyTime_localtime(time_t t, struct tm *tm)
12431321
{
12441322
#ifdef MS_WINDOWS
1245-
int error;
1246-
1247-
error = localtime_s(tm, &t);
1248-
if (error != 0) {
1249-
errno = error;
1250-
PyErr_SetFromErrno(PyExc_OSError);
1251-
return -1;
1323+
if (t >= 0) {
1324+
/* For non-negative timestamps, use standard conversion */
1325+
int error = localtime_s(tm, &t);
1326+
if (error != 0) {
1327+
errno = error;
1328+
PyErr_SetFromErrno(PyExc_OSError);
1329+
return -1;
1330+
}
1331+
return 0;
12521332
}
1253-
return 0;
1333+
1334+
/* For negative timestamps, use FILETIME-based conversion */
1335+
return _PyTime_windows_filetime(t, tm, 1);
12541336
#else /* !MS_WINDOWS */
12551337

12561338
#if defined(_AIX) && (SIZEOF_TIME_T < 8)
@@ -1281,15 +1363,19 @@ int
12811363
_PyTime_gmtime(time_t t, struct tm *tm)
12821364
{
12831365
#ifdef MS_WINDOWS
1284-
int error;
1285-
1286-
error = gmtime_s(tm, &t);
1287-
if (error != 0) {
1288-
errno = error;
1289-
PyErr_SetFromErrno(PyExc_OSError);
1290-
return -1;
1366+
/* For non-negative timestamps, use standard conversion */
1367+
if (t >= 0) {
1368+
int error = gmtime_s(tm, &t);
1369+
if (error != 0) {
1370+
errno = error;
1371+
PyErr_SetFromErrno(PyExc_OSError);
1372+
return -1;
1373+
}
1374+
return 0;
12911375
}
1292-
return 0;
1376+
1377+
/* For negative timestamps, use FILETIME-based conversion */
1378+
return _PyTime_windows_filetime(t, tm, 0);
12931379
#else /* !MS_WINDOWS */
12941380
if (gmtime_r(&t, tm) == NULL) {
12951381
#ifdef EINVAL

0 commit comments

Comments
 (0)