toTime(getGMTime(...)) doesn't work correctly when local timezone is not UTC #5065

Closed
MednauN opened this Issue Nov 28, 2016 · 7 comments

Projects

None yet

3 participants

@MednauN
Contributor
MednauN commented Nov 28, 2016

Sample program:

import times
echo int64(fromSeconds(0).getLocalTime().toTime())
echo int64(fromSeconds(0).getGMTime().toTime())

Output:

0
25199

Expected output:

0
0

Environment: OS Win10, timezone: UTC +7

When I change local timezone to UTC, this code works fine.

@MednauN
Contributor
MednauN commented Nov 28, 2016

Found out that the issue doesn't appear on macOS. Seems like it's Windows-only.

@flyx
Contributor
flyx commented Nov 28, 2016

Can you give the output of

echo fromSeconds(0).getLocalTime()
echo fromSeconds(0).getGMTime()

I would run it myself, but I don't have any functioning Windows environment currently.

@Araq
Member
Araq commented Nov 28, 2016

@flxy on my machine

1970-01-01T01:00:00+01:00
1970-01-01T00:00:00+00:00
@MednauN
Contributor
MednauN commented Nov 29, 2016

@flyx

1970-01-01T07:00:00+07:00
1970-01-01T00:00:00+00:00
@flyx
Contributor
flyx commented Nov 29, 2016

This works correctly:

echo fromSeconds(3600).getLocalTime()
echo fromSeconds(3600).getGMTime()

Output (+01:00; wil not work on +07:00, try 3600 * 7 there):

3600
3600

The problem is that on Windows, mktime returns -1 if the given date interpreted as local time lies before January 1, 1970. On OSX, it returns the correct negative amount of seconds. So this seems to be a Windows limitation, and the only thing that can be done here is to check the result of mktime for -1 on Windows and if true, raise some exception.

@MednauN
Contributor
MednauN commented Nov 30, 2016

Yes, works correctly starting with 3600 * 7 - 1 for UTC +7.
Checked the output of mktime, it returns -1 indeed. And if I understand correctly, since mktime accepts local time, TimeInfo is being converted to local time before calling mktime and there is where the problem lies.
So, if add, like, 1 day to TimeInfo before calling toTime and substract 1 day after that, everyting will work fine. Something like that should do the trick:

proc toTimeSafe*(timeInfo: TimeInfo): Time =
  when defined(windows):
    if timeInfo.year == 1970 and timeInfo.month == mJan and timeInfo.monthDay == 1:
      var safeTimeInfo = timeInfo
      safeTimeInfo.monthDay += 1
      return safeTimeInfo.toTime() - 1.days
  return timeInfo.toTime()
@MednauN
Contributor
MednauN commented Nov 30, 2016

Found function _mkgmtime on Windows that accepts time in GMT rather than local time. Using this instead of mktime may also help. And for Linux there is timegm which do the same.

@Araq Araq closed this in 50a1267 Dec 6, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment