Provides LaxZonedDateTime, an alternative to TimeZones.jl's ZonedDateTime that does
not raise exceptions when a time that is ambiguous or doesn't exist is encountered.
julia> using LaxZonedDateTimes, TimeZones
julia> winnipeg = TimeZone("America/Winnipeg")
America/Winnipeg (UTC-6/UTC-5)
julia> LaxZonedDateTime(DateTime(2016), winnipeg)
2016-01-01T00:00:00-06:00
julia> LaxZonedDateTime(DateTime(2016, 3, 13, 2, 45), winnipeg)
2016-03-13T02:45:00-DNE
julia> LaxZonedDateTime(DateTime(2016, 11, 6, 1, 45), winnipeg)
2016-11-06T01:45:00-AMBOne of the advantages of using LaxZonedDateTimes is that when you encounter a time
that is ambiguous or doesn't exist, you don't lose the information. For example,
consider the following case:
julia> lzdt = LaxZonedDateTime(DateTime(2016, 3, 12, 2), winnipeg)
2016-03-11T02:00:00-06:00
julia> nonexistent = lzdt + Dates.Day(1)
2016-03-13T02:00:00-DNE
julia> nonexistent + Dates.Day(1)
2016-03-14T02:00:00-05:00In some cases, however, it's difficult to determine what the result should be. While
arithmetic with DatePeriods will always work, attempting to add or subtract a
TimePeriod value from an ambiguous or nonexistent LaxZonedDateTime will result in an
unrepresentable value (which is an unrecoverable state):
julia> lzdt = LaxZonedDateTime(DateTime(2016, 3, 13, 2), winnipeg)
2016-03-13T02:00:00-DNE
julia> lzdt + Dates.Hour(2)
INVALIDYou can test a LaxZonedDateTime for validity using isnonexistent, isambiguous, and
isvalid (the last of which returns false if the value is nonexistent, ambiguous, or
unrepresentable).
julia> lzdt = LaxZonedDateTime(DateTime(2016, 3, 11, 2), winnipeg)
2016-03-11T02:00:00-06:00
julia> r = lzdt:Dates.Day(1):(lzdt + Dates.Day(5))
2016-03-16T02:00:00-05:002016-03-11T02:00:00-06:00
julia> collect(r)
6-element Array{LaxZonedDateTimes.LaxZonedDateTime,1}:
2016-03-11T02:00:00-06:00
2016-03-12T02:00:00-06:00
2016-03-13T02:00:00-DNE
2016-03-14T02:00:00-05:00
2016-03-15T02:00:00-05:00
2016-03-16T02:00:00-05:00Notice that the third element represents a nonexistent time (a time that has no UTC representation).
Note that ambiguous and nonexistent values only occur in places where a ZonedDateTime
would raise an exception. Here, we step right past the nonexistent time:
julia> lzdt = LaxZonedDateTime(DateTime(2016, 3, 13), TimeZone("America/Winnipeg"))
2016-03-13T00:00:00-06:00
julia> r = lzdt:Dates.Hour(1):(lzdt + Dates.Hour(5))
2016-03-16T02:00:00-05:002016-03-11T02:00:00-06:00
julia> collect(r)
6-element Array{LaxZonedDateTimes.LaxZonedDateTime,1}:
2016-03-13T00:00:00-06:00
2016-03-13T01:00:00-06:00
2016-03-13T03:00:00-05:00
2016-03-13T04:00:00-05:00
2016-03-13T05:00:00-05:00
2016-03-13T06:00:00-05:00Ranges should generally work as expected, but here are the rules for some of the edge cases that you might encounter:
- If start and/or finish is unrepresentable, the range collects to nothing
- If start is AMB/DNE, and step is a
DatePeriod, it works (first element will be DNE/AMB) - If start is AMB/DNE, and step is a
TimePeriod, the range collects to nothing - If finish is AMB/DNE, and step is a
DatePeriod, it works (last element may be DNE/AMB) - If finish is AMB/DNE, and step is a
TimePeriod, it works (last element omitted for DNE, both versions included for AMB)
(The last two descriptions above assume that step divides evenly into the range. If it doesn't, then the last element won't actually hit the AMB/DNE value.)
When transitions occur between the start and end of a range, they are skipped over as per
the TimeZones.jl implementation (e.g., when stepping one hour at a time, a "spring
forward" will result in the range collecting to 0:00, 1:00, 3:00, ...). A DNE
LaxZonedDateTime will only appear in cases where TimeZones.jl would throw an error
(e.g., stepping through the "spring forward" transition one day at a time, and landing on
the missing hour).