Skip to content

Commit

Permalink
rktio: fix Windows file timestamp dst correction
Browse files Browse the repository at this point in the history
The old correction was broken in at least a couple of ways; use
the more tested DST calculation in the implementation of
`seconds->date`.
  • Loading branch information
mflatt committed Mar 30, 2018
1 parent e95c3fe commit d3aa7e9
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 59 deletions.
45 changes: 3 additions & 42 deletions racket/src/rktio/rktio_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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. */
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. */
if (!rktio_system_time_is_dst(&st, NULL))
delta = ((tz.StandardBias - tz.DaylightBias) * 60);
}
}

l = ((((LONGLONG)ft->dwHighDateTime << 32) | ft->dwLowDateTime)
Expand Down
5 changes: 5 additions & 0 deletions racket/src/rktio/rktio_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

41 changes: 24 additions & 17 deletions racket/src/rktio/rktio_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit d3aa7e9

Please sign in to comment.