Skip to content

Commit 4f8c2ae

Browse files
committed
restore tm_yday calculation, improve time test
1 parent 64c70c3 commit 4f8c2ae

File tree

2 files changed

+28
-25
lines changed

2 files changed

+28
-25
lines changed

Lib/test/test_time.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,13 +501,13 @@ def test_localtime_without_arg(self):
501501
def test_mktime(self):
502502
# Issue #1726687
503503
for t in (-2, -1, 0, 1):
504+
t_struct = time.localtime(t)
504505
try:
505-
tt = time.localtime(t)
506-
ts = time.mktime(tt)
506+
t1 = time.mktime(t_struct)
507507
except (OverflowError, OSError):
508508
pass
509509
else:
510-
self.assertEqual(ts, t)
510+
self.assertEqual(t1, t)
511511

512512
# Issue #13309: passing extreme values to mktime() or localtime()
513513
# borks the glibc's internal timezone data.

Python/pytime.c

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,28 @@ _PyTime_AsCLong(PyTime_t t, long *t2)
278278
#define SECS_BETWEEN_EPOCHS 11644473600LL /* Seconds between 1601-01-01 and 1970-01-01 */
279279
#define HUNDRED_NS_PER_SEC 10000000LL
280280

281+
// Calculate day of year (0-365) from SYSTEMTIME
282+
static int
283+
_PyTime_calc_yday(const SYSTEMTIME *st)
284+
{
285+
// Cumulative days before each month (non-leap year)
286+
static const int days_before_month[] = {
287+
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
288+
};
289+
int yday = days_before_month[st->wMonth - 1] + st->wDay - 1;
290+
// Account for leap day if we're past February in a leap year.
291+
if (st->wMonth > 2) {
292+
// Leap year rules (Gregorian calendar):
293+
// - Years divisible by 4 are leap years
294+
// - EXCEPT years divisible by 100 are NOT leap years
295+
// - EXCEPT years divisible by 400 ARE leap years
296+
int year = st->wYear;
297+
int is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
298+
yday += is_leap;
299+
}
300+
return yday;
301+
}
302+
281303
// Convert time_t to struct tm using Windows FILETIME API.
282304
// If is_local is true, convert to local time. */
283305
// Fallback for negative timestamps that localtime_s/gmtime_s cannot handle.
@@ -325,28 +347,9 @@ _PyTime_windows_filetime(time_t timer, struct tm *tm, int is_local)
325347
tm->tm_sec = st_result.wSecond;
326348
tm->tm_wday = st_result.wDayOfWeek; /* 0=Sunday */
327349

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+
// `time.gmtime` and `time.localtime` will return `struct_time` containing this
351+
// not currently used by `datetime` module
352+
tm->tm_yday = _PyTime_calc_yday(&st_result);
350353

351354
/* DST flag: -1 (unknown) for local time on historical dates, 0 for UTC */
352355
tm->tm_isdst = is_local ? -1 : 0;

0 commit comments

Comments
 (0)