Arithmetic
ZonedDateTime-Period Arithmetic
ZonedDateTime
uses calendrical arithmetic in a similar manner to DateTime
but with some key differences. Lets look at these differences by adding a day to March 30th 2014 in Europe/Warsaw.
julia> using TimeZones, Dates
julia> warsaw = tz"Europe/Warsaw"
Europe/Warsaw (UTC+1/UTC+2)
julia> spring = ZonedDateTime(2014, 3, 30, warsaw)
2014-03-30T00:00:00+01:00
julia> spring + Day(1)
2014-03-31T00:00:00+02:00
Adding a day to the ZonedDateTime
changed the date from the 30th to the 31st as expected. Looking closely however you'll notice that the time zone offset changed from +01:00 to +02:00. The reason for this change is because the time zone "Europe/Warsaw" switched from standard time (+01:00) to daylight saving time (+02:00) on the 30th. The change in the offset caused the local DateTime 2014-03-31T02:00:00 to be skipped effectively making the 30th a day which only contained 23 hours. Alternatively if we added hours we can see the difference:
julia> spring + Hour(24)
2014-03-31T01:00:00+02:00
julia> spring + Hour(23)
2014-03-31T00:00:00+02:00
A potential cause of confusion regarding this behaviour is the loss in associativity when ordering is forced. For example:
julia> (spring + Day(1)) + Hour(24)
2014-04-01T00:00:00+02:00
julia> (spring + Hour(24)) + Day(1)
2014-04-01T01:00:00+02:00
The first example adds 1 day to 2014-03-30T00:00:00+01:00, which results in 2014-03-31T00:00:00+02:00; then we add 24 hours to get 2014-04-01T00:00:00+02:00. The second example add 24 hours first to get 2014-03-31T01:00:00+02:00, and then add 1 day which results in 2014-04-01T01:00:00+02:00. When working with operations using multiple periods the operations will be ordered by the Period's types and not their positional order; this means Day
will be added before Hour
. Hence the following does result in associativity:
julia> spring + Hour(24) + Day(1)
2014-04-01T00:00:00+02:00
julia> spring + Day(1) + Hour(24)
2014-04-01T00:00:00+02:00
Ranges
Query and adjuster functions can be used as with Date
and DateTime
. We can use filter
to apply a predicate to a StepRange
of TimeType
s to produce a vector of dates that fit certain inclusion criteria (for example, "every fifth Wednesday of the month in 2014 at 09:00"):
julia> warsaw = tz"Europe/Warsaw"
Europe/Warsaw (UTC+1/UTC+2)
julia> start = ZonedDateTime(2014, warsaw)
2014-01-01T00:00:00+01:00
julia> stop = ZonedDateTime(2015, warsaw)
2015-01-01T00:00:00+01:00
julia> filter(start:Dates.Hour(1):stop) do d
Dates.dayofweek(d) == Dates.Wednesday &&
Dates.hour(d) == 9 &&
Dates.dayofweekofmonth(d) == 5
end
5-element Array{ZonedDateTime,1}:
2014-01-29T09:00:00+01:00
2014-04-30T09:00:00+02:00
2014-07-30T09:00:00+02:00
2014-10-29T09:00:00+01:00
2014-12-31T09:00:00+01:00
Note the transition from standard time to daylight saving time (and back again).
It is possible to define a range start:step:stop
such that start
and stop
have different time zones. In this case the resulting ZonedDateTime
s will all share a time zone with start
but the range will stop at the instant that corresponds to stop
in start
's time zone. For example:
julia> start = ZonedDateTime(2016, 1, 1, 12, tz"UTC")
2016-01-01T12:00:00+00:00
julia> stop = ZonedDateTime(2016, 1, 1, 18, tz"Europe/Warsaw")
2016-01-01T18:00:00+01:00
julia> collect(start:Dates.Hour(1):stop)
6-element Array{ZonedDateTime,1}:
2016-01-01T12:00:00+00:00
2016-01-01T13:00:00+00:00
2016-01-01T14:00:00+00:00
2016-01-01T15:00:00+00:00
2016-01-01T16:00:00+00:00
2016-01-01T17:00:00+00:00
Note that 2016-01-01T17:00:00 in UTC corresponds to 2016-01-01T18:00:00 in "Europe/Warsaw", which is the requested endpoint of the range.