mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-02 03:02:31 +00:00
Fixed daylight saving time
* When formatting timezone, substract 1 hour from timezone when isDST * Do not depend DST in current timezone when parsing arbitrary date because formatted timestamps are never in DST. * On the way, removed an unnecessary line in parsing code which could cause bugs. * Added DST tests
This commit is contained in:
@@ -144,8 +144,9 @@ type
|
||||
yearday*: range[0..365] ## The number of days since January 1,
|
||||
## in the range 0 to 365.
|
||||
## Always 0 if the target is JS.
|
||||
isDST*: bool ## Determines whether DST is in effect. Always
|
||||
## ``False`` if time is UTC.
|
||||
isDST*: bool ## Determines whether DST is in effect.
|
||||
## Semantically, this adds another negative hour
|
||||
## offset to the time in addition to the timezone.
|
||||
timezone*: int ## The offset of the (non-DST) timezone in seconds
|
||||
## west of UTC. Note that the sign of this number
|
||||
## is the opposite of the one in a formatted
|
||||
@@ -824,28 +825,32 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
||||
if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
|
||||
buf.add(fyear)
|
||||
of "z":
|
||||
let hours = abs(info.timezone) div secondsInHour
|
||||
if info.timezone <= 0: buf.add('+')
|
||||
let
|
||||
nonDstTz = info.timezone - int(info.isDst) * secondsInHour
|
||||
hours = abs(nonDstTz) div secondsInHour
|
||||
if nonDstTz <= 0: buf.add('+')
|
||||
else: buf.add('-')
|
||||
buf.add($hours)
|
||||
of "zz":
|
||||
let hours = abs(info.timezone) div secondsInHour
|
||||
if info.timezone <= 0: buf.add('+')
|
||||
let
|
||||
nonDstTz = info.timezone - int(info.isDst) * secondsInHour
|
||||
hours = abs(nonDstTz) div secondsInHour
|
||||
if nonDstTz <= 0: buf.add('+')
|
||||
else: buf.add('-')
|
||||
if hours < 10: buf.add('0')
|
||||
buf.add($hours)
|
||||
of "zzz":
|
||||
let
|
||||
hours = abs(info.timezone) div secondsInHour
|
||||
minutes = (abs(info.timezone) div secondsInMin) mod minutesInHour
|
||||
if info.timezone <= 0: buf.add('+')
|
||||
nonDstTz = info.timezone - int(info.isDst) * secondsInHour
|
||||
hours = abs(nonDstTz) div secondsInHour
|
||||
minutes = (abs(nonDstTz) div secondsInMin) mod minutesInHour
|
||||
if nonDstTz <= 0: buf.add('+')
|
||||
else: buf.add('-')
|
||||
if hours < 10: buf.add('0')
|
||||
buf.add($hours)
|
||||
buf.add(':')
|
||||
if minutes < 10: buf.add('0')
|
||||
buf.add($minutes)
|
||||
|
||||
of "":
|
||||
discard
|
||||
else:
|
||||
@@ -1000,7 +1005,6 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
||||
of "M":
|
||||
var pd = parseInt(value[j..j+1], sv)
|
||||
info.month = Month(sv-1)
|
||||
info.monthday = sv
|
||||
j += pd
|
||||
of "MM":
|
||||
var month = value[j..j+1].parseInt()
|
||||
@@ -1166,6 +1170,7 @@ proc parse*(value, layout: string): TimeInfo =
|
||||
info.hour = 0
|
||||
info.minute = 0
|
||||
info.second = 0
|
||||
info.isDST = false # DST is never encoded in timestamps.
|
||||
while true:
|
||||
case layout[i]
|
||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||
@@ -1195,14 +1200,6 @@ proc parse*(value, layout: string): TimeInfo =
|
||||
parseToken(info, token, value, j)
|
||||
token = ""
|
||||
|
||||
# We are going to process the date to find out if we are in DST, because the
|
||||
# default based on the current time may be wrong. Calling getLocalTime will
|
||||
# set this correctly, but the actual time may be offset from when we called
|
||||
# toTime with a possibly incorrect DST setting, so we are only going to take
|
||||
# the isDST from this result.
|
||||
let correctDST = getLocalTime(toTime(info))
|
||||
info.isDST = correctDST.isDST
|
||||
|
||||
# Now we process it again with the correct isDST to correct things like
|
||||
# weekday and yearday.
|
||||
return getLocalTime(toTime(info))
|
||||
|
||||
@@ -201,4 +201,25 @@ for tz in [
|
||||
let ti = TimeInfo(monthday: 1, timezone: tz[0])
|
||||
doAssert ti.format("z") == tz[1]
|
||||
doAssert ti.format("zz") == tz[2]
|
||||
doAssert ti.format("zzz") == tz[3]
|
||||
doAssert ti.format("zzz") == tz[3]
|
||||
|
||||
block dstTest:
|
||||
let nonDst = TimeInfo(year: 2015, month: mJan, monthday: 01, yearday: 0,
|
||||
weekday: dThu, hour: 00, minute: 00, second: 00, isDST: false, timezone: 0)
|
||||
var dst = nonDst
|
||||
dst.isDst = true
|
||||
# note that both isDST == true and isDST == false are valid here because
|
||||
# DST is in effect on January 1st in some southern parts of Australia.
|
||||
|
||||
doAssert nonDst.toTime() - dst.toTime() == 3600
|
||||
doAssert nonDst.format("z") == "+0"
|
||||
doAssert dst.format("z") == "+1"
|
||||
|
||||
# parsing will set isDST in relation to the local time. We take a date in
|
||||
# January and one in July to maximize the probability to hit one date with DST
|
||||
# and one without on the local machine. However, this is not guaranteed.
|
||||
let
|
||||
parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
||||
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-ss HH:mm:sszzz")
|
||||
doAssert toTime(parsedJan) == fromSeconds(1452394800)
|
||||
doAssert toTime(parsedJul) == fromSeconds(1467342000)
|
||||
|
||||
Reference in New Issue
Block a user