diff --git a/src/mzscheme/src/file.c b/src/mzscheme/src/file.c index eadf6453c4..4b745eae6b 100644 --- a/src/mzscheme/src/file.c +++ b/src/mzscheme/src/file.c @@ -2035,24 +2035,72 @@ char *scheme_expand_string_filename(Scheme_Object *o, const char *errorin, int * # define GET_FF_NAME(fd) fd.cFileName static time_t convert_date(const FILETIME *ft) { - LONGLONG l; + LONGLONG l, delta; FILETIME ft2; - SYSTEMTIME st, st2; + SYSTEMTIME st; + TIME_ZONE_INFORMATION tz; - /* FindFirstFile incorrectly shifts for daylight saving. Counteract - the difference by using FileTimeToLocalFileTime, which also - shifts incorrectly. There's a race condition here, because we - might cross the daylight-saving boundary between the time that - FindFirstFile runs and FileTimeToLocalFileTime runs. Cross your - fingers... */ + /* FindFirstFile 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 + runs. Cross your fingers... */ FileTimeToLocalFileTime(ft, &ft2); FileTimeToSystemTime(&ft2, &st); - TzSpecificLocalTimeToSystemTime(NULL, &st, &st2); - SystemTimeToFileTime(&st2, &ft2); + + delta = 0; + 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; - l = ((((LONGLONG)ft2.dwHighDateTime << 32) | ft2.dwLowDateTime) + /* 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. */ + 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) - (((LONGLONG)0x019DB1DE << 32) | 0xD53E8000)); l /= 10000000; + l += delta; return (time_t)l; }