From d3aa7e90e7c27cfbf6adad8db2e2c3ba6abac6a7 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Thu, 29 Mar 2018 20:30:19 -0600 Subject: [PATCH] rktio: fix Windows file timestamp dst correction The old correction was broken in at least a couple of ways; use the more tested DST calculation in the implementation of `seconds->date`. --- racket/src/rktio/rktio_fs.c | 45 +++----------------------------- racket/src/rktio/rktio_private.h | 5 ++++ racket/src/rktio/rktio_time.c | 41 +++++++++++++++++------------ 3 files changed, 32 insertions(+), 59 deletions(-) diff --git a/racket/src/rktio/rktio_fs.c b/racket/src/rktio/rktio_fs.c index f78973de8f..eb967911b5 100644 --- a/racket/src/rktio/rktio_fs.c +++ b/racket/src/rktio/rktio_fs.c @@ -129,12 +129,12 @@ static time_t convert_date(const FILETIME *ft) SYSTEMTIME st; TIME_ZONE_INFORMATION tz; - /* FindFirstFile incorrectly shifts for daylight saving. It + /* GetFileAttributes incorrectly shifts for daylight saving. It subtracts an hour to get to UTC when daylight saving is in effect now, even when daylight saving was not in effect when the file was saved. Counteract the difference. There's a race condition here, because we might cross the daylight-saving boundary between - the time that FindFirstFile runs and GetTimeZoneInformation + the time that GetFileAttributes runs and GetTimeZoneInformation runs. Cross your fingers... */ FileTimeToLocalFileTime(ft, &ft2); FileTimeToSystemTime(&ft2, &st); @@ -143,47 +143,8 @@ static time_t convert_date(const FILETIME *ft) if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_DAYLIGHT) { /* Daylight saving is in effect now, so there may be a bad shift. Check the file's date. */ - int start_day_of_month, end_day_of_month, first_day_of_week, diff, end_shift; - - /* Valid only when the months match: */ - first_day_of_week = (st.wDayOfWeek - (st.wDay - 1 - (((st.wDay - 1) / 7) * 7))); - if (first_day_of_week < 0) - first_day_of_week += 7; - - diff = (tz.DaylightDate.wDayOfWeek - first_day_of_week); - if (diff < 0) - diff += 7; - start_day_of_month = 1 + (((tz.DaylightDate.wDay - 1) * 7) - + diff); - - diff = (tz.StandardDate.wDayOfWeek - first_day_of_week); - if (diff < 0) - diff += 7; - end_day_of_month = 1 + (((tz.StandardDate.wDay - 1) * 7) - + diff); - - /* Count ambigious range (when the clock goes back) as - in standard time. We assume that subtracting the - ambiguous range does not go back into the previous day, - and that the shift is a multiple of an hour. */ - end_shift = ((tz.StandardBias - tz.DaylightBias) / 60); - - if ((st.wMonth < tz.DaylightDate.wMonth) - || ((st.wMonth == tz.DaylightDate.wMonth) - && ((st.wDay < start_day_of_month) - || ((st.wDay == start_day_of_month) - && (st.wHour < tz.DaylightDate.wHour))))) { - /* Daylight saving had not yet started. */ + if (!rktio_system_time_is_dst(&st, NULL)) delta = ((tz.StandardBias - tz.DaylightBias) * 60); - } else if ((st.wMonth > tz.StandardDate.wMonth) - || ((st.wMonth == tz.StandardDate.wMonth) - && ((st.wDay > end_day_of_month) - || ((st.wDay == end_day_of_month) - && (st.wHour >= (tz.StandardDate.wHour - - end_shift)))))) { - /* Daylight saving was already over. */ - delta = ((tz.StandardBias - tz.DaylightBias) * 60); - } } l = ((((LONGLONG)ft->dwHighDateTime << 32) | ft->dwLowDateTime) diff --git a/racket/src/rktio/rktio_private.h b/racket/src/rktio/rktio_private.h index 6ad662900b..8306056931 100644 --- a/racket/src/rktio/rktio_private.h +++ b/racket/src/rktio/rktio_private.h @@ -360,3 +360,8 @@ char *rktio_strndup(char *s, intptr_t len); void rktio_set_signal_handler(int sig_id, void (*proc)(int)); #endif void rktio_forget_os_signal_handler(rktio_t *rktio); + +#ifdef RKTIO_SYSTEM_WINDOWS +int rktio_system_time_is_dst(SYSTEMTIME *st, TIME_ZONE_INFORMATION *_tz); +#endif + diff --git a/racket/src/rktio/rktio_time.c b/racket/src/rktio/rktio_time.c index 442f4491b5..1d1c903f53 100644 --- a/racket/src/rktio/rktio_time.c +++ b/racket/src/rktio/rktio_time.c @@ -258,6 +258,28 @@ static int is_day_before(SYSTEMTIME *a, SYSTEMTIME *b) return 0; } # undef dtxCOMP + +int rktio_system_time_is_dst(SYSTEMTIME *st, TIME_ZONE_INFORMATION *_tz) +{ + TIME_ZONE_INFORMATION tz; + if (GetTimeZoneInformationForYearProc) + GetTimeZoneInformationForYearProc(st->wYear, NULL, &tz); + else + (void)GetTimeZoneInformation(&tz); + if (_tz) *_tz = tz; + if (tz.StandardDate.wMonth) { + if (is_start_day_before(&tz.DaylightDate, &tz.StandardDate)) { + /* northern hemisphere */ + return (!is_day_before(st, &tz.DaylightDate) + && is_day_before(st, &tz.StandardDate)); + } else { + /* southern hemisphere */ + return (is_day_before(st, &tz.StandardDate) + || !is_day_before(st, &tz.DaylightDate)); + } + } + return 0; +} #endif #if defined(OS_X) && defined(__x86_64__) @@ -378,23 +400,8 @@ rktio_date_t *rktio_seconds_to_date(rktio_t *rktio, rktio_timestamp_t seconds, i tzoffset = 0; tzn = MSC_IZE(strdup)("UTC"); } else { - TIME_ZONE_INFORMATION tz; - if (GetTimeZoneInformationForYearProc) - GetTimeZoneInformationForYearProc(localTime.wYear, NULL, &tz); - else - (void)GetTimeZoneInformation(&tz); - if (tz.StandardDate.wMonth) { - if (is_start_day_before(&tz.DaylightDate, &tz.StandardDate)) { - /* northern hemisphere */ - dst = (!is_day_before(&localTime, &tz.DaylightDate) - && is_day_before(&localTime, &tz.StandardDate)); - } else { - /* southern hemisphere */ - dst = (is_day_before(&localTime, &tz.StandardDate) - || !is_day_before(&localTime, &tz.DaylightDate)); - } - } - if (dst) { + TIME_ZONE_INFORMATION tz; + if (rktio_system_time_is_dst(&localTime, &tz)) { tzoffset = (tz.Bias + tz.DaylightBias) * -60; tzn = NARROW_PATH_copy(tz.DaylightName); } else {