Skip to content

Commit

Permalink
Fix FixedTimeZone negative parsing corner case (#133)
Browse files Browse the repository at this point in the history
When parsing a FixedTimeZone where the offset is negative but the hour
is zero the resulting FixedTimeZone would have the offset be positive.
  • Loading branch information
omus authored Apr 4, 2018
1 parent 7a544cb commit 13de88e
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 34 deletions.
54 changes: 29 additions & 25 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ import Base: promote_rule, ==, hash, isequal, isless, typemin, typemax
import Compat: xor

const FIXED_TIME_ZONE_REGEX = r"""
^(?|
UTC([+-]\d{1,2})?
^(?|
UTC
(?:
(?<sign>[+-])
(?<hour>\d{1,2})
)?
|
([+-]\d{2})
(?<sign>[+-])
(?<hour>\d{2})
|
(?:UTC(?=[+-]))?
([+-]?\d{2})
(?|
(\d{2})
(?:UTC(?=[+-]))?
(?<sign>[+-])?
(?<hour>\d{2})
(?|
(?(hour)\:(?<minute>\d{2}))
(?(minute)\:(?<second>\d{2}))?
|
\:(\d{2})
(?:\:(\d{2}))?
)
)$
"""x
(?(hour)(?<minute>\d{2}))
)
)$
"""x

# Using type Symbol instead of AbstractString for name since it
# gets us ==, and hash for free.
Expand Down Expand Up @@ -71,25 +77,23 @@ UTC+15:45:21
"""
function FixedTimeZone(s::AbstractString)
m = match(FIXED_TIME_ZONE_REGEX, s)
m == nothing && throw(ArgumentError("Unrecognized time zone: $s"))
m === nothing && throw(ArgumentError("Unrecognized time zone: $s"))

values = map(n -> n == nothing ? 0 : Base.parse(Int, n), m.captures)
coefficient = m[:sign] == "-" ? -1 : 1
sig = coefficient < 0 ? '-' : '+'
hour = m[:hour] === nothing ? 0 : parse(Int, m[:hour])
minute = m[:minute] === nothing ? 0 : parse(Int, m[:minute])
second = m[:second] === nothing ? 0 : parse(Int, m[:second])

if values == [0, 0, 0]
if hour == 0 && minute == 0 && second == 0
name = "UTC"
elseif values[3] == 0
name = @sprintf("UTC%+03d:%02d", values[1:2]...)
elseif second == 0
name = @sprintf("UTC%c%02d:%02d", sig, hour, minute)
else
name = @sprintf("UTC%+03d:%02d:%02d", values...)
end

if values[1] < 0
for i in 2:length(values)
values[i] = -values[i]
end
name = @sprintf("UTC%c%02d:%02d:%02d", sig, hour, minute, second)
end

offset = values[1] * 3600 + values[2] * 60 + values[3]
offset = coefficient * (hour * 3600 + minute * 60 + second)
return FixedTimeZone(name, offset)
end

Expand Down
20 changes: 11 additions & 9 deletions src/utcoffset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,18 @@ end
isless(x::UTCOffset, y::UTCOffset) = isless(value(x), value(y))

function offset_string(seconds::Second, iso8601::Bool=false)
v = value(seconds)
h, v = divrem(v, 3600)
m, s = divrem(abs(v), 60)

if !iso8601 && m == 0 && s == 0
return @sprintf("%+02d", h)
elseif s == 0
return @sprintf("%+03d:%02d", h, m)
val = value(seconds)
sig = val < 0 ? '-' : '+'
hour, val = divrem(abs(val), 3600)
minute, second = divrem(val, 60)

if !iso8601 && minute == 0 && second == 0
return @sprintf("%c%01d", sig, hour)
elseif second == 0
return @sprintf("%c%02d:%02d", sig, hour, minute)
else
return @sprintf("%+03d:%02d:%02d", h, m, s) # Not in the ISO 8601 standard
# Not in the ISO 8601 standard
return @sprintf("%c%02d:%02d:%02d", sig, hour, minute, second)
end
end
function offset_string(offset::UTCOffset, iso8601::Bool=false)
Expand Down
2 changes: 2 additions & 0 deletions test/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import Compat.Dates: Hour, Second, UTM

@test FixedTimeZone("+01") == FixedTimeZone("UTC+01:00", 3600)
@test FixedTimeZone("-02") == FixedTimeZone("UTC-02:00", -7200)
@test FixedTimeZone("+00:30") == FixedTimeZone("UTC+00:30", 1800)
@test FixedTimeZone("-00:30") == FixedTimeZone("UTC-00:30", -1800)

@test_throws ArgumentError FixedTimeZone("1")
@test_throws ArgumentError FixedTimeZone("01")
Expand Down
2 changes: 2 additions & 0 deletions test/utcoffset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import TimeZones: UTCOffset, value, isdst
@test string(UTCOffset(0, 3600)) == "+01:00"
@test string(UTCOffset(-7200, 3600)) == "-01:00"
@test string(UTCOffset(-3661)) == "-01:01:01"
@test string(UTCOffset( 1800)) == "+00:30"
@test string(UTCOffset(-1800)) == "-00:30"

@test !isdst(UTCOffset(0))
@test !isdst(UTCOffset(0, 0))
Expand Down

0 comments on commit 13de88e

Please sign in to comment.