diff --git a/README.md b/README.md index c97b5e068..d36afa251 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,17 @@ TimeZones.jl [![Stable Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](http://timezonesjl.readthedocs.io/en/stable/) [![Latest Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](http://timezonesjl.readthedocs.io/en/latest/) -[IANA time zone database](http://www.iana.org/time-zones) access for the [Julia](http://julialang.org/) programming language. TimeZones.jl extends the Date/DateTime support for Julia to include a new time zone aware TimeType: ZonedDateTime. +[IANA time zone database](http://www.iana.org/time-zones) access for the [Julia](http://julialang.org/) programming language. TimeZones.jl extends the Date/DateTime support for Julia to include a new time zone aware TimeType: Localized. ## Features -* A new time zone aware TimeType: ZonedDateTime +* A new time zone aware TimeType: Localized * Support for all time zones in the IANA time zone database (also known as the tz/zoneinfo/Olson database) -* ZonedDateTime-Period arithmetic [similar to that of DateTime](https://docs.julialang.org/en/stable/manual/dates/#TimeType-Period-Arithmetic-1) +* Localized-Period arithmetic [similar to that of DateTime](https://docs.julialang.org/en/stable/manual/dates/#TimeType-Period-Arithmetic-1) * Local system time zone information as a TimeZone * Current system time in any TimeZone * Support for reading the [tzfile](http://man7.org/linux/man-pages/man5/tzfile.5.html) format -* String parsing of ZonedDateTime using [DateFormat](https://docs.julialang.org/en/stable/stdlib/dates/#Base.Dates.DateFormat) +* String parsing of Localized using [DateFormat](https://docs.julialang.org/en/stable/stdlib/dates/#Base.Dates.DateFormat) ## Installation diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 76d5166de..15597cda3 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -9,12 +9,12 @@ import TimeZones.TZData: parse_components # https://github.com/JuliaTime/TimeZones.jl/issues/25 function parse_dates(n) df = DateFormat("yyyymmddHH:MM:SS ZZZ") - arr = Array{ZonedDateTime}(n) + arr = Array{Localized}(n) for (i, s) in enumerate(Iterators.repeated("2016060701:02:03 America/Toronto", n)) - arr[i] = ZonedDateTime(s,df) + arr[i] = Localized(s,df) end return arr end - @bench "ZonedDateTime" parse_dates(1000) + @bench "Localized" parse_dates(1000) end diff --git a/docs/arithmetic.md b/docs/arithmetic.md index d915c7909..25c39281d 100644 --- a/docs/arithmetic.md +++ b/docs/arithmetic.md @@ -1,6 +1,6 @@ -## ZonedDateTime-Period Arithmetic +## Localized-Period Arithmetic -`ZonedDateTime` uses calendrical arithmetic in a [similar manner to `DateTime`](https://docs.julialang.org/en/stable/manual/dates/#TimeType-Period-Arithmetic-1) but with some key differences. Lets look at these differences by adding a day to March 30th 2014 in Europe/Warsaw. +`Localized` uses calendrical arithmetic in a [similar manner to `DateTime`](https://docs.julialang.org/en/stable/manual/dates/#TimeType-Period-Arithmetic-1) but with some key differences. Lets look at these differences by adding a day to March 30th 2014 in Europe/Warsaw. ```julia julia> using Base.Dates @@ -8,14 +8,14 @@ julia> using Base.Dates julia> warsaw = tz"Europe/Warsaw" Europe/Warsaw (UTC+1/UTC+2) -julia> spring = ZonedDateTime(2014, 3, 30, warsaw) +julia> spring = Localized(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: +Adding a day to the `Localized` 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 julia> spring + Hour(24) @@ -48,17 +48,17 @@ julia> spring + Day(1) + Hour(24) If using a version of Julia 0.5 or below you may want to force precedence when mixing `DatePeriod`s and `TimePeriod`s since the expression `Day(1) + Hour(24)` would be automatically canonicalized to `Day(2)`: ```julia -julia> ZonedDateTime(2014, 10, 25, warsaw) + Day(1) + Hour(24) # On Julia 0.5 or below +julia> Localized(2014, 10, 25, warsaw) + Day(1) + Hour(24) # On Julia 0.5 or below 2014-10-27T00:00:00+01:00 -julia> ZonedDateTime(2014, 10, 25, warsaw) + Day(2) +julia> Localized(2014, 10, 25, warsaw) + Day(2) 2014-10-27T00:00:00+01:00 ``` In Julia 0.6 period canonicalization no longer happens automatically: ``` -julia> ZonedDateTime(2014, 10, 25, warsaw) + Day(1) + Hour(24) # Julia 0.6 and above +julia> Localized(2014, 10, 25, warsaw) + Day(1) + Hour(24) # Julia 0.6 and above 2014-10-26T23:00:00+01:00 ``` @@ -70,10 +70,10 @@ Julia allows for the use of powerful [adjuster functions](https://docs.julialang julia> warsaw = tz"Europe/Warsaw" Europe/Warsaw (UTC+1/UTC+2) -julia> start = ZonedDateTime(2014, warsaw) +julia> start = Localized(2014, warsaw) 2014-01-01T00:00:00+01:00 -julia> stop = ZonedDateTime(2015, warsaw) +julia> stop = Localized(2015, warsaw) 2015-01-01T00:00:00+01:00 julia> Dates.recur(start:Dates.Hour(1):stop) do d @@ -81,7 +81,7 @@ julia> Dates.recur(start:Dates.Hour(1):stop) do d Dates.hour(d) == 9 && Dates.dayofweekofmonth(d) == 5 end -5-element Array{TimeZones.ZonedDateTime,1}: +5-element Array{TimeZones.Localized,1}: 2014-01-29T09:00:00+01:00 2014-04-30T09:00:00+02:00 2014-07-30T09:00:00+02:00 @@ -91,17 +91,17 @@ julia> Dates.recur(start:Dates.Hour(1):stop) do d 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: +It is possible to define a range `start:step:stop` such that `start` and `stop` have different time zones. In this case the resulting `Localized`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 -julia> start = ZonedDateTime(2016, 1, 1, 12, tz"UTC") +julia> start = Localized(2016, 1, 1, 12, tz"UTC") 2016-01-01T12:00:00+00:00 -julia> stop = ZonedDateTime(2016, 1, 1, 18, tz"Europe/Warsaw") +julia> stop = Localized(2016, 1, 1, 18, tz"Europe/Warsaw") 2016-01-01T18:00:00+01:00 julia> collect(start:Dates.Hour(1):stop) -6-element Array{TimeZones.ZonedDateTime,1}: +6-element Array{TimeZones.Localized,1}: 2016-01-01T12:00:00+00:00 2016-01-01T13:00:00+00:00 2016-01-01T14:00:00+00:00 diff --git a/docs/conversions.md b/docs/conversions.md index a3bbc2fb0..6153dfffa 100644 --- a/docs/conversions.md +++ b/docs/conversions.md @@ -1,18 +1,18 @@ ## Switching Time Zones -Switching an existing `ZonedDateTime` from one `TimeZone` to another can be done with the function `astimezone`: +Switching an existing `Localized` from one `TimeZone` to another can be done with the function `astimezone`: ```julia -julia> zdt = ZonedDateTime(2014, 1, 1, tz"UTC") +julia> ldt = Localized(2014, 1, 1, tz"UTC") 2014-01-01T00:00:00+00:00 -julia> astimezone(zdt, tz"Asia/Tokyo") +julia> astimezone(ldt, tz"Asia/Tokyo") 2014-01-01T09:00:00+09:00 ``` ## Parsing strings -`ZonedDateTime` parsing extends the functionality provided by `Dates`. If you haven't already it is recommended that you first read the official Julia manual on [Date and DateTime](https://docs.julialang.org/en/stable/manual/dates/#Constructors-1). The `TimeZones` package adds `z` and `Z` to the list of available [parsing character codes](https://docs.julialang.org/en/stable/stdlib/dates/#Base.Dates.DateFormat): +`Localized` parsing extends the functionality provided by `Dates`. If you haven't already it is recommended that you first read the official Julia manual on [Date and DateTime](https://docs.julialang.org/en/stable/manual/dates/#Constructors-1). The `TimeZones` package adds `z` and `Z` to the list of available [parsing character codes](https://docs.julialang.org/en/stable/stdlib/dates/#Base.Dates.DateFormat): | Code | Matches | Comment | |:-----|:---------------------|:-------------------------------------------------| @@ -21,31 +21,31 @@ julia> astimezone(zdt, tz"Asia/Tokyo") Note that with the exception of "UTC" and "GMT" time zone abbrevations cannot be parsed using the `Z` character code since most abbreviations are ambiguous. For example abbreviation "MST" could be interpreted as "Mountain Standard Time" (UTC-7) or "Moscow Summer Time" (UTC+3:31). -Parsing a `ZonedDateTime` just requires the text to parse and a format string: +Parsing a `Localized` just requires the text to parse and a format string: ```julia -julia> ZonedDateTime("20150101-0700", "yyyymmddzzzz") +julia> Localized("20150101-0700", "yyyymmddzzzz") 2015-01-01T00:00:00-07:00 -julia> ZonedDateTime("2015-08-06T22:25:31+07:00", "yyyy-mm-ddTHH:MM:SSzzzz") +julia> Localized("2015-08-06T22:25:31+07:00", "yyyy-mm-ddTHH:MM:SSzzzz") 2015-08-06T22:25:31+07:00 ``` -When parsing several `ZonedDateTime` strings which use the same format you will see better performance if you first create a `Dates.DateFormat` instead of passing in a raw format string. +When parsing several `Localized` strings which use the same format you will see better performance if you first create a `Dates.DateFormat` instead of passing in a raw format string. ```julia julia> df = Dates.DateFormat("yy-mm-ddz"); -julia> ZonedDateTime("2015-03-29+01:00", df) +julia> Localized("2015-03-29+01:00", df) 2015-03-29T00:00:00+01:00 -julia> ZonedDateTime("2015-03-30+02:00", df) +julia> Localized("2015-03-30+02:00", df) 2015-03-30T00:00:00+02:00 ``` ## Formatting strings -Formatting a `ZonedDateTime` as a string also extends the functionality provided by `Base.Dates`. The `TimeZones` package adds the new formatting character codes `z` and `Z` to the list of available [formatting character codes](https://docs.julialang.org/en/stable/stdlib/dates/#Base.Dates.format-Tuple{Base.Dates.TimeType,AbstractString}): +Formatting a `Localized` as a string also extends the functionality provided by `Base.Dates`. The `TimeZones` package adds the new formatting character codes `z` and `Z` to the list of available [formatting character codes](https://docs.julialang.org/en/stable/stdlib/dates/#Base.Dates.format-Tuple{Base.Dates.TimeType,AbstractString}): | Code | Examples | Comment | |:-----|:---------------------|:-------------------------------------------------| @@ -54,15 +54,15 @@ Formatting a `ZonedDateTime` as a string also extends the functionality provided It is recommended that you prefer the use of the `z` character code over `Z` time zone abbreviations can be interpreted in different ways. -Formatting uses the `Dates.format` function with a `ZonedDateTime` and a format string: +Formatting uses the `Dates.format` function with a `Localized` and a format string: ```julia -julia> zdt = ZonedDateTime(2015,8,6,22,25,tz"Europe/Warsaw") +julia> ldt = Localized(2015,8,6,22,25,tz"Europe/Warsaw") 2015-08-06T22:25:00+02:00 -julia> Dates.format(zdt, "yyyymmddzzzz") +julia> Dates.format(ldt, "yyyymmddzzzz") "20150806+02:00" -julia> Dates.format(zdt, "yyyy-mm-dd HH:MM ZZZ") +julia> Dates.format(ldt, "yyyy-mm-dd HH:MM ZZZ") "2015-08-06 22:25 CEST" ``` diff --git a/docs/current.md b/docs/current.md index 7cf320419..b84899e0b 100644 --- a/docs/current.md +++ b/docs/current.md @@ -1,6 +1,6 @@ ## Current Time / System Time Zone -Julia provides the `now()` method to retrieve your current system's time as a `DateTime`. The TimeZones.jl package provides an additional `now(::TimeZone)` method providing the current time as a `ZonedDateTime`: +Julia provides the `now()` method to retrieve your current system's time as a `DateTime`. The TimeZones.jl package provides an additional `now(::TimeZone)` method providing the current time as a `Localized`: ```julia now(tz"Europe/Warsaw") @@ -27,22 +27,22 @@ julia> today(tz"Pacific/Midway"), today(tz"Pacific/Apia") (2018-01-29, 2018-01-30) ``` -You should be careful not to use `today()` when working with `ZonedDateTime`s as you may end up using the wrong day. For example: +You should be careful not to use `today()` when working with `Localized`s as you may end up using the wrong day. For example: ```julia julia> midway, apia = tz"Pacific/Midway", tz"Pacific/Apia" (Pacific/Midway (UTC-11), Pacific/Apia (UTC+13/UTC+14)) -julia> ZonedDateTime(today() + Time(11), midway) +julia> Localized(today() + Time(11), midway) 2018-01-29T11:00:00-11:00 -julia> ZonedDateTime(today() + Time(11), apia) # Should be 2018-01-30 +julia> Localized(today() + Time(11), apia) # Should be 2018-01-30 2018-01-29T11:00:00+14:00 -julia> ZonedDateTime(today(midway) + Time(11), midway) +julia> Localized(today(midway) + Time(11), midway) 2018-01-29T11:00:00-11:00 -julia> ZonedDateTime(today(apia) + Time(11), apia) +julia> Localized(today(apia) + Time(11), apia) 2018-01-30T11:00:00+14:00 ``` diff --git a/docs/faq.md b/docs/faq.md index bb5992a3c..1688a4476 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -13,7 +13,7 @@ extract(active_archive(), TimeZones.TZ_SOURCE_DIR, "etcetera") compile() ``` -## Far-future ZonedDateTime with VariableTimeZone +## Far-future Localized with VariableTimeZone Due to the internal representation of a `VariableTimeZone` it is infeasible to determine a time zones transitions to infinity. Since [2038-01-19T03:14:07](https://en.wikipedia.org/wiki/Year_2038_problem) is the last `DateTime` that can be represented by an `Int32` (`Dates.unix2datetime(typemax(Int32))`) it was decided that 2037 would be the last year in which all transition dates are computed. If additional transitions are known to exist after the last transition then a cutoff date is specified. @@ -27,18 +27,18 @@ julia> last(warsaw.transitions) julia> warsaw.cutoff # DateTime up until the last transition is effective Nullable{DateTime}(2038-03-28T01:00:00) -julia> ZonedDateTime(DateTime(2039), warsaw) +julia> Localized(DateTime(2039), warsaw) ERROR: TimeZone Europe/Warsaw does not handle dates on or after 2038-03-28T01:00:00 UTC ``` -It is important to note that since we are taking about future time zone transitions and the rules dictating these transitions are subject to change and may not be accurate. If you still want to work with future `ZonedDateTime` past the default cutoff you can re-compile the `TimeZone` objects and specify the `max_year` keyword: +It is important to note that since we are taking about future time zone transitions and the rules dictating these transitions are subject to change and may not be accurate. If you still want to work with future `Localized` past the default cutoff you can re-compile the `TimeZone` objects and specify the `max_year` keyword: ```julia julia> using TimeZones julia> TimeZones.TZData.compile(max_year=2200) -julia> ZonedDateTime(DateTime(2100), tz"Europe/Warsaw") +julia> Localized(DateTime(2100), tz"Europe/Warsaw") 2100-01-01T00:00:00+01:00 ``` @@ -47,13 +47,13 @@ Warning: since the `tz` string macro loads the `TimeZone` at compile time the ti ```julia julia> begin TimeZones.TZData.compile(max_year=2210) - ZonedDateTime(DateTime(2205), tz"Europe/Warsaw") + Localized(DateTime(2205), tz"Europe/Warsaw") end ERROR: UnhandledTimeError: TimeZone Europe/Warsaw does not handle dates on or after 2038-03-28T01:00:00 UTC julia> begin TimeZones.TZData.compile(max_year=2220) - ZonedDateTime(DateTime(2215), TimeZone("Europe/Warsaw")) + Localized(DateTime(2215), TimeZone("Europe/Warsaw")) end 2215-01-01T00:00:00+01:00 ``` \ No newline at end of file diff --git a/docs/rounding.md b/docs/rounding.md index 9f0e5f6a9..3a00739a8 100644 --- a/docs/rounding.md +++ b/docs/rounding.md @@ -1,11 +1,11 @@ -## Rounding a ZonedDateTime +## Rounding a Localized -Rounding operations (`floor`, `ceil`, and `round`) on `ZonedDateTime`s are performed in a +Rounding operations (`floor`, `ceil`, and `round`) on `Localized`s are performed in a [similar manner to `DateTime`](https://docs.julialang.org/en/stable/manual/dates/#Rounding-1) and should generally behave as expected. When `VariableTimeZone` transitions are involved, however, unexpected behaviour may be encountered. -Instead of performing rounding operations on a UTC representation of the `ZonedDateTime`, +Instead of performing rounding operations on a UTC representation of the `Localized`, which would in some cases be computationally less expensive, rounding is done in the local time zone. This ensures that rounding behaves as expected and is maximally meaningful. @@ -18,8 +18,8 @@ wouldn't be on the hour in the local time zone. When the target resolution is a `TimePeriod` the likelihood of encountering an ambiguous or non-existent time (due to daylight saving time transitions) is increased. To resolve this -issue, rounding a `ZonedDateTime` with a `VariableTimeZone` to a `TimePeriod` uses the -`DateTime` value in the appropriate `FixedTimeZone`, then reconverts it to a `ZonedDateTime` +issue, rounding a `Localized` with a `VariableTimeZone` to a `TimePeriod` uses the +`DateTime` value in the appropriate `FixedTimeZone`, then reconverts it to a `Localized` in the appropriate `VariableTimeZone` afterward. (See [Examples](#examples) below.) ### Rounding to a DatePeriod @@ -27,7 +27,7 @@ in the appropriate `VariableTimeZone` afterward. (See [Examples](#examples) belo When the target resolution is a `DatePeriod` rounding is done in the local time zone in a straightforward fashion. -Rounding is not an entirely "safe" operation for `ZonedDateTime`s, as in some cases +Rounding is not an entirely "safe" operation for `Localized`s, as in some cases historical transitions for some time zones (`Asia/Colombo`, for example) occur at midnight. In such cases rounding to a `DatePeriod` may still result in an `AmbiguousTimeError` or a `NonExistentTimeError`s. (But such occurrences should be relatively rare.) @@ -40,25 +40,25 @@ The `America/Winnipeg` time zone transitioned from Central Standard Time (UTC-6: Central Daylight Time (UTC-5:00) on 2016-03-13, moving directly from 01:59:59 to 03:00:00. ```julia -julia> zdt = ZonedDateTime(2016, 3, 13, 1, 45, tz"America/Winnipeg") +julia> ldt = Localized(2016, 3, 13, 1, 45, tz"America/Winnipeg") 2016-03-13T01:45:00-06:00 -julia> floor(zdt, Dates.Day) +julia> floor(ldt, Dates.Day) 2016-03-13T00:00:00-06:00 -julia> ceil(zdt, Dates.Day) +julia> ceil(ldt, Dates.Day) 2016-03-14T00:00:00-05:00 -julia> round(zdt, Dates.Day) +julia> round(ldt, Dates.Day) 2016-03-13T00:00:00-06:00 -julia> floor(zdt, Dates.Hour) +julia> floor(ldt, Dates.Hour) 2016-03-13T01:00:00-06:00 -julia> ceil(zdt, Dates.Hour) +julia> ceil(ldt, Dates.Hour) 2016-03-13T03:00:00-05:00 -julia> round(zdt, Dates.Hour) +julia> round(ldt, Dates.Hour) 2016-03-13T03:00:00-05:00 ``` @@ -66,12 +66,12 @@ The `Asia/Colombo` time zone revised the definition of Lanka Time from UTC+6:30 on 1996-10-26, moving from 00:29:59 back to 00:00:00. ```julia -julia> zdt = ZonedDateTime(1996, 10, 25, 23, 45, tz"Asia/Colombo") +julia> ldt = Localized(1996, 10, 25, 23, 45, tz"Asia/Colombo") 1996-10-25T23:45:00+06:30 -julia> round(zdt, Dates.Hour) +julia> round(ldt, Dates.Hour) 1996-10-26T00:00:00+06:30 -julia> round(zdt, Dates.Day) +julia> round(ldt, Dates.Day) ERROR: Local DateTime 1996-10-26T00:00:00 is ambiguous ``` diff --git a/docs/types.md b/docs/types.md index 5dada781d..3db43e4f4 100644 --- a/docs/types.md +++ b/docs/types.md @@ -20,17 +20,17 @@ To see all of the [currently available](faq#why-are-the-etc-time-zones-unsupport timezone_names() ``` -## ZonedDateTime +## Localized -A `ZonedDateTime` is a *time zone aware* version of a `DateTime` (in Python parlance). Note that all `ZonedDateTime` instances will always be in the correct zone without requiring manual normalization (unlike Python's [pytz](http://pytz.sourceforge.net/) module). +A `Localized` is a *time zone aware* version of a `DateTime` (in Python parlance). Note that all `Localized` instances will always be in the correct zone without requiring manual normalization (unlike Python's [pytz](http://pytz.sourceforge.net/) module). -To construct a `ZonedDateTime` instance you just need a `DateTime` and a `TimeZone`: +To construct a `Localized` instance you just need a `DateTime` and a `TimeZone`: ```julia -julia> ZonedDateTime(DateTime(2014,1,1), tz"Europe/Warsaw") +julia> Localized(DateTime(2014,1,1), tz"Europe/Warsaw") 2014-01-01T00:00:00+01:00 -julia> ZonedDateTime(2014, 1, 1, tz"Europe/Warsaw") +julia> Localized(2014, 1, 1, tz"Europe/Warsaw") 2014-01-01T00:00:00+01:00 ``` @@ -45,23 +45,23 @@ Europe/Warsaw (UTC+1/UTC+2) julia> typeof(warsaw) TimeZones.VariableTimeZone -julia> ZonedDateTime(DateTime(2014,1,1), warsaw) +julia> Localized(DateTime(2014,1,1), warsaw) 2014-01-01T00:00:00+01:00 -julia> ZonedDateTime(DateTime(2014,6,1), warsaw) +julia> Localized(DateTime(2014,6,1), warsaw) 2014-06-01T00:00:00+02:00 ``` From the above example you can see that the offset for this time zone differed based upon the `DateTime` provided. An unfortunate side effect of having the offset change over time results in some difficulties in working with dates near the transitions. For example when working with a `DateTime` that occurs during the "spring forward" transition will result in a `NonExistentTimeError`: ```julia -julia> ZonedDateTime(DateTime(2014,3,30,1), warsaw) +julia> Localized(DateTime(2014,3,30,1), warsaw) 2014-03-30T01:00:00+01:00 -julia> ZonedDateTime(DateTime(2014,3,30,2), warsaw) +julia> Localized(DateTime(2014,3,30,2), warsaw) ERROR: DateTime 2014-03-30T02:00:00 does not exist within Europe/Warsaw -julia> ZonedDateTime(DateTime(2014,3,30,3), warsaw) +julia> Localized(DateTime(2014,3,30,3), warsaw) 2014-03-30T03:00:00+02:00 ``` @@ -71,33 +71,33 @@ Alternatively, working with a `DateTime` that occurs during the "fall back" tran julia> dt = DateTime(2014,10,26,2) 2014-10-26T02:00:00 -julia> ZonedDateTime(dt, warsaw) +julia> Localized(dt, warsaw) ERROR: Local DateTime 2014-10-26T02:00:00 is ambiguous -julia> ZonedDateTime(dt, warsaw, 1) # first occurrence of the duplicate hour +julia> Localized(dt, warsaw, 1) # first occurrence of the duplicate hour 2014-10-26T02:00:00+02:00 -julia> ZonedDateTime(dt, warsaw, 2) # second occurrence of the duplicate hour +julia> Localized(dt, warsaw, 2) # second occurrence of the duplicate hour 2014-10-26T02:00:00+01:00 -julia> ZonedDateTime(dt, warsaw, true) # use the hour which is in daylight saving time +julia> Localized(dt, warsaw, true) # use the hour which is in daylight saving time 2014-10-26T02:00:00+02:00 -julia> ZonedDateTime(dt, warsaw, false) # use the hour which is not in daylight saving time +julia> Localized(dt, warsaw, false) # use the hour which is not in daylight saving time 2014-10-26T02:00:00+01:00 ``` When working with dates prior to the year 1900 you may notice that the time zone offset includes minutes or even seconds. These kind of offsets are normal: ```julia -julia> ZonedDateTime(1879, 1, 1, warsaw) +julia> Localized(1879, 1, 1, warsaw) 1879-01-01T00:00:00+01:24 ``` Alternatively, when using future dates past the year 2038 will result in an error: ```julia -julia> ZonedDateTime(2039, warsaw) +julia> Localized(2039, warsaw) ERROR: TimeZone Europe/Warsaw does not handle dates on or after 2038-03-28T01:00:00 UTC ``` @@ -123,9 +123,9 @@ FixedTimeZone("+12:34:56") FixedTimeZone("FOO", -6 * 3600) # 6 hours in seconds ``` -Constructing a `ZonedDateTime` works similarly to `VariableTimeZone`: +Constructing a `Localized` works similarly to `VariableTimeZone`: ```julia -julia> ZonedDateTime(1960, 1, 1, tz"UTC") +julia> Localized(1960, 1, 1, tz"UTC") 1960-01-01T00:00:00+00:00 ``` diff --git a/src/TimeZones.jl b/src/TimeZones.jl index e4f31e860..70dd174b2 100644 --- a/src/TimeZones.jl +++ b/src/TimeZones.jl @@ -8,7 +8,7 @@ using Compat.Dates, Compat.Printf, Compat.Serialization, Compat.Unicode import Compat.Dates: TimeZone, AbstractTime using Nullables -export TimeZone, @tz_str, istimezone, FixedTimeZone, VariableTimeZone, ZonedDateTime, +export TimeZone, @tz_str, istimezone, FixedTimeZone, VariableTimeZone, Localized, DateTime, TimeError, AmbiguousTimeError, NonExistentTimeError, UnhandledTimeError, # discovery.jl timezone_names, all_timezones, timezones_from_abbr, timezone_abbrs, @@ -43,7 +43,7 @@ function __init__() Dates.CONVERSION_SPECIFIERS['z'] = TimeZone Dates.CONVERSION_SPECIFIERS['Z'] = TimeZone Dates.CONVERSION_DEFAULTS[TimeZone] = "" - Dates.CONVERSION_TRANSLATIONS[ZonedDateTime] = ( + Dates.CONVERSION_TRANSLATIONS[Localized] = ( Year, Month, Day, Hour, Minute, Second, Millisecond, TimeZone, ) diff --git a/src/accessors.jl b/src/accessors.jl index b6001a7ca..ad8ba04cb 100644 --- a/src/accessors.jl +++ b/src/accessors.jl @@ -2,34 +2,64 @@ import Compat.Dates: Hour, Minute, Second, Millisecond, days, hour, minute, second, millisecond """ - localtime(::ZonedDateTime) -> DateTime + localtime(::Localized) -> DateTime -Creates a local or civil `DateTime` from the given `ZonedDateTime`. For example the +Creates a local or civil `DateTime` from the given `Localized`. For example the `2014-05-30T08:11:24-04:00` would return `2014-05-30T08:11:24`. """ -localtime(zdt::ZonedDateTime) = zdt.utc_datetime + zdt.zone.offset +localtime(ldt::Localized) = ldt.utc_datetime + ldt.zone.offset """ - utc(::ZonedDateTime) -> DateTime + utc(::Localized) -> DateTime -Creates a utc `DateTime` from the given `ZonedDateTime`. For example the +Creates a utc `DateTime` from the given `Localized`. For example the `2014-05-30T08:11:24-04:00` would return `2014-05-30T12:11:24`. """ -utc(zdt::ZonedDateTime) = zdt.utc_datetime +utc(ldt::Localized) = ldt.utc_datetime """ - timezone(::ZonedDateTime) -> TimeZone + timezone(::Localized) -> TimeZone -Returns the `TimeZone` used by the `ZonedDateTime`. +Returns the `TimeZone` used by the `Localized`. """ -timezone(zdt::ZonedDateTime) = zdt.timezone +timezone(ldt::Localized) = ldt.timezone -days(zdt::ZonedDateTime) = days(localtime(zdt)) +""" + isstrict(ldt::Localized) -> Bool + +Returns whether the localized datetime is strict +(e.g., cannot represent non-existent or ambiguous hours). +""" +isstrict(ldt::Localized{T, true}) where T = true +isstrict(ldt::Localized{T, false}) where T = false + +""" + isvalid(ldt::Localized) -> Bool + +Returns wether the localized datetime is valid (e.g., exists and is not amibiguous) +""" +Base.isvalid(ldt::Localized) = !isa(ldt.zone, InvalidTimeZone) + +""" + isambiguous(ldt::Localized) -> Bool + +Returns whether the localized datetime is ambiguous. +""" +isambiguous(ldt::Localized) = isa(ldt.zone, Ambiguous) + +""" + isnonexistent(ldt::Localized) -> Bool + +Returns whether the localized datetime is non-existent. +""" +isnonexistent(ldt::Localized) = isa(ldt.zone, NonExistent) + +days(ldt::Localized) = days(localtime(ldt)) for period in (:Hour, :Minute, :Second, :Millisecond) accessor = Symbol(lowercase(string(period))) @eval begin - $accessor(zdt::ZonedDateTime) = $accessor(localtime(zdt)) - $period(zdt::ZonedDateTime) = $period($accessor(zdt)) + $accessor(ldt::Localized) = $accessor(localtime(ldt)) + $period(ldt::Localized) = $period($accessor(ldt)) end end diff --git a/src/adjusters.jl b/src/adjusters.jl index 9d5a96c35..6dac98015 100644 --- a/src/adjusters.jl +++ b/src/adjusters.jl @@ -5,20 +5,20 @@ import Compat.Dates: firstdayofweek, lastdayofweek, firstdayofmonth, lastdayofmo # Truncation # TODO: Just utilize floor code for truncation? -function trunc(zdt::ZonedDateTime, ::Type{P}) where P<:DatePeriod - ZonedDateTime(trunc(localtime(zdt), P), timezone(zdt)) +function trunc(ldt::Localized, ::Type{P}) where P<:DatePeriod + Localized(trunc(localtime(ldt), P), timezone(ldt)) end -function trunc(zdt::ZonedDateTime, ::Type{P}) where P<:TimePeriod - local_dt = trunc(localtime(zdt), P) - utc_dt = local_dt - zdt.zone.offset - ZonedDateTime(utc_dt, timezone(zdt); from_utc=true) +function trunc(ldt::Localized, ::Type{P}) where P<:TimePeriod + local_dt = trunc(localtime(ldt), P) + utc_dt = local_dt - ldt.zone.offset + Localized(utc_dt, timezone(ldt); from_utc=true) end -trunc(zdt::ZonedDateTime, ::Type{Millisecond}) = zdt +trunc(ldt::Localized, ::Type{Millisecond}) = ldt # Adjusters for prefix in ("firstdayof", "lastdayof"), suffix in ("week", "month", "year", "quarter") func = Symbol(prefix * suffix) @eval begin - $func(dt::ZonedDateTime) = ZonedDateTime($func(localtime(dt)), dt.timezone) + $func(dt::Localized) = Localized($func(localtime(dt)), dt.timezone) end end diff --git a/src/arithmetic.jl b/src/arithmetic.jl index c8205309f..f0d918c61 100644 --- a/src/arithmetic.jl +++ b/src/arithmetic.jl @@ -8,24 +8,24 @@ else import Base.Broadcast: broadcasted end -# ZonedDateTime arithmetic -(+)(x::ZonedDateTime) = x -(-)(x::ZonedDateTime, y::ZonedDateTime) = x.utc_datetime - y.utc_datetime +# Localized arithmetic +(+)(x::Localized) = x +(-)(x::Localized, y::Localized) = x.utc_datetime - y.utc_datetime -function (+)(zdt::ZonedDateTime, p::DatePeriod) - return ZonedDateTime(localtime(zdt) + p, timezone(zdt)) +function (+)(ldt::Localized, p::DatePeriod) + return Localized(localtime(ldt) + p, timezone(ldt)) end -function (+)(zdt::ZonedDateTime, p::TimePeriod) - return ZonedDateTime(zdt.utc_datetime + p, timezone(zdt); from_utc=true) +function (+)(ldt::Localized, p::TimePeriod) + return Localized(ldt.utc_datetime + p, timezone(ldt); from_utc=true) end -function (-)(zdt::ZonedDateTime, p::DatePeriod) - return ZonedDateTime(localtime(zdt) - p, timezone(zdt)) +function (-)(ldt::Localized, p::DatePeriod) + return Localized(localtime(ldt) - p, timezone(ldt)) end -function (-)(zdt::ZonedDateTime, p::TimePeriod) - return ZonedDateTime(zdt.utc_datetime - p, timezone(zdt); from_utc=true) +function (-)(ldt::Localized, p::TimePeriod) + return Localized(ldt.utc_datetime - p, timezone(ldt); from_utc=true) end -function broadcasted(::typeof(+), r::StepRange{ZonedDateTime}, p::DatePeriod) +function broadcasted(::typeof(+), r::StepRange{<:Localized}, p::DatePeriod) start, step, stop = first(r), Base.step(r), last(r) # Since the localtime + period can result in an invalid local datetime when working with @@ -49,4 +49,4 @@ function broadcasted(::typeof(+), r::StepRange{ZonedDateTime}, p::DatePeriod) return StepRange(start, step, stop) end -broadcasted(::typeof(-), r::StepRange{ZonedDateTime}, p::DatePeriod) = broadcast(+, r, -p) +broadcasted(::typeof(-), r::StepRange{Localized}, p::DatePeriod) = broadcast(+, r, -p) diff --git a/src/conversions.jl b/src/conversions.jl index 1e896d350..4f693d8ae 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -6,20 +6,20 @@ using Mocking const utc_tz = FixedTimeZone("UTC") """ - DateTime(::ZonedDateTime) -> DateTime + DateTime(::Localized) -> DateTime Returns an equivalent `DateTime` without any `TimeZone` information. """ -DateTime(zdt::ZonedDateTime) = localtime(zdt) +DateTime(ldt::Localized) = localtime(ldt) """ - now(::TimeZone) -> ZonedDateTime + now(::TimeZone) -> Localized -Returns a `ZonedDateTime` corresponding to the user's system time in the specified `TimeZone`. +Returns a `Localized` corresponding to the user's system time in the specified `TimeZone`. """ function now(tz::TimeZone) utc = unix2datetime(time()) - ZonedDateTime(utc, tz, from_utc=true) + Localized(utc, tz, from_utc=true) end """ @@ -43,9 +43,9 @@ julia> today(tz"Pacific/Midway"), today(tz"Pacific/Apia") today(tz::TimeZone) = Date(localtime(now(tz))) """ - todayat(tod::Time, tz::TimeZone, [amb]) -> ZonedDateTime + todayat(tod::Time, tz::TimeZone, [amb]) -> Localized -Creates a `ZonedDateTime` for today at the specified time of day. If the result is ambiguous +Creates a `Localized` for today at the specified time of day. If the result is ambiguous in the given `TimeZone` then `amb` can be supplied to resolve ambiguity. # Examples @@ -59,65 +59,83 @@ julia> todayat(Time(10, 30), tz"Europe/Warsaw") ``` """ function todayat(tod::Time, tz::VariableTimeZone, amb::Union{Integer,Bool}) - ZonedDateTime((@mock today(tz)) + tod, tz, amb) + Localized((@mock today(tz)) + tod, tz, amb) end -todayat(tod::Time, tz::TimeZone) = ZonedDateTime((@mock today(tz)) + tod, tz) +todayat(tod::Time, tz::TimeZone) = Localized((@mock today(tz)) + tod, tz) """ - astimezone(zdt::ZonedDateTime, tz::TimeZone) -> ZonedDateTime + astimezone(ldt::Localized, tz::TimeZone) -> Localized -Converts a `ZonedDateTime` from its current `TimeZone` into the specified `TimeZone`. +Converts a `Localized` from its current `TimeZone` into the specified `TimeZone`. """ function astimezone end -function astimezone(zdt::ZonedDateTime, tz::VariableTimeZone) +function astimezone(ldt::Localized, tz::VariableTimeZone) i = searchsortedlast( - tz.transitions, zdt.utc_datetime, + tz.transitions, ldt.utc_datetime, by=v -> typeof(v) == Transition ? v.utc_datetime : v, ) if i == 0 - throw(NonExistentTimeError(localtime(zdt), tz)) + throw(NonExistentTimeError(localtime(ldt), tz)) end zone = tz.transitions[i].zone - return ZonedDateTime(zdt.utc_datetime, tz, zone) + return Localized(ldt.utc_datetime, tz, zone) end -function astimezone(zdt::ZonedDateTime, tz::FixedTimeZone) - return ZonedDateTime(zdt.utc_datetime, tz, tz) +function astimezone(ldt::Localized, tz::FixedTimeZone) + return Localized(ldt.utc_datetime, tz, tz) end -function zdt2julian(zdt::ZonedDateTime) - datetime2julian(utc(zdt)) +""" + restrict(ldt::Localized) -> Localized + +Return a restricted representation of the localized datetime or throws an error if that +isn't possible. +""" +restrict(ldt::Localized) = Localized(ldt.utc_datetime, ldt.timezone, ldt.zone, true) + +""" + relax(ldt::Localized) -> Localized + +Return a relaxed representation of the localized datetime. +""" +relax(ldt::Localized) = Localized(ldt.utc_datetime, ldt.timezone, ldt.zone, false) + +function localized2julian(ldt::Localized) + datetime2julian(utc(ldt)) end -function zdt2julian(::Type{T}, zdt::ZonedDateTime) where T<:Integer - floor(T, datetime2julian(utc(zdt))) +function localized2julian(::Type{T}, ldt::Localized) where T<:Integer + floor(T, datetime2julian(utc(ldt))) end -function zdt2julian(::Type{T}, zdt::ZonedDateTime) where T<:Real - convert(T, datetime2julian(utc(zdt))) +function localized2julian(::Type{T}, ldt::Localized) where T<:Real + convert(T, datetime2julian(utc(ldt))) end -function julian2zdt(jd::Real) - ZonedDateTime(julian2datetime(jd), utc_tz, from_utc=true) +function julian2localized(jd::Real) + Localized(julian2datetime(jd), utc_tz, from_utc=true) end -function zdt2unix(zdt::ZonedDateTime) - datetime2unix(utc(zdt)) +function localized2unix(ldt::Localized) + datetime2unix(utc(ldt)) end -function zdt2unix(::Type{T}, zdt::ZonedDateTime) where T<:Integer - floor(T, datetime2unix(utc(zdt))) +function localized2unix(::Type{T}, ldt::Localized) where T<:Integer + floor(T, datetime2unix(utc(ldt))) end -function zdt2unix(::Type{T}, zdt::ZonedDateTime) where T<:Real - convert(T, datetime2unix(utc(zdt))) +function localized2unix(::Type{T}, ldt::Localized) where T<:Real + convert(T, datetime2unix(utc(ldt))) end -function unix2zdt(seconds::Real) - ZonedDateTime(unix2datetime(seconds), utc_tz, from_utc=true) +function unix2localized(seconds::Real) + Localized(unix2datetime(seconds), utc_tz, from_utc=true) end + +Base.convert(::Type{Localized{T, false}}, x::Localized{T, true}) where T = relax(x) +Base.convert(::Type{Localized{T, true}}, x::Localized{T, false}) where T = restrict(x) \ No newline at end of file diff --git a/src/deprecated.jl b/src/deprecated.jl index 57728e9bd..94c08849f 100644 --- a/src/deprecated.jl +++ b/src/deprecated.jl @@ -11,12 +11,18 @@ end # JuliaLang/julia#24258 if VERSION < v"0.7.0-DEV.2778" # Only remove this method when support for Julia 0.6 is dropped. - colon(start::T, stop::T) where {T <: ZonedDateTime} = start:Day(1):stop + colon(start::T, stop::T) where {T <: Localized} = start:Day(1):stop elseif VERSION < v"0.7.0-DEV.4003" # JuliaLang/julia#26074 - @deprecate colon(start::T, stop::T) where {T <: ZonedDateTime} start:Day(1):stop false + @deprecate colon(start::T, stop::T) where {T <: Localized} start:Day(1):stop false else # Only remove this deprecation when support for Julia 0.7 is dropped. - @deprecate (:)(start::T, stop::T) where {T <: ZonedDateTime} start:Day(1):stop false + @deprecate (:)(start::T, stop::T) where {T <: Localized} start:Day(1):stop false end +@deprecate_binding ZonedDateTime Localized +@deprecate julian2zdt julian2localized +@deprecate zdt2julia localized2julian +@deprecate unix2zdt unix2localized +@deprecate zdt2unix localized2unix + # END TimeZones 0.6 deprecations diff --git a/src/discovery.jl b/src/discovery.jl index 9ccc6fa5e..2ba758207 100644 --- a/src/discovery.jl +++ b/src/discovery.jl @@ -129,16 +129,16 @@ end """ - next_transition_instant(zdt::ZonedDateTime) -> ZonedDateTime - next_transition_instant(tz::TimeZone=localzone()) -> ZonedDateTime + next_transition_instant(ldt::Localized) -> Localized + next_transition_instant(tz::TimeZone=localzone()) -> Localized Determine the next instant at which a time zone transition occurs (typically due to daylight-savings time). -Note that the provided `ZonedDateTime` isn't normally constructable: +Note that the provided `Localized` isn't normally constructable: ```julia -julia> instant = next_transition_instant(ZonedDateTime(2018, 3, 1, tz"Europe/London")) +julia> instant = next_transition_instant(Localized(2018, 3, 1, tz"Europe/London")) 2018-03-25T01:00:00+00:00 julia> instant - Millisecond(1) # Instant prior to the change @@ -147,38 +147,38 @@ julia> instant - Millisecond(1) # Instant prior to the change julia> instant - Millisecond(0) # Instant after the change 2018-03-25T02:00:00+01:00 -julia> ZonedDateTime(2018, 3, 25, 1, tz"Europe/London") # Cannot normally construct the `instant` +julia> Localized(2018, 3, 25, 1, tz"Europe/London") # Cannot normally construct the `instant` ERROR: NonExistentTimeError: Local DateTime 2018-03-25T01:00:00 does not exist within Europe/London ... ``` """ next_transition_instant -function next_transition_instant(zdt::ZonedDateTime) - tz = zdt.timezone +function next_transition_instant(ldt::Localized) + tz = ldt.timezone # Determine the index of the transition which occurs after the UTC datetime specified index = searchsortedfirst( - tz.transitions, TimeZones.utc(zdt), + tz.transitions, TimeZones.utc(ldt), by=el -> isa(el, TimeZones.Transition) ? el.utc_datetime : el, ) # Use the UTC datetime of the transition and the offset information prior to the - # transition to create a `ZonedDateTime` which cannot be constructed with the high-level + # transition to create a `Localized` which cannot be constructed with the high-level # constructors. The instant constructed is equivalent to the first instant after the # transition but visually appears to be before the transition. For example in a # transition where the clock changes from 01:59 → 03:00 we would return 02:00 where # the UTC datetime of 02:00 == 03:00. utc_datetime = tz.transitions[index].utc_datetime prev_zone = tz.transitions[index - 1].zone - ZonedDateTime(utc_datetime, tz, prev_zone) + Localized(utc_datetime, tz, prev_zone) end next_transition_instant(tz::TimeZone=localzone()) = next_transition_instant(@mock now(tz)) """ - show_next_transition(io::IO=stdout, zdt::ZonedDateTime) + show_next_transition(io::IO=stdout, ldt::Localized) show_next_transition(io::IO=stdout, tz::TimeZone=localzone()) Display useful information about the next time zone transition (typically @@ -193,14 +193,14 @@ due to daylight-savings time). Information displayed includes: * Transition To: the instant after the transition occurs ```julia -julia> show_next_transition(ZonedDateTime(2018, 8, 1, tz"Europe/London")) +julia> show_next_transition(Localized(2018, 8, 1, tz"Europe/London")) Transition Date: 2018-10-28 Local Time Change: 02:00 → 01:00 (Backward) Offset Change: UTC+0/+1 → UTC+0/+0 Transition From: 2018-10-28T01:59:59.999+01:00 (BST) Transition To: 2018-10-28T01:00:00.000+00:00 (GMT) -julia> show_next_transition(ZonedDateTime(2011, 12, 1, tz"Pacific/Apia")) +julia> show_next_transition(Localized(2011, 12, 1, tz"Pacific/Apia")) Transition Date: 2011-12-30 Local Time Change: 00:00 → 00:00 (Forward) Offset Change: UTC-11/+1 → UTC+13/+1 @@ -210,29 +210,29 @@ Transition To: 2011-12-31T00:00:00.000+14:00 """ show_next_transition -function show_next_transition(io::IO, zdt::ZonedDateTime) - instant = next_transition_instant(zdt) +function show_next_transition(io::IO, ldt::Localized) + instant = next_transition_instant(ldt) from, to = instant - Millisecond(1), instant + Millisecond(0) direction = value(to.zone.offset - from.zone.offset) < 0 ? "Backward" : "Forward" - function zdt_format(zdt) - name_suffix = string(zdt.zone.name) + function loc_format(ldt) + name_suffix = string(ldt.zone.name) !isempty(name_suffix) && (name_suffix = string(" (", name_suffix, ")")) string( - Dates.format(zdt, dateformat"yyyy-mm-ddTHH:MM:SS.sss"), - zdt.zone.offset, # Note: "zzz" will not work in the format above as is + Dates.format(ldt, dateformat"yyyy-mm-ddTHH:MM:SS.sss"), + ldt.zone.offset, # Note: "zzz" will not work in the format above as is name_suffix, ) end - function time_format(zdt) - Dates.format(zdt, second(zdt) == 0 ? dateformat"HH:MM" : dateformat"HH:MM:SS") + function time_format(ldt) + Dates.format(ldt, second(ldt) == 0 ? dateformat"HH:MM" : dateformat"HH:MM:SS") end println(io, "Transition Date: ", Dates.format(instant, dateformat"yyyy-mm-dd")) println(io, "Local Time Change: ", time_format(instant), " → ", time_format(to), " (", direction, ")") println(io, "Offset Change: ", repr(from.zone.offset), " → ", repr(to.zone.offset)) - println(io, "Transition From: ", zdt_format(from)) - println(io, "Transition To: ", zdt_format(to)) + println(io, "Transition From: ", loc_format(from)) + println(io, "Transition To: ", loc_format(to)) end diff --git a/src/interpret.jl b/src/interpret.jl index 3a7ab8d76..92987a1ae 100644 --- a/src/interpret.jl +++ b/src/interpret.jl @@ -50,8 +50,8 @@ that UTC context will always return a range of length one. """ transition_range(::DateTime, ::VariableTimeZone, ::Type{Union{Local,UTC}}) -function interpret(local_dt::DateTime, tz::VariableTimeZone, ::Type{Local}) - interpretations = ZonedDateTime[] +function interpret(local_dt::DateTime, tz::VariableTimeZone, ::Type{Local}, strict::Bool=true) + interpretations = Localized[] t = tz.transitions n = length(t) for i in transition_range(local_dt, tz, Local) @@ -59,33 +59,33 @@ function interpret(local_dt::DateTime, tz::VariableTimeZone, ::Type{Local}) utc_dt = local_dt - t[i].zone.offset if utc_dt >= t[i].utc_datetime && (i == n || utc_dt < t[i + 1].utc_datetime) - push!(interpretations, ZonedDateTime(utc_dt, tz, t[i].zone)) + push!(interpretations, Localized(utc_dt, tz, t[i].zone, strict)) end end return interpretations end -function interpret(utc_dt::DateTime, tz::VariableTimeZone, ::Type{UTC}) +function interpret(utc_dt::DateTime, tz::VariableTimeZone, ::Type{UTC}, strict::Bool=true) range = transition_range(utc_dt, tz, UTC) length(range) == 1 || error("Internal TimeZones error: A UTC DateTime should only have a single interpretation") i = first(range) - return [ZonedDateTime(utc_dt, tz, tz.transitions[i].zone)] + return [Localized(utc_dt, tz, tz.transitions[i].zone, strict)] end """ - interpret(dt::DateTime, tz::VariableTimeZone, context::Type{Union{Local,UTC}}) -> Array{ZonedDateTime} + interpret(dt::DateTime, tz::VariableTimeZone, context::Type{Union{Local,UTC}}, strict::Bool) -> Array{Localized} -Produces a list of possible `ZonedDateTime`s given a `DateTime` and `VariableTimeZone`. +Produces a list of possible `Localized`s given a `DateTime` and `VariableTimeZone`. The result will be returned in chronological order. Note that `DateTime`s in the local context typically return 0-2 results while the UTC context will always return 1 result. """ -interpret(::DateTime, ::VariableTimeZone, ::Type{Union{Local,UTC}}) +interpret(::DateTime, ::VariableTimeZone, ::Type{Union{Local,UTC}}, strict::Bool=true) """ - shift_gap(local_dt::DateTime, tz::VariableTimeZone) -> Array{ZonedDateTime} + shift_gap(local_dt::DateTime, tz::VariableTimeZone) -> Array{Localized} -Given a non-existent local `DateTime` in a `TimeZone` produces two valid `ZonedDateTime`s +Given a non-existent local `DateTime` in a `TimeZone` produces two valid `Localized`s that span the gap. Providing a valid local `DateTime` returns an empty array. Note that this function does not support passing in a UTC `DateTime` since there are no non-existent UTC `DateTime`s. @@ -93,8 +93,8 @@ function does not support passing in a UTC `DateTime` since there are no non-exi Aside: the function name refers to a period of invalid local time (gap) caused by daylight saving time or offset changes (shift). """ -function shift_gap(local_dt::DateTime, tz::VariableTimeZone) - boundaries = ZonedDateTime[] +function shift_gap(local_dt::DateTime, tz::VariableTimeZone, strict::Bool=true) + boundaries = Localized{DateTime, strict}[] t = tz.transitions n = length(t) delta = eps(local_dt) @@ -113,11 +113,11 @@ function shift_gap(local_dt::DateTime, tz::VariableTimeZone) # UTC DateTime proceeds the end of the transition range elseif !ends_before - push!(boundaries, ZonedDateTime(t[i + 1].utc_datetime - delta, tz, t[i].zone)) + push!(boundaries, Localized(t[i + 1].utc_datetime - delta, tz, t[i].zone, strict)) # UTC DateTime preceeds the start of the transition range elseif !starts_after - push!(boundaries, ZonedDateTime(t[i].utc_datetime, tz, t[i].zone)) + push!(boundaries, Localized(t[i].utc_datetime, tz, t[i].zone, strict)) end # A slower but much easier to understand version of the above code: @@ -128,8 +128,8 @@ function shift_gap(local_dt::DateTime, tz::VariableTimeZone) # elseif !starts_after # push!( # boundaries, - # ZonedDateTime(t[i].utc_datetime - eps(t[i].utc_datetime), tz, from_utc=true), - # ZonedDateTime(t[i].utc_datetime, tz, from_utc=true), + # Localized(t[i].utc_datetime - eps(t[i].utc_datetime), tz, from_utc=true), + # Localized(t[i].utc_datetime, tz, from_utc=true), # ) # end end @@ -149,17 +149,17 @@ end """ first_valid(local_dt::DateTime, tz::VariableTimeZone, step::Period) -Construct a valid `ZonedDateTime` by adjusting the local `DateTime`. If the local `DateTime` +Construct a valid `Localized` by adjusting the local `DateTime`. If the local `DateTime` is non-existent then it will be adjusted using the `step` to be *after* the gap. When the local `DateTime` is ambiguous the *first* ambiguous `DateTime` will be returned. """ -function first_valid(local_dt::DateTime, tz::VariableTimeZone, step::Period) - possible = interpret(local_dt, tz, Local) +function first_valid(local_dt::DateTime, tz::VariableTimeZone, step::Period, strict::Bool=true) + possible = interpret(local_dt, tz, Local, strict) # Skip all non-existent local datetimes. while isempty(possible) local_dt += step - possible = interpret(local_dt, tz, Local) + possible = interpret(local_dt, tz, Local, strict) end return first(possible) @@ -168,28 +168,28 @@ end """ last_valid(local_dt::DateTime, tz::VariableTimeZone, step::Period) -Construct a valid `ZonedDateTime` by adjusting the local `DateTime`. If the local `DateTime` +Construct a valid `Localized` by adjusting the local `DateTime`. If the local `DateTime` is non-existent then it will be adjusted using the `step` to be *before* the gap. When the local `DateTime` is ambiguous the *last* ambiguous `DateTime` will be returned. """ -function last_valid(local_dt::DateTime, tz::VariableTimeZone, step::Period) - possible = interpret(local_dt, tz, Local) +function last_valid(local_dt::DateTime, tz::VariableTimeZone, step::Period, strict::Bool=true) + possible = interpret(local_dt, tz, Local, strict) # Skip all non-existent local datetimes. while isempty(possible) local_dt -= step - possible = interpret(local_dt, tz, Local) + possible = interpret(local_dt, tz, Local, strict) end return last(possible) end -function first_valid(local_dt::DateTime, tz::VariableTimeZone) - possible = interpret(local_dt, tz, Local) - return isempty(possible) ? last(shift_gap(local_dt, tz)) : first(possible) +function first_valid(local_dt::DateTime, tz::VariableTimeZone, strict::Bool=true) + possible = interpret(local_dt, tz, Local, strict) + return isempty(possible) ? last(shift_gap(local_dt, tz, strict)) : first(possible) end -function last_valid(local_dt::DateTime, tz::VariableTimeZone) - possible = interpret(local_dt, tz, Local) - return isempty(possible) ? first(shift_gap(local_dt, tz)) : last(possible) +function last_valid(local_dt::DateTime, tz::VariableTimeZone, strict::Bool=true) + possible = interpret(local_dt, tz, Local, strict) + return isempty(possible) ? first(shift_gap(local_dt, tz, strict)) : last(possible) end diff --git a/src/io.jl b/src/io.jl index efe922139..b9c8d2d9a 100644 --- a/src/io.jl +++ b/src/io.jl @@ -6,7 +6,7 @@ function print(io::IO, tz::FixedTimeZone) name = string(tz.name) isempty(name) ? print(io, "UTC", tz.offset) : print(io, name) end -print(io::IO, zdt::ZonedDateTime) = print(io, localtime(zdt), zdt.zone.offset) +print(io::IO, ldt::Localized) = print(io, localtime(ldt), ldt.zone.offset) function show(io::IO, t::Transition) name_str = string(t.zone.name) @@ -60,4 +60,4 @@ function show(io::IO, tz::VariableTimeZone) end end -show(io::IO,dt::ZonedDateTime) = print(io, string(dt)) +show(io::IO,dt::Localized) = print(io, string(dt)) diff --git a/src/parse.jl b/src/parse.jl index c56a7a596..4ebb5a266 100644 --- a/src/parse.jl +++ b/src/parse.jl @@ -96,21 +96,21 @@ function tryparsenext(d::DatePart{'Z'}, str, i, len) tryparsenext_tz(str, i, len, min_width(d), max_width(d)) end -function format(io::IO, d::DatePart{'z'}, zdt, locale) - write(io, string(zdt.zone.offset)) +function format(io::IO, d::DatePart{'z'}, ldt, locale) + write(io, string(ldt.zone.offset)) end -function format(io::IO, d::DatePart{'Z'}, zdt, locale) - write(io, string(zdt.zone)) # In most cases will be an abbreviation. +function format(io::IO, d::DatePart{'Z'}, ldt, locale) + write(io, string(ldt.zone)) # In most cases will be an abbreviation. end # Note: ISOZonedDateTimeFormat is defined in the module __init__ which means that this # function can not be called from within this module. TODO: Ignore linting for this line -function ZonedDateTime(str::AbstractString, df::DateFormat=ISOZonedDateTimeFormat) - parse(ZonedDateTime, str, df) +function Localized(str::AbstractString, df::DateFormat=ISOZonedDateTimeFormat) + parse(Localized, str, df) end -function ZonedDateTime(str::AbstractString, format::AbstractString; locale::AbstractString="english") - ZonedDateTime(str, DateFormat(format,locale)) +function Localized(str::AbstractString, format::AbstractString; locale::AbstractString="english") + Localized(str, DateFormat(format,locale)) end -default_format(::Type{ZonedDateTime}) = ISOZonedDateTimeFormat +default_format(::Type{Localized}) = ISOZonedDateTimeFormat diff --git a/src/ranges.jl b/src/ranges.jl index 083069ca8..25a4fae20 100644 --- a/src/ranges.jl +++ b/src/ranges.jl @@ -1,11 +1,11 @@ import Compat.Dates: guess """ - guess(start::ZonedDateTime, finish::ZonedDateTime, step) -> Integer + guess(start::Localized, finish::Localized, step) -> Integer Given a start and end date, indicates how many steps/periods are between them. Defining this -function allows `StepRange`s to be defined for `ZonedDateTime`s. +function allows `StepRange`s to be defined for `Localized`s. """ -function guess(start::ZonedDateTime, finish::ZonedDateTime, step) +function guess(start::Localized, finish::Localized, step) guess(start.utc_datetime, finish.utc_datetime, step) end diff --git a/src/rounding.jl b/src/rounding.jl index 387a39a3a..cc231cbff 100644 --- a/src/rounding.jl +++ b/src/rounding.jl @@ -1,33 +1,33 @@ import Compat.Dates: Period, DatePeriod, TimePeriod -function Base.floor(zdt::ZonedDateTime, p::DatePeriod) - return ZonedDateTime(floor(localtime(zdt), p), timezone(zdt)) +function Base.floor(ldt::Localized, p::DatePeriod) + return Localized(floor(localtime(ldt), p), timezone(ldt)) end -function Base.floor(zdt::ZonedDateTime, p::TimePeriod) +function Base.floor(ldt::Localized, p::TimePeriod) # Rounding is done using the current fixed offset to avoid transitional ambiguities. - dt = floor(localtime(zdt), p) - utc_dt = dt - zdt.zone.offset - return ZonedDateTime(utc_dt, timezone(zdt); from_utc=true) + dt = floor(localtime(ldt), p) + utc_dt = dt - ldt.zone.offset + return Localized(utc_dt, timezone(ldt); from_utc=true) end -function Base.ceil(zdt::ZonedDateTime, p::DatePeriod) - return ZonedDateTime(ceil(localtime(zdt), p), timezone(zdt)) +function Base.ceil(ldt::Localized, p::DatePeriod) + return Localized(ceil(localtime(ldt), p), timezone(ldt)) end -#function Dates.floorceil(zdt::ZonedDateTime, p::Dates.DatePeriod) - #return floor(zdt, p), ceil(zdt, p) +#function Dates.floorceil(ldt::Localized, p::Dates.DatePeriod) + #return floor(ldt, p), ceil(ldt, p) #end """ - floor(zdt::ZonedDateTime, p::Period) -> ZonedDateTime - floor(zdt::ZonedDateTime, p::Type{Period}) -> ZonedDateTime + floor(ldt::Localized, p::Period) -> Localized + floor(ldt::Localized, p::Type{Period}) -> Localized -Returns the nearest `ZonedDateTime` less than or equal to `zdt` at resolution `p`. The -result will be in the same time zone as `zdt`. +Returns the nearest `Localized` less than or equal to `ldt` at resolution `p`. The +result will be in the same time zone as `ldt`. -For convenience, `p` may be a type instead of a value: `floor(zdt, Dates.Hour)` is a -shortcut for `floor(zdt, Dates.Hour(1))`. +For convenience, `p` may be a type instead of a value: `floor(ldt, Dates.Hour)` is a +shortcut for `floor(ldt, Dates.Hour(1))`. `VariableTimeZone` transitions are handled as for `round`. @@ -37,27 +37,27 @@ The `America/Winnipeg` time zone transitioned from Central Standard Time (UTC-6: Central Daylight Time (UTC-5:00) on 2016-03-13, moving directly from 01:59:59 to 03:00:00. ```julia -julia> zdt = ZonedDateTime(2016, 3, 13, 1, 45, TimeZone("America/Winnipeg")) +julia> ldt = Localized(2016, 3, 13, 1, 45, TimeZone("America/Winnipeg")) 2016-03-13T01:45:00-06:00 -julia> floor(zdt, Dates.Day) +julia> floor(ldt, Dates.Day) 2016-03-13T00:00:00-06:00 -julia> floor(zdt, Dates.Hour) +julia> floor(ldt, Dates.Hour) 2016-03-13T01:00:00-06:00 ``` """ -Base.floor(::TimeZones.ZonedDateTime, ::Union{Period, Type{Period}}) +Base.floor(::TimeZones.Localized, ::Union{Period, Type{Period}}) """ - ceil(zdt::ZonedDateTime, p::Period) -> ZonedDateTime - ceil(zdt::ZonedDateTime, p::Type{Period}) -> ZonedDateTime + ceil(ldt::Localized, p::Period) -> Localized + ceil(ldt::Localized, p::Type{Period}) -> Localized -Returns the nearest `ZonedDateTime` greater than or equal to `zdt` at resolution `p`. -The result will be in the same time zone as `zdt`. +Returns the nearest `Localized` greater than or equal to `ldt` at resolution `p`. +The result will be in the same time zone as `ldt`. -For convenience, `p` may be a type instead of a value: `ceil(zdt, Dates.Hour)` is a -shortcut for `ceil(zdt, Dates.Hour(1))`. +For convenience, `p` may be a type instead of a value: `ceil(ldt, Dates.Hour)` is a +shortcut for `ceil(ldt, Dates.Hour(1))`. `VariableTimeZone` transitions are handled as for `round`. @@ -67,35 +67,35 @@ The `America/Winnipeg` time zone transitioned from Central Standard Time (UTC-6: Central Daylight Time (UTC-5:00) on 2016-03-13, moving directly from 01:59:59 to 03:00:00. ```julia -julia> zdt = ZonedDateTime(2016, 3, 13, 1, 45, TimeZone("America/Winnipeg")) +julia> ldt = Localized(2016, 3, 13, 1, 45, TimeZone("America/Winnipeg")) 2016-03-13T01:45:00-06:00 -julia> ceil(zdt, Dates.Day) +julia> ceil(ldt, Dates.Day) 2016-03-14T00:00:00-05:00 -julia> ceil(zdt, Dates.Hour) +julia> ceil(ldt, Dates.Hour) 2016-03-13T03:00:00-05:00 ``` """ -Base.ceil(::TimeZones.ZonedDateTime, ::Union{Period, Type{Period}}) +Base.ceil(::TimeZones.Localized, ::Union{Period, Type{Period}}) """ - round(zdt::ZonedDateTime, p::Period, [r::RoundingMode]) -> ZonedDateTime - round(zdt::ZonedDateTime, p::Type{Period}, [r::RoundingMode]) -> ZonedDateTime + round(ldt::Localized, p::Period, [r::RoundingMode]) -> Localized + round(ldt::Localized, p::Type{Period}, [r::RoundingMode]) -> Localized -Returns the `ZonedDateTime` nearest to `zdt` at resolution `p`. The result will be in the -same time zone as `zdt`. By default (`RoundNearestTiesUp`), ties (e.g., rounding 9:30 to the +Returns the `Localized` nearest to `ldt` at resolution `p`. The result will be in the +same time zone as `ldt`. By default (`RoundNearestTiesUp`), ties (e.g., rounding 9:30 to the nearest hour) will be rounded up. -For convenience, `p` may be a type instead of a value: `round(zdt, Dates.Hour)` is a -shortcut for `round(zdt, Dates.Hour(1))`. +For convenience, `p` may be a type instead of a value: `round(ldt, Dates.Hour)` is a +shortcut for `round(ldt, Dates.Hour(1))`. Valid rounding modes for `round(::TimeType, ::Period, ::RoundingMode)` are `RoundNearestTiesUp` (default), `RoundDown` (`floor`), and `RoundUp` (`ceil`). ### `VariableTimeZone` Transitions -Instead of performing rounding operations on the `ZonedDateTime`'s internal UTC `DateTime`, +Instead of performing rounding operations on the `Localized`'s internal UTC `DateTime`, which would be computationally less expensive, rounding is done in the local time zone. This ensures that rounding behaves as expected and is maximally meaningful. @@ -107,11 +107,11 @@ wouldn't be on the hour in the local time zone. When `p` is a `DatePeriod` rounding is done in the local time zone in a straightforward fashion. When `p` is a `TimePeriod` the likelihood of encountering an ambiguous or non-existent time (due to daylight saving time transitions) is increased. To resolve this -issue, rounding a `ZonedDateTime` with a `VariableTimeZone` to a `TimePeriod` uses the -`DateTime` value in the appropriate `FixedTimeZone`, then reconverts it to a `ZonedDateTime` +issue, rounding a `Localized` with a `VariableTimeZone` to a `TimePeriod` uses the +`DateTime` value in the appropriate `FixedTimeZone`, then reconverts it to a `Localized` in the appropriate `VariableTimeZone` afterward. -Rounding is not an entirely "safe" operation for `ZonedDateTime`s, as in some cases +Rounding is not an entirely "safe" operation for `Localized`s, as in some cases historical transitions for some time zones (such as `Asia/Colombo`) occur at midnight. In such cases rounding to a `DatePeriod` may still result in an `AmbiguousTimeError` or a `NonExistentTimeError`. (But these events should be relatively rare.) @@ -124,13 +124,13 @@ The `America/Winnipeg` time zone transitioned from Central Standard Time (UTC-6: Central Daylight Time (UTC-5:00) on 2016-03-13, moving directly from 01:59:59 to 03:00:00. ```julia -julia> zdt = ZonedDateTime(2016, 3, 13, 1, 45, TimeZone("America/Winnipeg")) +julia> ldt = Localized(2016, 3, 13, 1, 45, TimeZone("America/Winnipeg")) 2016-03-13T01:45:00-06:00 -julia> round(zdt, Dates.Hour) +julia> round(ldt, Dates.Hour) 2016-03-13T03:00:00-05:00 -julia> round(zdt, Dates.Day) +julia> round(ldt, Dates.Day) 2016-03-13T00:00:00-06:00 ``` @@ -138,14 +138,14 @@ The `Asia/Colombo` time zone revised the definition of Lanka Time from UTC+6:30 on 1996-10-26, moving from 00:29:59 back to 00:00:00. ```julia -julia> zdt = ZonedDateTime(1996, 10, 25, 23, 45, TimeZone("Asia/Colombo")) +julia> ldt = Localized(1996, 10, 25, 23, 45, TimeZone("Asia/Colombo")) 1996-10-25T23:45:00+06:30 -julia> round(zdt, Dates.Hour) +julia> round(ldt, Dates.Hour) 1996-10-26T00:00:00+06:30 -julia> round(zdt, Dates.Day) +julia> round(ldt, Dates.Day) ERROR: Local DateTime 1996-10-26T00:00:00 is ambiguous ``` """ # Defined in base/dates/rounding.jl -Base.round(::TimeZones.ZonedDateTime, ::Union{Period, Type{Period}}) +Base.round(::TimeZones.Localized, ::Union{Period, Type{Period}}) diff --git a/src/types.jl b/src/types.jl index eafb823bd..5428bcc56 100644 --- a/src/types.jl +++ b/src/types.jl @@ -129,131 +129,157 @@ function VariableTimeZone(name::AbstractString, transitions::Vector{Transition}) return VariableTimeZone(Symbol(name), transitions, Nullable{DateTime}()) end +# Define invalid TimeZone types +abstract type InvalidTimeZone <: TimeZone end -# """ -# ZonedDateTime +struct NonExistent <: InvalidTimeZone end + +struct Ambiguous <: InvalidTimeZone end +# """ +# Localized +# # A `DateTime` that includes `TimeZone` information. # """ -struct ZonedDateTime <: AbstractDateTime - utc_datetime::DateTime +struct Localized{T<:Compat.AbstractDateTime, S} <: AbstractDateTime + utc_datetime::T timezone::TimeZone - zone::FixedTimeZone # The current zone for the utc_datetime. + zone::Union{FixedTimeZone, InvalidTimeZone} # The current zone for the utc_datetime. +end - function ZonedDateTime(utc_datetime::DateTime, timezone::TimeZone, zone::FixedTimeZone) - return new(utc_datetime, timezone, zone) - end +function Localized( + utc_datetime::T, timezone::TimeZone, zone::FixedTimeZone, strict::Bool=true +) where T<:Compat.AbstractDateTime + Localized{T, strict}(utc_datetime, timezone, zone) +end - function ZonedDateTime(utc_datetime::DateTime, timezone::VariableTimeZone, zone::FixedTimeZone) - if utc_datetime >= get(timezone.cutoff, typemax(DateTime)) - throw(UnhandledTimeError(timezone)) - end +function Localized( + utc_datetime::T, timezone::TimeZone, zone::NonExistent, strict::Bool=true +) where T<:Compat.AbstractDateTime + strict && throw(NonExistentTimeError(utc_datetime, timezone)) + Localized{T, strict}(utc_datetime, timezone, zone) +end + +function Localized( + utc_datetime::T, timezone::TimeZone, zone::Ambiguous, strict::Bool=true +) where T<:Compat.AbstractDateTime + strict && throw(AmbiguousTimeError(utc_datetime, timezone)) + Localized{T, strict}(utc_datetime, timezone, zone) +end - return new(utc_datetime, timezone, zone) +function Localized( + utc_datetime::T, timezone::VariableTimeZone, zone::FixedTimeZone, strict::Bool=true +) where T<:Compat.AbstractDateTime + if utc_datetime >= get(timezone.cutoff, typemax(DateTime)) + throw(UnhandledTimeError(timezone)) end + + return Localized{T, strict}(utc_datetime, timezone, zone) end """ - ZonedDateTime(dt::DateTime, tz::TimeZone; from_utc=false) -> ZonedDateTime + Localized(dt::DateTime, tz::TimeZone; from_utc=false, strict=true) -> Localized -Construct a `ZonedDateTime` by applying a `TimeZone` to a `DateTime`. When the `from_utc` +Construct a `Localized` by applying a `TimeZone` to a `DateTime`. When the `from_utc` keyword is true the given `DateTime` is assumed to be in UTC instead of in local time and is converted to the specified `TimeZone`. Note that when `from_utc` is true the given `DateTime` will always exists and is never ambiguous. """ -function ZonedDateTime(dt::DateTime, tz::VariableTimeZone; from_utc::Bool=false) +function Localized(dt::DateTime, tz::VariableTimeZone; from_utc::Bool=false, strict::Bool=true) possible = interpret(dt, tz, from_utc ? UTC : Local) num = length(possible) if num == 1 return first(possible) elseif num == 0 - throw(NonExistentTimeError(dt, tz)) + return Localized(dt, tz, NonExistent(), strict) else - throw(AmbiguousTimeError(dt, tz)) + return Localized(dt, tz, Ambiguous(), strict) end end -function ZonedDateTime(dt::DateTime, tz::FixedTimeZone; from_utc::Bool=false) +function Localized(dt::DateTime, tz::FixedTimeZone; from_utc::Bool=false, strict::Bool=true) utc_dt = from_utc ? dt : dt - tz.offset - return ZonedDateTime(utc_dt, tz, tz) + return Localized(utc_dt, tz, tz, strict) end """ - ZonedDateTime(dt::DateTime, tz::VariableTimeZone, occurrence::Integer) -> ZonedDateTime + Localized(dt::DateTime, tz::VariableTimeZone, occurrence::Integer) -> Localized -Construct a `ZonedDateTime` by applying a `TimeZone` to a `DateTime`. If the `DateTime` is +Construct a `Localized` by applying a `TimeZone` to a `DateTime`. If the `DateTime` is ambiguous within the given time zone you can set `occurrence` to a positive integer to resolve the ambiguity. """ -function ZonedDateTime(dt::DateTime, tz::VariableTimeZone, occurrence::Integer) - possible = interpret(dt, tz, Local) +function Localized(dt::DateTime, tz::VariableTimeZone, occurrence::Integer; strict::Bool=true) + possible = interpret(dt, tz, Local, strict) num = length(possible) if num == 1 return first(possible) elseif num == 0 - throw(NonExistentTimeError(dt, tz)) + return Localized(dt, tz, NonExistent(), strict) elseif occurrence > 0 return possible[occurrence] else - throw(AmbiguousTimeError(dt, tz)) + return Localized(dt, tz, Ambiguous(), strict) end end """ - ZonedDateTime(dt::DateTime, tz::VariableTimeZone, is_dst::Bool) -> ZonedDateTime + Localized(dt::DateTime, tz::VariableTimeZone, is_dst::Bool) -> Localized -Construct a `ZonedDateTime` by applying a `TimeZone` to a `DateTime`. If the `DateTime` is +Construct a `Localized` by applying a `TimeZone` to a `DateTime`. If the `DateTime` is ambiguous within the given time zone you can set `is_dst` to resolve the ambiguity. """ -function ZonedDateTime(dt::DateTime, tz::VariableTimeZone, is_dst::Bool) - possible = interpret(dt, tz, Local) +function Localized(dt::DateTime, tz::VariableTimeZone, is_dst::Bool, strict::Bool=true) + possible = interpret(dt, tz, Local, strict) num = length(possible) if num == 1 return first(possible) elseif num == 0 - throw(NonExistentTimeError(dt, tz)) + return Localized(dt, tz, NonExistent(), strict) elseif num == 2 - mask = [isdst(zdt.zone.offset) for zdt in possible] + mask = [isdst(ldt.zone.offset) for ldt in possible] # Mask is expected to be unambiguous. - !xor(mask...) && throw(AmbiguousTimeError(dt, tz)) + if !xor(mask...) + return Localized(dt, tz, Ambiguous(), strict) + end occurrence = findfirst(d -> d == is_dst, mask) return possible[occurrence] else - throw(AmbiguousTimeError(dt, tz)) + return Localized(dt, tz, Ambiguous(), strict) end end # Convenience constructors @doc """ - ZonedDateTime(y, [m, d, h, mi, s, ms], tz, [amb]) -> DateTime + Localized(y, [m, d, h, mi, s, ms], tz, [amb]) -> DateTime -Construct a `ZonedDateTime` type by parts. Arguments `y, m, ..., ms` must be convertible to +Construct a `Localized` type by parts. Arguments `y, m, ..., ms` must be convertible to `Int64` and `tz` must be a `TimeZone`. If the given `DateTime` is ambiguous in the given `TimeZone` then `amb` can be supplied to resolve ambiguity. -""" ZonedDateTime +""" Localized -@optional function ZonedDateTime(y::Integer, m::Integer=1, d::Integer=1, h::Integer=0, mi::Integer=0, s::Integer=0, ms::Integer=0, tz::VariableTimeZone, amb::Union{Integer,Bool}) - ZonedDateTime(DateTime(y,m,d,h,mi,s,ms), tz, amb) +@optional function Localized(y::Integer, m::Integer=1, d::Integer=1, h::Integer=0, mi::Integer=0, s::Integer=0, ms::Integer=0, tz::VariableTimeZone, amb::Union{Integer,Bool}) + Localized(DateTime(y,m,d,h,mi,s,ms), tz, amb) end -@optional function ZonedDateTime(y::Integer, m::Integer=1, d::Integer=1, h::Integer=0, mi::Integer=0, s::Integer=0, ms::Integer=0, tz::TimeZone) - ZonedDateTime(DateTime(y,m,d,h,mi,s,ms), tz) +@optional function Localized(y::Integer, m::Integer=1, d::Integer=1, h::Integer=0, mi::Integer=0, s::Integer=0, ms::Integer=0, tz::TimeZone) + Localized(DateTime(y,m,d,h,mi,s,ms), tz) end # Parsing constructor. Note we typically don't support passing in time zone information as a # string since we cannot do not know if we need to support resolving ambiguity. -function ZonedDateTime(y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, tz::AbstractString) - ZonedDateTime(DateTime(y,m,d,h,mi,s,ms), TimeZone(tz)) +function Localized(y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, tz::AbstractString) + Localized(DateTime(y,m,d,h,mi,s,ms), TimeZone(tz)) end -function ZonedDateTime(parts::Union{Period,TimeZone}...) +function Localized(parts::Union{Period,TimeZone}...) periods = Period[] timezone = Nullable{TimeZone}() for part in parts @@ -267,7 +293,7 @@ function ZonedDateTime(parts::Union{Period,TimeZone}...) end isnull(timezone) && throw(ArgumentError("Missing time zone")) - return ZonedDateTime(DateTime(periods...), get(timezone)) + return Localized(DateTime(periods...), get(timezone)) end # Promotion @@ -276,24 +302,29 @@ end # undefined promote_rule on TimeType types. # Otherwise, typejoin(T,S) is called (returning TimeType) so no conversion happens, and # isless(promote(x,y)...) is called again, causing a stack overflow. -function promote_rule(::Type{T}, ::Type{S}) where {T<:TimeType, S<:ZonedDateTime} +function promote_rule(::Type{T}, ::Type{S}) where {T<:TimeType, S<:Localized} error("no promotion exists for ", T, " and ", S) end +# Promote strict localized times to relaxed times +function promote_rule(::Type{Localized{T, false}}, ::Type{Localized{T, true}}) where T + Localized{T, false} +end + # Equality -==(a::ZonedDateTime, b::ZonedDateTime) = a.utc_datetime == b.utc_datetime -isless(a::ZonedDateTime, b::ZonedDateTime) = isless(a.utc_datetime, b.utc_datetime) +==(a::Localized, b::Localized) = a.utc_datetime == b.utc_datetime +isless(a::Localized, b::Localized) = isless(a.utc_datetime, b.utc_datetime) -# Note: `hash` and `isequal` assume that the "zone" of a ZonedDateTime is not being set +# Note: `hash` and `isequal` assume that the "zone" of a Localized is not being set # incorrectly. -function hash(zdt::ZonedDateTime, h::UInt) - h = hash(zdt.utc_datetime, h) - h = hash(zdt.timezone, h) +function hash(ldt::Localized, h::UInt) + h = hash(ldt.utc_datetime, h) + h = hash(ldt.timezone, h) return h end -function isequal(a::ZonedDateTime, b::ZonedDateTime) +function isequal(a::Localized, b::Localized) return ( isequal(a.utc_datetime, b.utc_datetime) && isequal(a.timezone, b.timezone) && @@ -312,10 +343,10 @@ function hash(tz::VariableTimeZone, h::UInt) return h end -typemin(::Type{ZonedDateTime}) = ZonedDateTime(typemin(DateTime), utc_tz; from_utc=true) -typemax(::Type{ZonedDateTime}) = ZonedDateTime(typemax(DateTime), utc_tz; from_utc=true) +typemin(::Type{Localized}) = Localized(typemin(DateTime), utc_tz; from_utc=true) +typemax(::Type{Localized}) = Localized(typemax(DateTime), utc_tz; from_utc=true) -function validargs(::Type{ZonedDateTime}, y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, tz::AbstractString) +function validargs(::Type{Localized}, y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, tz::AbstractString) err = validargs(DateTime, y, m, d, h, mi, s, ms) isnull(err) || return err istimezone(tz) || return argerror("TimeZone: \"$str\" is not a recognized time zone") diff --git a/test/TimeZones.jl b/test/TimeZones.jl index 600793525..0214c41c5 100644 --- a/test/TimeZones.jl +++ b/test/TimeZones.jl @@ -46,7 +46,7 @@ if lowercase(get(ENV, "CI", "false")) == "true" @test last(warsaw.transitions).utc_datetime == DateTime(2037, 10, 25, 1) @test get(warsaw.cutoff) == DateTime(2038, 3, 28, 1) - @test_throws TimeZones.UnhandledTimeError ZonedDateTime(DateTime(2039), warsaw) + @test_throws TimeZones.UnhandledTimeError Localized(DateTime(2039), warsaw) TimeZones.TZData.compile(max_year=2200) new_warsaw = TimeZone("Europe/Warsaw") @@ -54,9 +54,9 @@ if lowercase(get(ENV, "CI", "false")) == "true" @test warsaw !== new_warsaw @test last(new_warsaw.transitions).utc_datetime == DateTime(2200, 10, 26, 1) @test get(new_warsaw.cutoff) == DateTime(2201, 3, 29, 1) - ZonedDateTime(2100, new_warsaw) # Test this doesn't throw an exception + Localized(2100, new_warsaw) # Test this doesn't throw an exception - @test_throws TimeZones.UnhandledTimeError ZonedDateTime(2100, warsaw) + @test_throws TimeZones.UnhandledTimeError Localized(2100, warsaw) # Using the tz string macro which runs at parse time means that the resulting TimeZone diff --git a/test/accessors.jl b/test/accessors.jl index 7e2f289d6..96032d4e2 100644 --- a/test/accessors.jl +++ b/test/accessors.jl @@ -4,31 +4,31 @@ import Compat.Dates: Second warsaw = resolve("Europe/Warsaw", tzdata["europe"]...) fixed = FixedTimeZone("Fixed", -7200, 3600) -# ZonedDateTime accessors -zdt = ZonedDateTime(DateTime(2014,6,12,23,59,58,57), fixed) -@test TimeZones.localtime(zdt) == DateTime(2014,6,12,23,59,58,57) -@test TimeZones.utc(zdt) == DateTime(2014,6,13,0,59,58,57) - -@test TimeZones.days(zdt) == 735396 -@test TimeZones.hour(zdt) == 23 -@test TimeZones.minute(zdt) == 59 -@test TimeZones.second(zdt) == 58 -@test TimeZones.millisecond(zdt) == 57 - -# Make sure that Dates accessors work with ZonedDateTime. -@test Dates.year(zdt) == 2014 -@test Dates.month(zdt) == 6 -@test Dates.week(zdt) == 24 -@test Dates.day(zdt) == 12 -@test Dates.dayofmonth(zdt) == 12 -@test Dates.yearmonth(zdt) == (2014, 6) -@test Dates.monthday(zdt) == (6, 12) -@test Dates.yearmonthday(zdt) == (2014, 6, 12) +# Localized accessors +ldt = Localized(DateTime(2014,6,12,23,59,58,57), fixed) +@test TimeZones.localtime(ldt) == DateTime(2014,6,12,23,59,58,57) +@test TimeZones.utc(ldt) == DateTime(2014,6,13,0,59,58,57) + +@test TimeZones.days(ldt) == 735396 +@test TimeZones.hour(ldt) == 23 +@test TimeZones.minute(ldt) == 59 +@test TimeZones.second(ldt) == 58 +@test TimeZones.millisecond(ldt) == 57 + +# Make sure that Dates accessors work with Localized. +@test Dates.year(ldt) == 2014 +@test Dates.month(ldt) == 6 +@test Dates.week(ldt) == 24 +@test Dates.day(ldt) == 12 +@test Dates.dayofmonth(ldt) == 12 +@test Dates.yearmonth(ldt) == (2014, 6) +@test Dates.monthday(ldt) == (6, 12) +@test Dates.yearmonthday(ldt) == (2014, 6, 12) # Vectorized accessors # Note: fill is used to test for size and equality. n = 10 -arr = fill(zdt, n) +arr = fill(ldt, n) @test TimeZones.hour.(arr) == fill(23, n) @test TimeZones.minute.(arr) == fill(59, n) @test TimeZones.second.(arr) == fill(58, n) @@ -41,3 +41,15 @@ arr = fill(zdt, n) @test Dates.yearmonth.(arr) == fill((2014, 6), n) @test Dates.monthday.(arr) == fill((6, 12), n) @test Dates.yearmonthday.(arr) == fill((2014, 6, 12), n) + +ambiguous = Localized(DateTime(2015, 10, 25, 2), warsaw; strict=false) # Ambiguous hour in Warsaw +nonexistent = Localized(DateTime(2014, 3, 30, 2), warsaw; strict=false) # Non-existent hour in Warsaw + +@test TimeZones.isstrict(ldt) +@test !TimeZones.isstrict(TimeZones.relax(ldt)) +@test TimeZones.isambiguous(ambiguous) +@test TimeZones.isnonexistent(nonexistent) +@test !isvalid(ambiguous) +@test !isvalid(nonexistent) +@test isvalid(ldt) +@test isvalid(TimeZones.relax(ldt)) \ No newline at end of file diff --git a/test/adjusters.jl b/test/adjusters.jl index 7985d1827..9a6b757e2 100644 --- a/test/adjusters.jl +++ b/test/adjusters.jl @@ -2,38 +2,38 @@ using Compat.Dates # Basic truncation warsaw = resolve("Europe/Warsaw", tzdata["europe"]...) -zdt = ZonedDateTime(DateTime(2014,10,15,23,59,58,57), warsaw) +ldt = Localized(DateTime(2014,10,15,23,59,58,57), warsaw) -@test trunc(zdt, Year) == ZonedDateTime(DateTime(2014), warsaw) -@test trunc(zdt, Month) == ZonedDateTime(DateTime(2014,10), warsaw) -@test trunc(zdt, Day) == ZonedDateTime(DateTime(2014,10,15), warsaw) -@test trunc(zdt, Hour) == ZonedDateTime(DateTime(2014,10,15,23), warsaw) -@test trunc(zdt, Minute) == ZonedDateTime(DateTime(2014,10,15,23,59), warsaw) -@test trunc(zdt, Second) == ZonedDateTime(DateTime(2014,10,15,23,59,58), warsaw) -@test trunc(zdt, Millisecond) == zdt +@test trunc(ldt, Year) == Localized(DateTime(2014), warsaw) +@test trunc(ldt, Month) == Localized(DateTime(2014,10), warsaw) +@test trunc(ldt, Day) == Localized(DateTime(2014,10,15), warsaw) +@test trunc(ldt, Hour) == Localized(DateTime(2014,10,15,23), warsaw) +@test trunc(ldt, Minute) == Localized(DateTime(2014,10,15,23,59), warsaw) +@test trunc(ldt, Second) == Localized(DateTime(2014,10,15,23,59,58), warsaw) +@test trunc(ldt, Millisecond) == ldt # Ambiguous hour truncation dt = DateTime(2014,10,26,2) -@test ZonedDateTime(dt, warsaw, 1) != ZonedDateTime(dt, warsaw, 2) -@test trunc(ZonedDateTime(dt + Minute(59), warsaw, 1), Hour) == ZonedDateTime(dt, warsaw, 1) -@test trunc(ZonedDateTime(dt + Minute(59), warsaw, 2), Hour) == ZonedDateTime(dt, warsaw, 2) +@test Localized(dt, warsaw, 1) != Localized(dt, warsaw, 2) +@test trunc(Localized(dt + Minute(59), warsaw, 1), Hour) == Localized(dt, warsaw, 1) +@test trunc(Localized(dt + Minute(59), warsaw, 2), Hour) == Localized(dt, warsaw, 2) # Sub-hourly offsets (Issue #33) st_johns = resolve("America/St_Johns", tzdata["northamerica"]...) # UTC-3:30 or UTC-2:30 -zdt = ZonedDateTime(DateTime(2016,8,18,17,57,56,513), st_johns) -@test trunc(zdt, Hour) == ZonedDateTime(DateTime(2016,8,18,17), st_johns) +ldt = Localized(DateTime(2016,8,18,17,57,56,513), st_johns) +@test trunc(ldt, Hour) == Localized(DateTime(2016,8,18,17), st_johns) # Adjuster functions -zdt = ZonedDateTime(DateTime(2013,9,9), warsaw) # Monday - -@test TimeZones.firstdayofweek(zdt) == ZonedDateTime(DateTime(2013,9,9), warsaw) -@test TimeZones.lastdayofweek(zdt) == ZonedDateTime(DateTime(2013,9,15), warsaw) -@test TimeZones.firstdayofmonth(zdt) == ZonedDateTime(DateTime(2013,9,1), warsaw) -@test TimeZones.lastdayofmonth(zdt) == ZonedDateTime(DateTime(2013,9,30), warsaw) -@test TimeZones.firstdayofyear(zdt) == ZonedDateTime(DateTime(2013,1,1), warsaw) -@test TimeZones.lastdayofyear(zdt) == ZonedDateTime(DateTime(2013,12,31), warsaw) -@test TimeZones.firstdayofquarter(zdt) == ZonedDateTime(DateTime(2013,7,1), warsaw) -@test TimeZones.lastdayofquarter(zdt) == ZonedDateTime(DateTime(2013,9,30), warsaw) +ldt = Localized(DateTime(2013,9,9), warsaw) # Monday + +@test TimeZones.firstdayofweek(ldt) == Localized(DateTime(2013,9,9), warsaw) +@test TimeZones.lastdayofweek(ldt) == Localized(DateTime(2013,9,15), warsaw) +@test TimeZones.firstdayofmonth(ldt) == Localized(DateTime(2013,9,1), warsaw) +@test TimeZones.lastdayofmonth(ldt) == Localized(DateTime(2013,9,30), warsaw) +@test TimeZones.firstdayofyear(ldt) == Localized(DateTime(2013,1,1), warsaw) +@test TimeZones.lastdayofyear(ldt) == Localized(DateTime(2013,12,31), warsaw) +@test TimeZones.firstdayofquarter(ldt) == Localized(DateTime(2013,7,1), warsaw) +@test TimeZones.lastdayofquarter(ldt) == Localized(DateTime(2013,9,30), warsaw) # TODO: Should be in Dates. diff --git a/test/arithmetic.jl b/test/arithmetic.jl index e4ca4752c..33997f8df 100644 --- a/test/arithmetic.jl +++ b/test/arithmetic.jl @@ -7,54 +7,54 @@ spring = DateTime(2015, 3, 29, 0) # a 23 hour day in warsaw fall = DateTime(2015, 10, 25, 0) # a 25 hour day in warsaw # Unary plus -@test +ZonedDateTime(normal, warsaw) == ZonedDateTime(normal, warsaw) +@test +Localized(normal, warsaw) == Localized(normal, warsaw) # Period arithmetic -@test ZonedDateTime(normal, warsaw) + Day(1) == ZonedDateTime(normal + Day(1), warsaw) -@test ZonedDateTime(spring, warsaw) + Day(1) == ZonedDateTime(spring + Day(1), warsaw) -@test ZonedDateTime(fall, warsaw) + Day(1) == ZonedDateTime(fall + Day(1), warsaw) +@test Localized(normal, warsaw) + Day(1) == Localized(normal + Day(1), warsaw) +@test Localized(spring, warsaw) + Day(1) == Localized(spring + Day(1), warsaw) +@test Localized(fall, warsaw) + Day(1) == Localized(fall + Day(1), warsaw) -@test ZonedDateTime(normal, warsaw) + Hour(24) == ZonedDateTime(normal + Hour(24), warsaw) -@test ZonedDateTime(spring, warsaw) + Hour(24) == ZonedDateTime(spring + Hour(25), warsaw) -@test ZonedDateTime(fall, warsaw) + Hour(24) == ZonedDateTime(fall + Hour(23), warsaw) +@test Localized(normal, warsaw) + Hour(24) == Localized(normal + Hour(24), warsaw) +@test Localized(spring, warsaw) + Hour(24) == Localized(spring + Hour(25), warsaw) +@test Localized(fall, warsaw) + Hour(24) == Localized(fall + Hour(23), warsaw) # Do the same calculations but backwards over the transitions. -@test ZonedDateTime(normal + Day(1), warsaw) - Day(1) == ZonedDateTime(normal, warsaw) -@test ZonedDateTime(spring + Day(1), warsaw) - Day(1) == ZonedDateTime(spring, warsaw) -@test ZonedDateTime(fall + Day(1), warsaw) - Day(1) == ZonedDateTime(fall, warsaw) +@test Localized(normal + Day(1), warsaw) - Day(1) == Localized(normal, warsaw) +@test Localized(spring + Day(1), warsaw) - Day(1) == Localized(spring, warsaw) +@test Localized(fall + Day(1), warsaw) - Day(1) == Localized(fall, warsaw) -@test ZonedDateTime(normal + Day(1), warsaw) - Hour(24) == ZonedDateTime(normal, warsaw) -@test ZonedDateTime(spring + Day(1), warsaw) - Hour(23) == ZonedDateTime(spring, warsaw) -@test ZonedDateTime(fall + Day(1), warsaw) - Hour(25) == ZonedDateTime(fall, warsaw) +@test Localized(normal + Day(1), warsaw) - Hour(24) == Localized(normal, warsaw) +@test Localized(spring + Day(1), warsaw) - Hour(23) == Localized(spring, warsaw) +@test Localized(fall + Day(1), warsaw) - Hour(25) == Localized(fall, warsaw) # Ensure that arithmetic around transitions works. -@test ZonedDateTime(spring, warsaw) + Hour(1) == ZonedDateTime(spring + Hour(1), warsaw) -@test ZonedDateTime(spring, warsaw) + Hour(2) == ZonedDateTime(spring + Hour(3), warsaw) -@test ZonedDateTime(fall, warsaw) + Hour(2) == ZonedDateTime(fall + Hour(2), warsaw, 1) -@test ZonedDateTime(fall, warsaw) + Hour(3) == ZonedDateTime(fall + Hour(2), warsaw, 2) +@test Localized(spring, warsaw) + Hour(1) == Localized(spring + Hour(1), warsaw) +@test Localized(spring, warsaw) + Hour(2) == Localized(spring + Hour(3), warsaw) +@test Localized(fall, warsaw) + Hour(2) == Localized(fall + Hour(2), warsaw, 1) +@test Localized(fall, warsaw) + Hour(3) == Localized(fall + Hour(2), warsaw, 2) # Non-Associativity -explicit_hour_day = (ZonedDateTime(spring, warsaw) + Hour(24)) + Day(1) -explicit_day_hour = (ZonedDateTime(spring, warsaw) + Day(1)) + Hour(24) -implicit_hour_day = ZonedDateTime(spring, warsaw) + Hour(24) + Day(1) -implicit_day_hour = ZonedDateTime(spring, warsaw) + Day(1) + Hour(24) +explicit_hour_day = (Localized(spring, warsaw) + Hour(24)) + Day(1) +explicit_day_hour = (Localized(spring, warsaw) + Day(1)) + Hour(24) +implicit_hour_day = Localized(spring, warsaw) + Hour(24) + Day(1) +implicit_day_hour = Localized(spring, warsaw) + Day(1) + Hour(24) -@test explicit_hour_day == ZonedDateTime(2015, 3, 31, 1, warsaw) -@test explicit_day_hour == ZonedDateTime(2015, 3, 31, 0, warsaw) -@test implicit_hour_day == ZonedDateTime(2015, 3, 31, 0, warsaw) -@test implicit_day_hour == ZonedDateTime(2015, 3, 31, 0, warsaw) +@test explicit_hour_day == Localized(2015, 3, 31, 1, warsaw) +@test explicit_day_hour == Localized(2015, 3, 31, 0, warsaw) +@test implicit_hour_day == Localized(2015, 3, 31, 0, warsaw) +@test implicit_day_hour == Localized(2015, 3, 31, 0, warsaw) -# CompoundPeriod canonicalization interacting with period arithmetic. Since `spring_zdt` is +# CompoundPeriod canonicalization interacting with period arithmetic. Since `spring_loc` is # a 23 hour day this means adding `Day(1)` and `Hour(23)` are equivalent. -spring_zdt = ZonedDateTime(spring, warsaw) -@test spring_zdt + Day(1) + Minute(1) == spring_zdt + Hour(23) + Minute(1) +spring_loc = Localized(spring, warsaw) +@test spring_loc + Day(1) + Minute(1) == spring_loc + Hour(23) + Minute(1) # When canonicalization happens automatically `Hour(24) + Minute(1)` is converted into # `Day(1) + Minute(1)`. Fixed in `JuliaLang/julia#19268` if VERSION >= v"0.6.0-dev.1874" - @test spring_zdt + Hour(23) + Minute(1) < spring_zdt + Hour(24) + Minute(1) + @test spring_loc + Hour(23) + Minute(1) < spring_loc + Hour(24) + Minute(1) else - @test spring_zdt + Hour(23) + Minute(1) == spring_zdt + Hour(24) + Minute(1) + @test spring_loc + Hour(23) + Minute(1) == spring_loc + Hour(24) + Minute(1) end # Arithmetic with a StepRange should always work even when the start/stop lands on @@ -62,14 +62,14 @@ end ambiguous = DateTime(2015, 10, 25, 2) # Ambiguous hour in Warsaw nonexistent = DateTime(2014, 3, 30, 2) # Non-existent hour in Warsaw -range = ZonedDateTime(ambiguous - Day(1), warsaw):Hour(1):ZonedDateTime(ambiguous - Day(1) + Hour(1), warsaw) -@test range .+ Day(1) == ZonedDateTime(ambiguous, warsaw, 1):Hour(1):ZonedDateTime(ambiguous + Hour(1), warsaw) +range = Localized(ambiguous - Day(1), warsaw):Hour(1):Localized(ambiguous - Day(1) + Hour(1), warsaw) +@test range .+ Day(1) == Localized(ambiguous, warsaw, 1):Hour(1):Localized(ambiguous + Hour(1), warsaw) -range = ZonedDateTime(ambiguous - Day(1) - Hour(1), warsaw):Hour(1):ZonedDateTime(ambiguous - Day(1), warsaw) -@test range .+ Day(1) == ZonedDateTime(ambiguous - Hour(1), warsaw, 1):Hour(1):ZonedDateTime(ambiguous, warsaw, 2) +range = Localized(ambiguous - Day(1) - Hour(1), warsaw):Hour(1):Localized(ambiguous - Day(1), warsaw) +@test range .+ Day(1) == Localized(ambiguous - Hour(1), warsaw, 1):Hour(1):Localized(ambiguous, warsaw, 2) -range = ZonedDateTime(nonexistent - Day(1), warsaw):Hour(1):ZonedDateTime(nonexistent - Day(1) + Hour(1), warsaw) -@test range .+ Day(1) == ZonedDateTime(nonexistent + Hour(1), warsaw):Hour(1):ZonedDateTime(nonexistent + Hour(1), warsaw) +range = Localized(nonexistent - Day(1), warsaw):Hour(1):Localized(nonexistent - Day(1) + Hour(1), warsaw) +@test range .+ Day(1) == Localized(nonexistent + Hour(1), warsaw):Hour(1):Localized(nonexistent + Hour(1), warsaw) -range = ZonedDateTime(nonexistent - Day(1) - Hour(1), warsaw):Hour(1):ZonedDateTime(nonexistent - Day(1), warsaw) -@test range .+ Day(1) == ZonedDateTime(nonexistent - Hour(1), warsaw):Hour(1):ZonedDateTime(nonexistent - Hour(1), warsaw) +range = Localized(nonexistent - Day(1) - Hour(1), warsaw):Hour(1):Localized(nonexistent - Day(1), warsaw) +@test range .+ Day(1) == Localized(nonexistent - Hour(1), warsaw):Hour(1):Localized(nonexistent - Hour(1), warsaw) diff --git a/test/conversions.jl b/test/conversions.jl index f8136c2bf..9e1220d43 100644 --- a/test/conversions.jl +++ b/test/conversions.jl @@ -6,24 +6,24 @@ warsaw = resolve("Europe/Warsaw", tzdata["europe"]...) apia = resolve("Pacific/Apia", tzdata["australasia"]...) midway = resolve("Pacific/Midway", tzdata["australasia"]...) -# Converting a ZonedDateTime into a DateTime +# Converting a Localized into a DateTime dt = DateTime(2015, 1, 1, 0) -zdt = ZonedDateTime(dt, warsaw) -@test DateTime(zdt) == dt +ldt = Localized(dt, warsaw) +@test DateTime(ldt) == dt -# Converting from ZonedDateTime to DateTime isn't possible as it is always inexact. -@test_throws MethodError convert(DateTime, zdt) +# Converting from Localized to DateTime isn't possible as it is always inexact. +@test_throws MethodError convert(DateTime, ldt) # Vectorized accessors n = 10 -arr = fill(zdt, n) +arr = fill(ldt, n) @test Dates.DateTime.(arr) == fill(dt, n) # now function dt = now(Dates.UTC)::DateTime -zdt = now(warsaw) -@test zdt.timezone == warsaw -@test Dates.datetime2unix(TimeZones.utc(zdt)) ≈ Dates.datetime2unix(dt) +ldt = now(warsaw) +@test ldt.timezone == warsaw +@test Dates.datetime2unix(TimeZones.utc(ldt)) ≈ Dates.datetime2unix(dt) # today function @test abs(today() - today(warsaw)) <= Dates.Day(1) @@ -31,9 +31,9 @@ zdt = now(warsaw) @testset "todayat" begin @testset "current time" begin - local zdt = now(warsaw) - now_time = Time(TimeZones.localtime(zdt)) - @test todayat(now_time, warsaw) == zdt + local ldt = now(warsaw) + now_time = Time(TimeZones.localtime(ldt)) + @test todayat(now_time, warsaw) == ldt end @testset "ambiguous" begin @@ -41,8 +41,8 @@ zdt = now(warsaw) local patch = @patch today(tz::TimeZone) = Date(1916, 10, 1) apply(patch) do @test_throws AmbiguousTimeError todayat(Time(0), warsaw) - @test todayat(Time(0), warsaw, 1) == ZonedDateTime(1916, 10, 1, 0, warsaw, 1) - @test todayat(Time(0), warsaw, 2) == ZonedDateTime(1916, 10, 1, 0, warsaw, 2) + @test todayat(Time(0), warsaw, 1) == Localized(1916, 10, 1, 0, warsaw, 1) + @test todayat(Time(0), warsaw, 2) == Localized(1916, 10, 1, 0, warsaw, 2) end end end @@ -50,55 +50,64 @@ end # Changing time zones dt = DateTime(2015, 1, 1, 0) -zdt_utc = ZonedDateTime(dt, utc; from_utc=true) -zdt_warsaw = ZonedDateTime(dt, warsaw; from_utc=true) +loc_utc = Localized(dt, utc; from_utc=true) +loc_warsaw = Localized(dt, warsaw; from_utc=true) -# Identical since ZonedDateTime is immutable -@test astimezone(zdt_utc, warsaw) === zdt_warsaw -@test astimezone(zdt_warsaw, utc) === zdt_utc +# Identical since Localized is immutable +@test astimezone(loc_utc, warsaw) === loc_warsaw +@test astimezone(loc_warsaw, utc) === loc_utc -# ZonedDateTime to Unix timestamp (and vice versa) -@test TimeZones.zdt2unix(ZonedDateTime(1970, utc)) == 0 -@test TimeZones.unix2zdt(0) == ZonedDateTime(1970, utc) +# Localized to Unix timestamp (and vice versa) +@test TimeZones.localized2unix(Localized(1970, utc)) == 0 +@test TimeZones.unix2localized(0) == Localized(1970, utc) for dt in (DateTime(2013, 2, 13), DateTime(2016, 8, 11)) local dt - local zdt = ZonedDateTime(dt, warsaw) - offset = TimeZones.value(zdt.zone.offset) # Total offset in seconds - @test TimeZones.zdt2unix(zdt) == datetime2unix(dt) - offset + local ldt = Localized(dt, warsaw) + offset = TimeZones.value(ldt.zone.offset) # Total offset in seconds + @test TimeZones.localized2unix(ldt) == datetime2unix(dt) - offset end -@test isa(TimeZones.zdt2unix(ZonedDateTime(1970, utc)), Float64) -@test isa(TimeZones.zdt2unix(Float32, ZonedDateTime(1970, utc)), Float32) -@test isa(TimeZones.zdt2unix(Int64, ZonedDateTime(1970, utc)), Int64) -@test isa(TimeZones.zdt2unix(Int32, ZonedDateTime(1970, utc)), Int32) +@test isa(TimeZones.localized2unix(Localized(1970, utc)), Float64) +@test isa(TimeZones.localized2unix(Float32, Localized(1970, utc)), Float32) +@test isa(TimeZones.localized2unix(Int64, Localized(1970, utc)), Int64) +@test isa(TimeZones.localized2unix(Int32, Localized(1970, utc)), Int32) -@test TimeZones.zdt2unix(ZonedDateTime(1970, 1, 1, 0, 0, 0, 750, utc)) == 0.75 -@test TimeZones.zdt2unix(Float32, ZonedDateTime(1970, 1, 1, 0, 0, 0, 750, utc)) == 0.75 -@test TimeZones.zdt2unix(Int64, ZonedDateTime(1970, 1, 1, 0, 0, 0, 750, utc)) == 0 -@test TimeZones.zdt2unix(Int32, ZonedDateTime(1970, 1, 1, 0, 0, 0, 750, utc)) == 0 +@test TimeZones.localized2unix(Localized(1970, 1, 1, 0, 0, 0, 750, utc)) == 0.75 +@test TimeZones.localized2unix(Float32, Localized(1970, 1, 1, 0, 0, 0, 750, utc)) == 0.75 +@test TimeZones.localized2unix(Int64, Localized(1970, 1, 1, 0, 0, 0, 750, utc)) == 0 +@test TimeZones.localized2unix(Int32, Localized(1970, 1, 1, 0, 0, 0, 750, utc)) == 0 # round-trip -zdt = ZonedDateTime(2010, 1, 2, 3, 4, 5, 999, utc) -round_trip = TimeZones.unix2zdt(TimeZones.zdt2unix(zdt)) -@test round_trip == zdt +ldt = Localized(2010, 1, 2, 3, 4, 5, 999, utc) +round_trip = TimeZones.unix2localized(TimeZones.localized2unix(ldt)) +@test round_trip == ldt # millisecond loss -zdt = ZonedDateTime(2010, 1, 2, 3, 4, 5, 999, utc) -round_trip = TimeZones.unix2zdt(TimeZones.zdt2unix(Int64, zdt)) -@test round_trip != zdt -@test round_trip == floor(zdt, Dates.Second(1)) +ldt = Localized(2010, 1, 2, 3, 4, 5, 999, utc) +round_trip = TimeZones.unix2localized(TimeZones.localized2unix(Int64, ldt)) +@test round_trip != ldt +@test round_trip == floor(ldt, Dates.Second(1)) # timezone loss -zdt = ZonedDateTime(2010, 1, 2, 3, 4, 5, warsaw) -round_trip = TimeZones.unix2zdt(TimeZones.zdt2unix(Int64, zdt)) -@test round_trip == zdt -@test !isequal(round_trip, zdt) +ldt = Localized(2010, 1, 2, 3, 4, 5, warsaw) +round_trip = TimeZones.unix2localized(TimeZones.localized2unix(Int64, ldt)) +@test round_trip == ldt +@test !isequal(round_trip, ldt) + +# restrict vs relax +relaxed_ldt = TimeZones.relax(ldt) +ambiguous = Localized(DateTime(2015, 10, 25, 2), warsaw; strict=false) # Ambiguous hour in Warsaw +nonexistent = Localized(DateTime(2014, 3, 30, 2), warsaw; strict=false) # Non-existent hour in Warsaw + +@test !TimeZones.isstrict(relaxed_ldt) +@test_throws AmbiguousTimeError TimeZones.restrict(ambiguous) +@test_throws NonExistentTimeError TimeZones.restrict(nonexistent) # Julian dates jd = 2457241.855 -jd_zdt = ZonedDateTime(Dates.julian2datetime(jd), warsaw, from_utc=true) -@test TimeZones.zdt2julian(jd_zdt) == jd -@test TimeZones.zdt2julian(Int, jd_zdt) === floor(Int, jd) -@test TimeZones.zdt2julian(Float64, jd_zdt) === jd -@test TimeZones.julian2zdt(jd) == jd_zdt +jd_loc = Localized(Dates.julian2datetime(jd), warsaw, from_utc=true) +@test TimeZones.localized2julian(jd_loc) == jd +@test TimeZones.localized2julian(Int, jd_loc) === floor(Int, jd) +@test TimeZones.localized2julian(Float64, jd_loc) === jd +@test TimeZones.julian2localized(jd) == jd_loc diff --git a/test/discovery.jl b/test/discovery.jl index b2c3a628a..213cfe1da 100644 --- a/test/discovery.jl +++ b/test/discovery.jl @@ -37,9 +37,9 @@ paris = resolve("Europe/Paris", tzdata["europe"]...) @testset "non-existent" begin local zone = FixedTimeZone("CST", -6 * 3600) - instant = next_transition_instant(ZonedDateTime(2018, 1, 1, wpg)) - expected_instant = ZonedDateTime(DateTime(2018, 3, 11, 8), wpg, zone) - expected_valid = ZonedDateTime(2018, 3, 11, 3, wpg) + instant = next_transition_instant(Localized(2018, 1, 1, wpg)) + expected_instant = Localized{DateTime, true}(DateTime(2018, 3, 11, 8), wpg, zone) + expected_valid = Localized(2018, 3, 11, 3, wpg) @test isequal(instant, expected_instant) @test instant == expected_valid @@ -50,9 +50,9 @@ paris = resolve("Europe/Paris", tzdata["europe"]...) @testset "ambiguous" begin local zone = FixedTimeZone("CDT", -6 * 3600, 3600) - instant = next_transition_instant(ZonedDateTime(2018, 6, 1, wpg)) - expected_instant = ZonedDateTime(DateTime(2018, 11, 4, 7), wpg, zone) - expected_valid = ZonedDateTime(2018, 11, 4, 1, wpg, 2) + instant = next_transition_instant(Localized(2018, 6, 1, wpg)) + expected_instant = Localized{DateTime, true}(DateTime(2018, 11, 4, 7), wpg, zone) + expected_valid = Localized(2018, 11, 4, 1, wpg, 2) @test isequal(instant, expected_instant) @test instant == expected_valid @@ -62,9 +62,9 @@ paris = resolve("Europe/Paris", tzdata["europe"]...) @testset "upcoming" begin if !compiled_modules_enabled - local patch = @patch now(tz::TimeZone) = ZonedDateTime(2000, 1, 1, tz) + local patch = @patch now(tz::TimeZone) = Localized(2000, 1, 1, tz) apply(patch) do - @test next_transition_instant(wpg) == ZonedDateTime(2000, 4, 2, 3, wpg) + @test next_transition_instant(wpg) == Localized(2000, 4, 2, 3, wpg) end end end @@ -72,7 +72,7 @@ end @testset "show_next_transition" begin @testset "non-existent" begin - @test sprint(show_next_transition, ZonedDateTime(2018, 1, 1, wpg)) == + @test sprint(show_next_transition, Localized(2018, 1, 1, wpg)) == """ Transition Date: 2018-03-11 Local Time Change: 02:00 → 03:00 (Forward) @@ -83,7 +83,7 @@ end end @testset "ambiguous" begin - @test sprint(show_next_transition, ZonedDateTime(2018, 6, 1, wpg)) == + @test sprint(show_next_transition, Localized(2018, 6, 1, wpg)) == """ Transition Date: 2018-11-04 Local Time Change: 02:00 → 01:00 (Backward) @@ -94,7 +94,7 @@ end end @testset "standard offset change" begin - @test sprint(show_next_transition, ZonedDateTime(2011, 12, 1, apia)) == + @test sprint(show_next_transition, Localized(2011, 12, 1, apia)) == """ Transition Date: 2011-12-30 Local Time Change: 00:00 → 00:00 (Forward) @@ -105,7 +105,7 @@ end end @testset "dst offset change" begin - @test sprint(show_next_transition, ZonedDateTime(1945, 4, 1, paris)) == + @test sprint(show_next_transition, Localized(1945, 4, 1, paris)) == """ Transition Date: 1945-04-02 Local Time Change: 02:00 → 03:00 (Forward) @@ -117,7 +117,7 @@ end @testset "upcoming" begin if !compiled_modules_enabled - local patch = @patch now(tz::TimeZone) = ZonedDateTime(2000, 1, 1, tz) + local patch = @patch now(tz::TimeZone) = Localized(2000, 1, 1, tz) apply(patch) do @test occursin("2000-04-02", sprint(show_next_transition, wpg)) end diff --git a/test/interpret.jl b/test/interpret.jl index ac03277f2..435bbc911 100644 --- a/test/interpret.jl +++ b/test/interpret.jl @@ -6,28 +6,28 @@ non_existent_pos = DateTime(2011,9,24,3) ambiguous_neg = DateTime(2012,4,1,3) non_existent_neg = DateTime(2012,9,30,3) -@test_throws AmbiguousTimeError ZonedDateTime(ambiguous_pos, apia) -@test_throws NonExistentTimeError ZonedDateTime(non_existent_pos, apia) -@test_throws AmbiguousTimeError ZonedDateTime(ambiguous_neg, apia) -@test_throws NonExistentTimeError ZonedDateTime(non_existent_neg, apia) +@test_throws AmbiguousTimeError Localized(ambiguous_pos, apia) +@test_throws NonExistentTimeError Localized(non_existent_pos, apia) +@test_throws AmbiguousTimeError Localized(ambiguous_neg, apia) +@test_throws NonExistentTimeError Localized(non_existent_neg, apia) -@test TimeZones.shift_gap(ambiguous_pos, apia) == ZonedDateTime[] +@test TimeZones.shift_gap(ambiguous_pos, apia) == Localized[] @test TimeZones.shift_gap(non_existent_pos, apia) == [ - ZonedDateTime(2011, 9, 24, 2, 59, 59, 999, apia), - ZonedDateTime(2011, 9, 24, 4, apia), + Localized(2011, 9, 24, 2, 59, 59, 999, apia), + Localized(2011, 9, 24, 4, apia), ] -@test TimeZones.shift_gap(ambiguous_neg, apia) == ZonedDateTime[] +@test TimeZones.shift_gap(ambiguous_neg, apia) == Localized[] @test TimeZones.shift_gap(non_existent_neg, apia) == [ - ZonedDateTime(2012, 9, 30, 2, 59, 59, 999, apia), - ZonedDateTime(2012, 9, 30, 4, apia), + Localized(2012, 9, 30, 2, 59, 59, 999, apia), + Localized(2012, 9, 30, 4, apia), ] # Valid local datetimes close to the non-existent hour should have no boundaries as are # already valid. -@test TimeZones.shift_gap(non_existent_pos - Second(1), apia) == ZonedDateTime[] -@test TimeZones.shift_gap(non_existent_pos + Hour(1), apia) == ZonedDateTime[] -@test TimeZones.shift_gap(non_existent_neg - Second(1), apia) == ZonedDateTime[] -@test TimeZones.shift_gap(non_existent_neg + Hour(1), apia) == ZonedDateTime[] +@test TimeZones.shift_gap(non_existent_pos - Second(1), apia) == Localized[] +@test TimeZones.shift_gap(non_existent_pos + Hour(1), apia) == Localized[] +@test TimeZones.shift_gap(non_existent_neg - Second(1), apia) == Localized[] +@test TimeZones.shift_gap(non_existent_neg + Hour(1), apia) == Localized[] # Create custom VariableTimeZones to test corner cases @@ -58,12 +58,12 @@ non_existent_2 = DateTime(1935,4,1,3) for tz in (long, hidden) local tz boundaries = [ - ZonedDateTime(1935, 4, 1, 1, 59, 59, 999, tz), - ZonedDateTime(1935, 4, 1, 4, tz), + Localized(1935, 4, 1, 1, 59, 59, 999, tz), + Localized(1935, 4, 1, 4, tz), ] - @test_throws NonExistentTimeError ZonedDateTime(non_existent_1, tz) - @test_throws NonExistentTimeError ZonedDateTime(non_existent_2, tz) + @test_throws NonExistentTimeError Localized(non_existent_1, tz) + @test_throws NonExistentTimeError Localized(non_existent_2, tz) @test TimeZones.shift_gap(non_existent_1, tz) == boundaries @test TimeZones.shift_gap(non_existent_2, tz) == boundaries @@ -76,21 +76,21 @@ ambiguous = ambiguous_pos non_existent = non_existent_pos # first_valid/last_valid with a step -@test TimeZones.first_valid(valid, apia, Hour(1)) == ZonedDateTime(valid, apia) -@test TimeZones.last_valid(valid, apia, Hour(1)) == ZonedDateTime(valid, apia) +@test TimeZones.first_valid(valid, apia, Hour(1)) == Localized(valid, apia) +@test TimeZones.last_valid(valid, apia, Hour(1)) == Localized(valid, apia) -@test TimeZones.first_valid(non_existent, apia, Hour(1)) == ZonedDateTime(2011,9,24,4,apia) -@test TimeZones.last_valid(non_existent, apia, Hour(1)) == ZonedDateTime(2011,9,24,2,apia) +@test TimeZones.first_valid(non_existent, apia, Hour(1)) == Localized(2011,9,24,4,apia) +@test TimeZones.last_valid(non_existent, apia, Hour(1)) == Localized(2011,9,24,2,apia) -@test TimeZones.first_valid(ambiguous, apia, Hour(1)) == ZonedDateTime(ambiguous,apia,1) -@test TimeZones.last_valid(ambiguous, apia, Hour(1)) == ZonedDateTime(ambiguous,apia,2) +@test TimeZones.first_valid(ambiguous, apia, Hour(1)) == Localized(ambiguous,apia,1) +@test TimeZones.last_valid(ambiguous, apia, Hour(1)) == Localized(ambiguous,apia,2) # first_valid/last_valid with no step -@test TimeZones.first_valid(valid, apia) == ZonedDateTime(valid, apia) -@test TimeZones.last_valid(valid, apia) == ZonedDateTime(valid, apia) +@test TimeZones.first_valid(valid, apia) == Localized(valid, apia) +@test TimeZones.last_valid(valid, apia) == Localized(valid, apia) -@test TimeZones.first_valid(non_existent, apia) == ZonedDateTime(2011,9,24,4,apia) -@test TimeZones.last_valid(non_existent, apia) == ZonedDateTime(2011,9,24,2,59,59,999,apia) +@test TimeZones.first_valid(non_existent, apia) == Localized(2011,9,24,4,apia) +@test TimeZones.last_valid(non_existent, apia) == Localized(2011,9,24,2,59,59,999,apia) -@test TimeZones.first_valid(ambiguous, apia) == ZonedDateTime(ambiguous,apia,1) -@test TimeZones.last_valid(ambiguous, apia) == ZonedDateTime(ambiguous,apia,2) +@test TimeZones.first_valid(ambiguous, apia) == Localized(ambiguous,apia,1) +@test TimeZones.last_valid(ambiguous, apia) == Localized(ambiguous,apia,2) diff --git a/test/io.jl b/test/io.jl index 8297eccfb..e0134c726 100644 --- a/test/io.jl +++ b/test/io.jl @@ -43,10 +43,10 @@ show_compact = (io, args...) -> show(IOContext(io, :compact => true), args...) @test sprint(show, FixedTimeZone("GMT", 0)) == "GMT" @test sprint(show, FixedTimeZone("FOO", 0)) == "FOO (UTC+0)" -# ZonedDateTime as a string -zdt = ZonedDateTime(dt, warsaw) -@test string(zdt) == "1942-12-25T01:23:45+01:00" -@test sprint(show, zdt) == "1942-12-25T01:23:45+01:00" +# Localized as a string +ldt = Localized(dt, warsaw) +@test string(ldt) == "1942-12-25T01:23:45+01:00" +@test sprint(show, ldt) == "1942-12-25T01:23:45+01:00" # TimeZone parsing @@ -68,40 +68,40 @@ df = Dates.DateFormat("Z") @test parse_components("UTC", df) == Any[FixedTimeZone("UTC")] @test parse_components("Europe/Warsaw", df) == Any[warsaw] -# ZonedDateTime parsing. +# Localized parsing. # Note: uses compiled time zone information. If these tests are failing try to rebuild # the TimeZones package. -@test ZonedDateTime("1942-12-25T01:23:45.000+01:00") == ZonedDateTime(dt, fixed) -@test ZonedDateTime("1942-12-25T01:23:45+0100", "yyyy-mm-ddTHH:MM:SSzzz") == ZonedDateTime(dt, fixed) -@test ZonedDateTime("1942-12-25T01:23:45 Europe/Warsaw", "yyyy-mm-ddTHH:MM:SS ZZZ") == ZonedDateTime(dt, warsaw) -@test ZonedDateTime("1942-12-25T01:23:45 America/New_York", "yyyy-mm-ddTHH:MM:SS ZZZ") == ZonedDateTime(dt, new_york) +@test Localized("1942-12-25T01:23:45.000+01:00") == Localized(dt, fixed) +@test Localized("1942-12-25T01:23:45+0100", "yyyy-mm-ddTHH:MM:SSzzz") == Localized(dt, fixed) +@test Localized("1942-12-25T01:23:45 Europe/Warsaw", "yyyy-mm-ddTHH:MM:SS ZZZ") == Localized(dt, warsaw) +@test Localized("1942-12-25T01:23:45 America/New_York", "yyyy-mm-ddTHH:MM:SS ZZZ") == Localized(dt, new_york) x = "1942-12-25T01:23:45.123+01:00" -@test string(ZonedDateTime(x)) == x +@test string(Localized(x)) == x # Note: CET here represents the FixedTimeZone used in Europe/Warsaw and not the # VariableTimeZone CET. -@test_throws ArgumentError ZonedDateTime("1942-12-25T01:23:45 CET", "yyyy-mm-ddTHH:MM:SS ZZZ") +@test_throws ArgumentError Localized("1942-12-25T01:23:45 CET", "yyyy-mm-ddTHH:MM:SS ZZZ") -# Creating a ZonedDateTime requires a TimeZone to be present. -@test_throws ArgumentError ZonedDateTime("1942-12-25T01:23:45", "yyyy-mm-ddTHH:MM:SSzzz") +# Creating a Localized requires a TimeZone to be present. +@test_throws ArgumentError Localized("1942-12-25T01:23:45", "yyyy-mm-ddTHH:MM:SSzzz") -# ZonedDateTime formatting +# Localized formatting f = "yyyy/m/d H:M:S ZZZ" -@test Dates.format(ZonedDateTime(dt, fixed), f) == "1942/12/25 1:23:45 UTC+01:00" -@test Dates.format(ZonedDateTime(dt, warsaw), f) == "1942/12/25 1:23:45 CET" -@test Dates.format(ZonedDateTime(dt, ulyanovsk), f) == "1942/12/25 1:23:45 UTC+04:00" +@test Dates.format(Localized(dt, fixed), f) == "1942/12/25 1:23:45 UTC+01:00" +@test Dates.format(Localized(dt, warsaw), f) == "1942/12/25 1:23:45 CET" +@test Dates.format(Localized(dt, ulyanovsk), f) == "1942/12/25 1:23:45 UTC+04:00" f = "yyyy/m/d H:M:S zzz" -@test Dates.format(ZonedDateTime(dt, fixed), f) == "1942/12/25 1:23:45 +01:00" -@test Dates.format(ZonedDateTime(dt, warsaw), f) == "1942/12/25 1:23:45 +01:00" -@test Dates.format(ZonedDateTime(dt, ulyanovsk), f) == "1942/12/25 1:23:45 +04:00" +@test Dates.format(Localized(dt, fixed), f) == "1942/12/25 1:23:45 +01:00" +@test Dates.format(Localized(dt, warsaw), f) == "1942/12/25 1:23:45 +01:00" +@test Dates.format(Localized(dt, ulyanovsk), f) == "1942/12/25 1:23:45 +04:00" # The "Z" slot displays the time zone abbreviation for VariableTimeZones. It is fine to use # the abbreviation for display purposes but not fine for parsing. This means that we # currently cannot parse all strings produced by format. df = Dates.DateFormat("yyyy-mm-ddTHH:MM:SS ZZZ") -zdt = ZonedDateTime(dt, warsaw) -@test_throws ArgumentError parse(ZonedDateTime, Dates.format(zdt, df), df) +ldt = Localized(dt, warsaw) +@test_throws ArgumentError parse(Localized, Dates.format(ldt, df), df) diff --git a/test/parse.jl b/test/parse.jl index 1a1e145d5..6ff2aecf2 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -2,35 +2,35 @@ import Compat.Dates: parse_components, default_format @testset "parse" begin @test isequal( - parse(ZonedDateTime, "2017-11-14 11:03:53 +0100", dateformat"yyyy-mm-dd HH:MM:SS zzzzz"), - ZonedDateTime(2017, 11, 14, 11, 3, 53, tz"UTC+01"), + parse(Localized, "2017-11-14 11:03:53 +0100", dateformat"yyyy-mm-dd HH:MM:SS zzzzz"), + Localized(2017, 11, 14, 11, 3, 53, tz"UTC+01"), ) @test isequal( - parse(ZonedDateTime, "2016-04-11 08:00 UTC", dateformat"yyyy-mm-dd HH:MM ZZZ"), - ZonedDateTime(2016, 4, 11, 8, tz"UTC"), + parse(Localized, "2016-04-11 08:00 UTC", dateformat"yyyy-mm-dd HH:MM ZZZ"), + Localized(2016, 4, 11, 8, tz"UTC"), ) # two-digit time zone @test isequal( - parse(ZonedDateTime, "2000+00", dateformat"yyyyz"), - ZonedDateTime(2000, tz"UTC"), + parse(Localized, "2000+00", dateformat"yyyyz"), + Localized(2000, tz"UTC"), ) - @test_throws ArgumentError parse(ZonedDateTime, "2016-04-11 08:00 EST", dateformat"yyyy-mm-dd HH:MM zzz") + @test_throws ArgumentError parse(Localized, "2016-04-11 08:00 EST", dateformat"yyyy-mm-dd HH:MM zzz") # test AbstractString @test isequal( - parse(ZonedDateTime, Test.GenericString("2018-01-01 00:00 UTC"), dateformat"yyyy-mm-dd HH:MM ZZZ"), - ZonedDateTime(2018, 1, 1, 0, tz"UTC"), + parse(Localized, Test.GenericString("2018-01-01 00:00 UTC"), dateformat"yyyy-mm-dd HH:MM ZZZ"), + Localized(2018, 1, 1, 0, tz"UTC"), ) end @testset "tryparse" begin @test isequal( - tryparse(ZonedDateTime, "2013-03-20 11:00:00+04:00", dateformat"y-m-d H:M:SSz"), - TimeZones.nullable(ZonedDateTime, ZonedDateTime(2013, 3, 20, 11, tz"UTC+04")), + tryparse(Localized, "2013-03-20 11:00:00+04:00", dateformat"y-m-d H:M:SSz"), + TimeZones.nullable(Localized, Localized(2013, 3, 20, 11, tz"UTC+04")), ) @test isequal( - tryparse(ZonedDateTime, "2016-04-11 08:00 EST", dateformat"yyyy-mm-dd HH:MM zzz"), - TimeZones.nullable(ZonedDateTime, nothing), + tryparse(Localized, "2016-04-11 08:00 EST", dateformat"yyyy-mm-dd HH:MM zzz"), + TimeZones.nullable(Localized, nothing), ) end @@ -49,5 +49,5 @@ end end @testset "default format" begin - @test default_format(ZonedDateTime) === TimeZones.ISOZonedDateTimeFormat + @test default_format(Localized) === TimeZones.ISOZonedDateTimeFormat end diff --git a/test/ranges.jl b/test/ranges.jl index 69ef92e92..fd1643bd1 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -11,10 +11,10 @@ fixed = FixedTimeZone("Fixed", -5 * 3600) d = DateTime(2013, 2, 13) raw_range = collect(d:Hour(1):d + Day(1)) -utc_range = ZonedDateTime(d, utc):Hour(1):ZonedDateTime(d, utc) + Day(1) -dst_range = ZonedDateTime(d, dst):Hour(1):ZonedDateTime(d, dst) + Day(1) -no_dst_range = ZonedDateTime(d, no_dst):Hour(1):ZonedDateTime(d, no_dst) + Day(1) -fixed_range = ZonedDateTime(d, fixed):Hour(1):ZonedDateTime(d, fixed) + Day(1) +utc_range = Localized(d, utc):Hour(1):Localized(d, utc) + Day(1) +dst_range = Localized(d, dst):Hour(1):Localized(d, dst) + Day(1) +no_dst_range = Localized(d, no_dst):Hour(1):Localized(d, no_dst) + Day(1) +fixed_range = Localized(d, fixed):Hour(1):Localized(d, fixed) + Day(1) # Each range should have 25 elements. @test length(utc_range) == 25 @@ -33,10 +33,10 @@ fixed_range = ZonedDateTime(d, fixed):Hour(1):ZonedDateTime(d, fixed) + Day(1) d = DateTime(2013, 3, 10) raw_range = collect(d:Hour(1):d + Day(1)) -utc_range = ZonedDateTime(d, utc):Hour(1):ZonedDateTime(d, utc) + Day(1) -dst_range = ZonedDateTime(d, dst):Hour(1):ZonedDateTime(d, dst) + Day(1) -no_dst_range = ZonedDateTime(d, no_dst):Hour(1):ZonedDateTime(d, no_dst) + Day(1) -fixed_range = ZonedDateTime(d, fixed):Hour(1):ZonedDateTime(d, fixed) + Day(1) +utc_range = Localized(d, utc):Hour(1):Localized(d, utc) + Day(1) +dst_range = Localized(d, dst):Hour(1):Localized(d, dst) + Day(1) +no_dst_range = Localized(d, no_dst):Hour(1):Localized(d, no_dst) + Day(1) +fixed_range = Localized(d, fixed):Hour(1):Localized(d, fixed) + Day(1) # The range that observes DST should have only 24 elements because 02:00 is skipped. @test length(utc_range) == 25 @@ -58,10 +58,10 @@ fixed_range = ZonedDateTime(d, fixed):Hour(1):ZonedDateTime(d, fixed) + Day(1) d = DateTime(2013, 11, 3) raw_range = collect(d:Hour(1):d + Day(1)) -utc_range = ZonedDateTime(d, utc):Hour(1):ZonedDateTime(d, utc) + Day(1) -dst_range = ZonedDateTime(d, dst):Hour(1):ZonedDateTime(d, dst) + Day(1) -no_dst_range = ZonedDateTime(d, no_dst):Hour(1):ZonedDateTime(d, no_dst) + Day(1) -fixed_range = ZonedDateTime(d, fixed):Hour(1):ZonedDateTime(d, fixed) + Day(1) +utc_range = Localized(d, utc):Hour(1):Localized(d, utc) + Day(1) +dst_range = Localized(d, dst):Hour(1):Localized(d, dst) + Day(1) +no_dst_range = Localized(d, no_dst):Hour(1):Localized(d, no_dst) + Day(1) +fixed_range = Localized(d, fixed):Hour(1):Localized(d, fixed) + Day(1) # The range that observes DST should have 26 elements because of the two instances of 01:00. @test length(utc_range) == 25 @@ -79,27 +79,27 @@ fixed_range = ZonedDateTime(d, fixed):Hour(1):ZonedDateTime(d, fixed) + Day(1) @test TimeZones.utc.(fixed_range) == raw_range .+ Hour(5) # filter behaviour with a non-existent hour -range = ZonedDateTime(2015, 3, 8, dst):Dates.Hour(1):ZonedDateTime(2015, 3, 10, dst) +range = Localized(2015, 3, 8, dst):Dates.Hour(1):Localized(2015, 3, 10, dst) @test filter(dt -> Dates.hour(dt) == 2, range) == [ - # ZonedDateTime(2015, 3, 8, 2, dst) # Non-existent hour - ZonedDateTime(2015, 3, 9, 2, dst) + # Localized(2015, 3, 8, 2, dst) # Non-existent hour + Localized(2015, 3, 9, 2, dst) ] # filter behaviour with ambiguous hour -range = ZonedDateTime(2015, 11, 1, dst):Dates.Hour(1):ZonedDateTime(2015, 11, 3, dst) +range = Localized(2015, 11, 1, dst):Dates.Hour(1):Localized(2015, 11, 3, dst) @test filter(dt -> Dates.hour(dt) == 1, range) == [ - ZonedDateTime(2015, 11, 1, 1, dst, 1) - ZonedDateTime(2015, 11, 1, 1, dst, 2) - ZonedDateTime(2015, 11, 2, 1, dst) + Localized(2015, 11, 1, 1, dst, 1) + Localized(2015, 11, 1, 1, dst, 2) + Localized(2015, 11, 2, 1, dst) ] -# default step for a ZonedDateTime range +# default step for a Localized range if VERSION < v"0.7.0-DEV.2778" - range = ZonedDateTime(2017, 10, 1, 9, utc):ZonedDateTime(2017, 12, 8, 23, utc) + range = Localized(2017, 10, 1, 9, utc):Localized(2017, 12, 8, 23, utc) @test step(range) == Dates.Day(1) elseif VERSION < v"0.7-DEV+" # Currently failing on Julia 0.7-DEV. Disabling for now. @test_warn( - "colon(start::T, stop::T) where T <: ZonedDateTime is deprecated, use start:Day(1):stop instead.", - ZonedDateTime(2017, 10, 1, 9, utc):ZonedDateTime(2017, 12, 8, 23, utc) + "colon(start::T, stop::T) where T <: Localized is deprecated, use start:Day(1):stop instead.", + Localized(2017, 10, 1, 9, utc):Localized(2017, 12, 8, 23, utc) ) end diff --git a/test/rounding.jl b/test/rounding.jl index 2f7bbd88d..bc4ac7408 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -23,11 +23,11 @@ dt = DateTime(2016) for tz in [utc, fixed, winnipeg, st_johns, eucla, colombo] local tz - local zdt = ZonedDateTime(dt, tz) + local ldt = Localized(dt, tz) for p in [Dates.Year, Dates.Month, Dates.Day, Dates.Hour, Dates.Minute, Dates.Second] - @test floor(zdt, p) == zdt - @test ceil(zdt, p) == zdt - @test round(zdt, p) == zdt + @test floor(ldt, p) == ldt + @test ceil(ldt, p) == ldt + @test round(ldt, p) == ldt end end @@ -37,31 +37,31 @@ dt = DateTime(2016, 2, 5, 13, 10, 20, 500) for tz in [utc, fixed, winnipeg, st_johns, eucla, colombo] local tz - local zdt = ZonedDateTime(dt, tz) - - @test floor(zdt, Dates.Year) == ZonedDateTime(2016, tz) - @test floor(zdt, Dates.Month) == ZonedDateTime(2016, 2, tz) - @test floor(zdt, Dates.Week) == ZonedDateTime(2016, 2, tz) # Previous Monday - @test floor(zdt, Dates.Day) == ZonedDateTime(2016, 2, 5, tz) - @test floor(zdt, Dates.Hour) == ZonedDateTime(2016, 2, 5, 13, tz) - @test floor(zdt, Dates.Minute) == ZonedDateTime(2016, 2, 5, 13, 10, tz) - @test floor(zdt, Dates.Second) == ZonedDateTime(2016, 2, 5, 13, 10, 20, tz) - - @test ceil(zdt, Dates.Year) == ZonedDateTime(2017, tz) - @test ceil(zdt, Dates.Month) == ZonedDateTime(2016, 3, tz) - @test ceil(zdt, Dates.Week) == ZonedDateTime(2016, 2, 8, tz) # Following Monday - @test ceil(zdt, Dates.Day) == ZonedDateTime(2016, 2, 6, tz) - @test ceil(zdt, Dates.Hour) == ZonedDateTime(2016, 2, 5, 14, tz) - @test ceil(zdt, Dates.Minute) == ZonedDateTime(2016, 2, 5, 13, 11, tz) - @test ceil(zdt, Dates.Second) == ZonedDateTime(2016, 2, 5, 13, 10, 21, tz) - - @test round(zdt, Dates.Year) == ZonedDateTime(2016, tz) - @test round(zdt, Dates.Month) == ZonedDateTime(2016, 2, tz) - @test round(zdt, Dates.Week) == ZonedDateTime(2016, 2, 8, tz) # Following Monday - @test round(zdt, Dates.Day) == ZonedDateTime(2016, 2, 6, tz) - @test round(zdt, Dates.Hour) == ZonedDateTime(2016, 2, 5, 13, tz) - @test round(zdt, Dates.Minute) == ZonedDateTime(2016, 2, 5, 13, 10, tz) - @test round(zdt, Dates.Second) == ZonedDateTime(2016, 2, 5, 13, 10, 21, tz) + local ldt = Localized(dt, tz) + + @test floor(ldt, Dates.Year) == Localized(2016, tz) + @test floor(ldt, Dates.Month) == Localized(2016, 2, tz) + @test floor(ldt, Dates.Week) == Localized(2016, 2, tz) # Previous Monday + @test floor(ldt, Dates.Day) == Localized(2016, 2, 5, tz) + @test floor(ldt, Dates.Hour) == Localized(2016, 2, 5, 13, tz) + @test floor(ldt, Dates.Minute) == Localized(2016, 2, 5, 13, 10, tz) + @test floor(ldt, Dates.Second) == Localized(2016, 2, 5, 13, 10, 20, tz) + + @test ceil(ldt, Dates.Year) == Localized(2017, tz) + @test ceil(ldt, Dates.Month) == Localized(2016, 3, tz) + @test ceil(ldt, Dates.Week) == Localized(2016, 2, 8, tz) # Following Monday + @test ceil(ldt, Dates.Day) == Localized(2016, 2, 6, tz) + @test ceil(ldt, Dates.Hour) == Localized(2016, 2, 5, 14, tz) + @test ceil(ldt, Dates.Minute) == Localized(2016, 2, 5, 13, 11, tz) + @test ceil(ldt, Dates.Second) == Localized(2016, 2, 5, 13, 10, 21, tz) + + @test round(ldt, Dates.Year) == Localized(2016, tz) + @test round(ldt, Dates.Month) == Localized(2016, 2, tz) + @test round(ldt, Dates.Week) == Localized(2016, 2, 8, tz) # Following Monday + @test round(ldt, Dates.Day) == Localized(2016, 2, 6, tz) + @test round(ldt, Dates.Hour) == Localized(2016, 2, 5, 13, tz) + @test round(ldt, Dates.Minute) == Localized(2016, 2, 5, 13, 10, tz) + @test round(ldt, Dates.Second) == Localized(2016, 2, 5, 13, 10, 21, tz) end ########################## @@ -76,35 +76,35 @@ end dt = DateTime(2016, 3, 13, 3, 15) # 15 minutes after transition -zdt = ZonedDateTime(dt, fixed) -@test floor(zdt, Dates.Day) == ZonedDateTime(2016, 3, 13, fixed) -@test floor(zdt, Dates.Hour(2)) == ZonedDateTime(2016, 3, 13, 2, fixed) +ldt = Localized(dt, fixed) +@test floor(ldt, Dates.Day) == Localized(2016, 3, 13, fixed) +@test floor(ldt, Dates.Hour(2)) == Localized(2016, 3, 13, 2, fixed) for tz in [winnipeg, st_johns] local tz - local zdt = ZonedDateTime(dt, tz) - @test floor(zdt, Dates.Day) == ZonedDateTime(2016, 3, 13, tz) - @test floor(zdt, Dates.Hour(2)) == ZonedDateTime(2016, 3, 13, 1, tz) + local ldt = Localized(dt, tz) + @test floor(ldt, Dates.Day) == Localized(2016, 3, 13, tz) + @test floor(ldt, Dates.Hour(2)) == Localized(2016, 3, 13, 1, tz) end # Test rounding forward, toward the missing hour. dt = DateTime(2016, 3, 13, 1, 55) # 5 minutes before transition -zdt = ZonedDateTime(dt, fixed) -@test ceil(zdt, Dates.Day) == ZonedDateTime(2016, 3, 14, fixed) -@test ceil(zdt, Dates.Hour) == ZonedDateTime(2016, 3, 13, 2, fixed) -@test ceil(zdt, Dates.Minute(30)) == ZonedDateTime(2016, 3, 13, 2, fixed) -@test round(zdt, Dates.Minute(30)) == ZonedDateTime(2016, 3, 13, 2, fixed) +ldt = Localized(dt, fixed) +@test ceil(ldt, Dates.Day) == Localized(2016, 3, 14, fixed) +@test ceil(ldt, Dates.Hour) == Localized(2016, 3, 13, 2, fixed) +@test ceil(ldt, Dates.Minute(30)) == Localized(2016, 3, 13, 2, fixed) +@test round(ldt, Dates.Minute(30)) == Localized(2016, 3, 13, 2, fixed) for tz in [winnipeg, st_johns] local tz - local zdt = ZonedDateTime(dt, tz) + local ldt = Localized(dt, tz) - @test ceil(zdt, Dates.Day) == ZonedDateTime(2016, 3, 14, tz) - @test ceil(zdt, Dates.Hour) == ZonedDateTime(2016, 3, 13, 3, tz) - @test ceil(zdt, Dates.Minute(30)) == ZonedDateTime(2016, 3, 13, 3, tz) - @test round(zdt, Dates.Minute(30)) == ZonedDateTime(2016, 3, 13, 3, tz) + @test ceil(ldt, Dates.Day) == Localized(2016, 3, 14, tz) + @test ceil(ldt, Dates.Hour) == Localized(2016, 3, 13, 3, tz) + @test ceil(ldt, Dates.Minute(30)) == Localized(2016, 3, 13, 3, tz) + @test round(ldt, Dates.Minute(30)) == Localized(2016, 3, 13, 3, tz) end # Test rounding from "midday". @@ -112,11 +112,11 @@ end dt = DateTime(2016, 3, 13, 12) # Noon on day of transition # Noon is the middle of the day, and ties round up by default. -@test round(ZonedDateTime(dt, fixed), Dates.Day) == ZonedDateTime(2016, 3, 14, fixed) +@test round(Localized(dt, fixed), Dates.Day) == Localized(2016, 3, 14, fixed) # Noon isn't the middle of the day, as 2:00 through 2:59:59.999 are missing in these zones. -@test round(ZonedDateTime(dt, winnipeg), Dates.Day) == ZonedDateTime(2016, 3, 13, winnipeg) -@test round(ZonedDateTime(dt, st_johns), Dates.Day) == ZonedDateTime(2016, 3, 13, st_johns) +@test round(Localized(dt, winnipeg), Dates.Day) == Localized(2016, 3, 13, winnipeg) +@test round(Localized(dt, st_johns), Dates.Day) == Localized(2016, 3, 13, st_johns) ########################### # DST TRANSITION BACKWARD # @@ -128,16 +128,16 @@ dt = DateTime(2016, 3, 13, 12) # Noon on day of transition dt = DateTime(2015, 11, 1, 2, 15) # 15 minutes after ambiguous hour -zdt = ZonedDateTime(dt, fixed) -@test floor(zdt, Dates.Day) == ZonedDateTime(2015, 11, 1, fixed) -@test floor(zdt, Dates.Hour(3)) == ZonedDateTime(2015, 11, 1, fixed) +ldt = Localized(dt, fixed) +@test floor(ldt, Dates.Day) == Localized(2015, 11, 1, fixed) +@test floor(ldt, Dates.Hour(3)) == Localized(2015, 11, 1, fixed) # Rounding to Hour(3) will give 00:00, 03:00, 06:00, 09:00, etc. for tz in [winnipeg, st_johns] local tz - local zdt = ZonedDateTime(dt, tz) - @test floor(zdt, Dates.Day) == ZonedDateTime(2015, 11, 1, tz) - @test floor(zdt, Dates.Hour(3)) == ZonedDateTime(2015, 11, 1, 1, tz, 1) + local ldt = Localized(dt, tz) + @test floor(ldt, Dates.Day) == Localized(2015, 11, 1, tz) + @test floor(ldt, Dates.Hour(3)) == Localized(2015, 11, 1, 1, tz, 1) # Rounding is performed in the current fixed zone, then relocalized if a transition has # occurred. This means that instead of 00:00, 03:00, etc., we expect 01:00, 04:00, etc. end @@ -146,53 +146,53 @@ end dt = DateTime(2015, 11, 1, 0, 55) # 5 minutes before ambiguous hour -zdt = ZonedDateTime(dt, fixed) -@test ceil(zdt, Dates.Day) == ZonedDateTime(2015, 11, 2, fixed) -@test ceil(zdt, Dates.Hour) == ZonedDateTime(2015, 11, 1, 1, fixed) -@test ceil(zdt, Dates.Minute(30)) == ZonedDateTime(2015, 11, 1, 1, fixed) -@test round(zdt, Dates.Minute(30)) == ZonedDateTime(2015, 11, 1, 1, fixed) +ldt = Localized(dt, fixed) +@test ceil(ldt, Dates.Day) == Localized(2015, 11, 2, fixed) +@test ceil(ldt, Dates.Hour) == Localized(2015, 11, 1, 1, fixed) +@test ceil(ldt, Dates.Minute(30)) == Localized(2015, 11, 1, 1, fixed) +@test round(ldt, Dates.Minute(30)) == Localized(2015, 11, 1, 1, fixed) for tz in [winnipeg, st_johns] local tz - local zdt = ZonedDateTime(dt, tz) - next_hour = ZonedDateTime(DateTime(2015, 11, 1, 1), tz, 1) + local ldt = Localized(dt, tz) + next_hour = Localized(DateTime(2015, 11, 1, 1), tz, 1) - @test ceil(zdt, Dates.Day) == ZonedDateTime(2015, 11, 2, tz) - @test ceil(zdt, Dates.Hour) == next_hour - @test ceil(zdt, Dates.Minute(30)) == next_hour - @test round(zdt, Dates.Minute(30)) == next_hour + @test ceil(ldt, Dates.Day) == Localized(2015, 11, 2, tz) + @test ceil(ldt, Dates.Hour) == next_hour + @test ceil(ldt, Dates.Minute(30)) == next_hour + @test round(ldt, Dates.Minute(30)) == next_hour end # Test rounding forward and backward, during the ambiguous hour. dt = DateTime(2015, 11, 1, 1, 25) # During ambiguous hour -zdt = ZonedDateTime(dt, fixed) -@test floor(zdt, Dates.Day) == ZonedDateTime(2015, 11, 1, fixed) -@test ceil(zdt, Dates.Day) == ZonedDateTime(2015, 11, 2, fixed) -@test round(zdt, Dates.Day) == ZonedDateTime(2015, 11, 1, fixed) -@test floor(zdt, Dates.Hour) == ZonedDateTime(2015, 11, 1, 1, fixed) -@test ceil(zdt, Dates.Hour) == ZonedDateTime(2015, 11, 1, 2, fixed) -@test round(zdt, Dates.Hour) == ZonedDateTime(2015, 11, 1, 1, fixed) -@test floor(zdt, Dates.Minute(30)) == ZonedDateTime(2015, 11, 1, 1, fixed) -@test ceil(zdt, Dates.Minute(30)) == ZonedDateTime(2015, 11, 1, 1, 30, fixed) -@test round(zdt, Dates.Minute(30)) == ZonedDateTime(2015, 11, 1, 1, 30, fixed) +ldt = Localized(dt, fixed) +@test floor(ldt, Dates.Day) == Localized(2015, 11, 1, fixed) +@test ceil(ldt, Dates.Day) == Localized(2015, 11, 2, fixed) +@test round(ldt, Dates.Day) == Localized(2015, 11, 1, fixed) +@test floor(ldt, Dates.Hour) == Localized(2015, 11, 1, 1, fixed) +@test ceil(ldt, Dates.Hour) == Localized(2015, 11, 1, 2, fixed) +@test round(ldt, Dates.Hour) == Localized(2015, 11, 1, 1, fixed) +@test floor(ldt, Dates.Minute(30)) == Localized(2015, 11, 1, 1, fixed) +@test ceil(ldt, Dates.Minute(30)) == Localized(2015, 11, 1, 1, 30, fixed) +@test round(ldt, Dates.Minute(30)) == Localized(2015, 11, 1, 1, 30, fixed) for tz in [winnipeg, st_johns] local tz - local zdt = ZonedDateTime(dt, tz, 1) # First 1:25, before "falling back" - prev_hour = ZonedDateTime(2015, 11, 1, 1, tz, 1) - between_hours = ZonedDateTime(2015, 11, 1, 1, 30, tz, 1) - next_hour = ZonedDateTime(2015, 11, 1, 1, tz, 2) - @test floor(zdt, Dates.Day) == ZonedDateTime(2015, 11, 1, tz) - @test ceil(zdt, Dates.Day) == ZonedDateTime(2015, 11, 2, tz) - @test round(zdt, Dates.Day) == ZonedDateTime(2015, 11, 1, tz) - @test floor(zdt, Dates.Hour) == prev_hour - @test ceil(zdt, Dates.Hour) == next_hour - @test round(zdt, Dates.Hour) == prev_hour - @test floor(zdt, Dates.Minute(30)) == prev_hour - @test ceil(zdt, Dates.Minute(30)) == between_hours - @test round(zdt, Dates.Minute(30)) == between_hours + local ldt = Localized(dt, tz, 1) # First 1:25, before "falling back" + prev_hour = Localized(2015, 11, 1, 1, tz, 1) + between_hours = Localized(2015, 11, 1, 1, 30, tz, 1) + next_hour = Localized(2015, 11, 1, 1, tz, 2) + @test floor(ldt, Dates.Day) == Localized(2015, 11, 1, tz) + @test ceil(ldt, Dates.Day) == Localized(2015, 11, 2, tz) + @test round(ldt, Dates.Day) == Localized(2015, 11, 1, tz) + @test floor(ldt, Dates.Hour) == prev_hour + @test ceil(ldt, Dates.Hour) == next_hour + @test round(ldt, Dates.Hour) == prev_hour + @test floor(ldt, Dates.Minute(30)) == prev_hour + @test ceil(ldt, Dates.Minute(30)) == between_hours + @test round(ldt, Dates.Minute(30)) == between_hours end ########################### @@ -202,44 +202,44 @@ end # Test rounding to ambiguous midnight, which (unfortunately) isn't handled well when # rounding to a DatePeriod resolution. -zdt = ZonedDateTime(1996, 10, 25, 23, 55, colombo) # 5 minutes before ambiguous half-hour -@test floor(zdt, Dates.Day) == ZonedDateTime(1996, 10, 25, colombo) -@test_throws AmbiguousTimeError ceil(zdt, Dates.Day) -@test_throws AmbiguousTimeError round(zdt, Dates.Day) +ldt = Localized(1996, 10, 25, 23, 55, colombo) # 5 minutes before ambiguous half-hour +@test floor(ldt, Dates.Day) == Localized(1996, 10, 25, colombo) +@test_throws AmbiguousTimeError ceil(ldt, Dates.Day) +@test_throws AmbiguousTimeError round(ldt, Dates.Day) -zdt = ZonedDateTime(1996, 10, 26, 0, 35, colombo) # 5 minutes after ambiguous half-hour -@test_throws AmbiguousTimeError floor(zdt, Dates.Day) -@test ceil(zdt, Dates.Day) == ZonedDateTime(1996, 10, 27, colombo) -@test_throws AmbiguousTimeError round(zdt, Dates.Day) +ldt = Localized(1996, 10, 26, 0, 35, colombo) # 5 minutes after ambiguous half-hour +@test_throws AmbiguousTimeError floor(ldt, Dates.Day) +@test ceil(ldt, Dates.Day) == Localized(1996, 10, 27, colombo) +@test_throws AmbiguousTimeError round(ldt, Dates.Day) # Rounding to the ambiguous midnight works fine using a TimePeriod resolution, however. -zdt = ZonedDateTime(1996, 10, 25, 23, 55, colombo) # 5 minutes before ambiguous half-hour -@test ceil(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 1) -@test round(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 1) +ldt = Localized(1996, 10, 25, 23, 55, colombo) # 5 minutes before ambiguous half-hour +@test ceil(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 1) +@test round(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 1) -zdt = ZonedDateTime(1996, 10, 26, 0, 35, colombo) # 5 minutes after ambiguous half-hour -@test floor(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 2) +ldt = Localized(1996, 10, 26, 0, 35, colombo) # 5 minutes after ambiguous half-hour +@test floor(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 2) # Rounding during the first half-hour between 00:00 and 00:30. -zdt = ZonedDateTime(1996, 10, 26, 0, 15, colombo, 1) -@test floor(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 1) -@test ceil(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, 0, 30, colombo) -@test round(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 1) -@test floor(zdt, Dates.Minute(30)) == ZonedDateTime(1996, 10, 26, colombo, 1) -@test ceil(zdt, Dates.Minute(30)) == ZonedDateTime(1996, 10, 26, colombo, 2) -@test round(zdt, Dates.Minute(30)) == ZonedDateTime(1996, 10, 26, colombo, 2) +ldt = Localized(1996, 10, 26, 0, 15, colombo, 1) +@test floor(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 1) +@test ceil(ldt, Dates.Hour) == Localized(1996, 10, 26, 0, 30, colombo) +@test round(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 1) +@test floor(ldt, Dates.Minute(30)) == Localized(1996, 10, 26, colombo, 1) +@test ceil(ldt, Dates.Minute(30)) == Localized(1996, 10, 26, colombo, 2) +@test round(ldt, Dates.Minute(30)) == Localized(1996, 10, 26, colombo, 2) # Rounding during the second half-hour between 00:00 and 00:30. -zdt = ZonedDateTime(1996, 10, 26, 0, 15, colombo, 2) -@test floor(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 2) -@test ceil(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, 1, colombo) -@test round(zdt, Dates.Hour) == ZonedDateTime(1996, 10, 26, colombo, 2) -@test floor(zdt, Dates.Minute(30)) == ZonedDateTime(1996, 10, 26, colombo, 2) -@test ceil(zdt, Dates.Minute(30)) == ZonedDateTime(1996, 10, 26, 0, 30, colombo) -@test round(zdt, Dates.Minute(30)) == ZonedDateTime(1996, 10, 26, 0, 30, colombo) +ldt = Localized(1996, 10, 26, 0, 15, colombo, 2) +@test floor(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 2) +@test ceil(ldt, Dates.Hour) == Localized(1996, 10, 26, 1, colombo) +@test round(ldt, Dates.Hour) == Localized(1996, 10, 26, colombo, 2) +@test floor(ldt, Dates.Minute(30)) == Localized(1996, 10, 26, colombo, 2) +@test ceil(ldt, Dates.Minute(30)) == Localized(1996, 10, 26, 0, 30, colombo) +@test round(ldt, Dates.Minute(30)) == Localized(1996, 10, 26, 0, 30, colombo) ############### # ERROR CASES # @@ -250,7 +250,7 @@ zdt = ZonedDateTime(1996, 10, 26, 0, 15, colombo, 2) dt = DateTime(2016, 2, 28, 12, 15, 10, 190) for tz in [utc, fixed, winnipeg, st_johns, eucla, colombo] local tz - local zdt = ZonedDateTime(dt, tz) + local ldt = Localized(dt, tz) for p in [Dates.Year, Dates.Month, Dates.Day, Dates.Hour, Dates.Minute, Dates.Second] for v in [-1, 0] @test_throws DomainError floor(dt, p(v)) diff --git a/test/types.jl b/test/types.jl index e48c840ce..717489838 100644 --- a/test/types.jl +++ b/test/types.jl @@ -63,19 +63,19 @@ local_dt = DateTime(1916, 2, 1, 0) utc_dt = DateTime(1916, 1, 31, 23) # Disambiguating parameters ignored when there is no ambiguity. -@test ZonedDateTime(local_dt, warsaw).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw, 1).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw, 2).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw, true).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw, false).zone.name == :CET -@test ZonedDateTime(utc_dt, warsaw, from_utc=true).zone.name == :CET +@test Localized(local_dt, warsaw).zone.name == :CET +@test Localized(local_dt, warsaw, 1).zone.name == :CET +@test Localized(local_dt, warsaw, 2).zone.name == :CET +@test Localized(local_dt, warsaw, true).zone.name == :CET +@test Localized(local_dt, warsaw, false).zone.name == :CET +@test Localized(utc_dt, warsaw, from_utc=true).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, 1).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, 2).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, true).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, false).utc_datetime == utc_dt -@test ZonedDateTime(utc_dt, warsaw, from_utc=true).utc_datetime == utc_dt +@test Localized(local_dt, warsaw).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, 1).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, 2).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, true).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, false).utc_datetime == utc_dt +@test Localized(utc_dt, warsaw, from_utc=true).utc_datetime == utc_dt # Daylight saving time behaviour @@ -83,19 +83,19 @@ local_dt = DateTime(1916, 6, 1, 0) utc_dt = DateTime(1916, 5, 31, 22) # Disambiguating parameters ignored when there is no ambiguity. -@test ZonedDateTime(local_dt, warsaw).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw, 1).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw, 2).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw, true).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw, false).zone.name == :CEST -@test ZonedDateTime(utc_dt, warsaw, from_utc=true).zone.name == :CEST +@test Localized(local_dt, warsaw).zone.name == :CEST +@test Localized(local_dt, warsaw, 1).zone.name == :CEST +@test Localized(local_dt, warsaw, 2).zone.name == :CEST +@test Localized(local_dt, warsaw, true).zone.name == :CEST +@test Localized(local_dt, warsaw, false).zone.name == :CEST +@test Localized(utc_dt, warsaw, from_utc=true).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, 1).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, 2).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, true).utc_datetime == utc_dt -@test ZonedDateTime(local_dt, warsaw, false).utc_datetime == utc_dt -@test ZonedDateTime(utc_dt, warsaw, from_utc=true).utc_datetime == utc_dt +@test Localized(local_dt, warsaw).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, 1).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, 2).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, true).utc_datetime == utc_dt +@test Localized(local_dt, warsaw, false).utc_datetime == utc_dt +@test Localized(utc_dt, warsaw, from_utc=true).utc_datetime == utc_dt # Typical "spring-forward" behaviour @@ -108,76 +108,76 @@ utc_dts = ( DateTime(1916,4,30,21), DateTime(1916,4,30,22), ) -@test_throws NonExistentTimeError ZonedDateTime(local_dts[2], warsaw) -@test_throws NonExistentTimeError ZonedDateTime(local_dts[2], warsaw, 1) -@test_throws NonExistentTimeError ZonedDateTime(local_dts[2], warsaw, 2) -@test_throws NonExistentTimeError ZonedDateTime(local_dts[2], warsaw, true) -@test_throws NonExistentTimeError ZonedDateTime(local_dts[2], warsaw, false) +@test_throws NonExistentTimeError Localized(local_dts[2], warsaw) +@test_throws NonExistentTimeError Localized(local_dts[2], warsaw, 1) +@test_throws NonExistentTimeError Localized(local_dts[2], warsaw, 2) +@test_throws NonExistentTimeError Localized(local_dts[2], warsaw, true) +@test_throws NonExistentTimeError Localized(local_dts[2], warsaw, false) -@test ZonedDateTime(local_dts[1], warsaw).zone.name == :CET -@test ZonedDateTime(local_dts[3], warsaw).zone.name == :CEST -@test ZonedDateTime(utc_dts[1], warsaw, from_utc=true).zone.name == :CET -@test ZonedDateTime(utc_dts[2], warsaw, from_utc=true).zone.name == :CEST +@test Localized(local_dts[1], warsaw).zone.name == :CET +@test Localized(local_dts[3], warsaw).zone.name == :CEST +@test Localized(utc_dts[1], warsaw, from_utc=true).zone.name == :CET +@test Localized(utc_dts[2], warsaw, from_utc=true).zone.name == :CEST -@test ZonedDateTime(local_dts[1], warsaw).utc_datetime == utc_dts[1] -@test ZonedDateTime(local_dts[3], warsaw).utc_datetime == utc_dts[2] -@test ZonedDateTime(utc_dts[1], warsaw, from_utc=true).utc_datetime == utc_dts[1] -@test ZonedDateTime(utc_dts[2], warsaw, from_utc=true).utc_datetime == utc_dts[2] +@test Localized(local_dts[1], warsaw).utc_datetime == utc_dts[1] +@test Localized(local_dts[3], warsaw).utc_datetime == utc_dts[2] +@test Localized(utc_dts[1], warsaw, from_utc=true).utc_datetime == utc_dts[1] +@test Localized(utc_dts[2], warsaw, from_utc=true).utc_datetime == utc_dts[2] # Typical "fall-back" behaviour local_dt = DateTime(1916, 10, 1, 0) utc_dts = (DateTime(1916, 9, 30, 22), DateTime(1916, 9, 30, 23)) -@test_throws AmbiguousTimeError ZonedDateTime(local_dt, warsaw) - -@test ZonedDateTime(local_dt, warsaw, 1).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw, 2).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw, true).zone.name == :CEST -@test ZonedDateTime(local_dt, warsaw, false).zone.name == :CET -@test ZonedDateTime(utc_dts[1], warsaw, from_utc=true).zone.name == :CEST -@test ZonedDateTime(utc_dts[2], warsaw, from_utc=true).zone.name == :CET - -@test ZonedDateTime(local_dt, warsaw, 1).utc_datetime == utc_dts[1] -@test ZonedDateTime(local_dt, warsaw, 2).utc_datetime == utc_dts[2] -@test ZonedDateTime(local_dt, warsaw, true).utc_datetime == utc_dts[1] -@test ZonedDateTime(local_dt, warsaw, false).utc_datetime == utc_dts[2] -@test ZonedDateTime(utc_dts[1], warsaw, from_utc=true).utc_datetime == utc_dts[1] -@test ZonedDateTime(utc_dts[2], warsaw, from_utc=true).utc_datetime == utc_dts[2] +@test_throws AmbiguousTimeError Localized(local_dt, warsaw) + +@test Localized(local_dt, warsaw, 1).zone.name == :CEST +@test Localized(local_dt, warsaw, 2).zone.name == :CET +@test Localized(local_dt, warsaw, true).zone.name == :CEST +@test Localized(local_dt, warsaw, false).zone.name == :CET +@test Localized(utc_dts[1], warsaw, from_utc=true).zone.name == :CEST +@test Localized(utc_dts[2], warsaw, from_utc=true).zone.name == :CET + +@test Localized(local_dt, warsaw, 1).utc_datetime == utc_dts[1] +@test Localized(local_dt, warsaw, 2).utc_datetime == utc_dts[2] +@test Localized(local_dt, warsaw, true).utc_datetime == utc_dts[1] +@test Localized(local_dt, warsaw, false).utc_datetime == utc_dts[2] +@test Localized(utc_dts[1], warsaw, from_utc=true).utc_datetime == utc_dts[1] +@test Localized(utc_dts[2], warsaw, from_utc=true).utc_datetime == utc_dts[2] # Zone offset reduced creating an ambiguous hour local_dt = DateTime(1922,5,31,23) utc_dts = (DateTime(1922, 5, 31, 21), DateTime(1922, 5, 31, 22)) -@test_throws AmbiguousTimeError ZonedDateTime(local_dt, warsaw) +@test_throws AmbiguousTimeError Localized(local_dt, warsaw) -@test ZonedDateTime(local_dt, warsaw, 1).zone.name == :EET -@test ZonedDateTime(local_dt, warsaw, 2).zone.name == :CET -@test_throws AmbiguousTimeError ZonedDateTime(local_dt, warsaw, true) -@test_throws AmbiguousTimeError ZonedDateTime(local_dt, warsaw, false) -@test ZonedDateTime(utc_dts[1], warsaw, from_utc=true).zone.name == :EET -@test ZonedDateTime(utc_dts[2], warsaw, from_utc=true).zone.name == :CET +@test Localized(local_dt, warsaw, 1).zone.name == :EET +@test Localized(local_dt, warsaw, 2).zone.name == :CET +@test_throws AmbiguousTimeError Localized(local_dt, warsaw, true) +@test_throws AmbiguousTimeError Localized(local_dt, warsaw, false) +@test Localized(utc_dts[1], warsaw, from_utc=true).zone.name == :EET +@test Localized(utc_dts[2], warsaw, from_utc=true).zone.name == :CET -@test ZonedDateTime(local_dt, warsaw, 1).utc_datetime == utc_dts[1] -@test ZonedDateTime(local_dt, warsaw, 2).utc_datetime == utc_dts[2] -@test ZonedDateTime(utc_dts[1], warsaw, from_utc=true).utc_datetime == utc_dts[1] -@test ZonedDateTime(utc_dts[2], warsaw, from_utc=true).utc_datetime == utc_dts[2] +@test Localized(local_dt, warsaw, 1).utc_datetime == utc_dts[1] +@test Localized(local_dt, warsaw, 2).utc_datetime == utc_dts[2] +@test Localized(utc_dts[1], warsaw, from_utc=true).utc_datetime == utc_dts[1] +@test Localized(utc_dts[2], warsaw, from_utc=true).utc_datetime == utc_dts[2] # Check behaviour when save is larger than an hour. paris = resolve("Europe/Paris", tzdata["europe"]...) -@test ZonedDateTime(DateTime(1945,4,2,1), paris).zone == FixedTimeZone("WEST", 0, 3600) -@test_throws NonExistentTimeError ZonedDateTime(DateTime(1945,4,2,2), paris) -@test ZonedDateTime(DateTime(1945,4,2,3), paris).zone == FixedTimeZone("WEMT", 0, 7200) +@test Localized(DateTime(1945,4,2,1), paris).zone == FixedTimeZone("WEST", 0, 3600) +@test_throws NonExistentTimeError Localized(DateTime(1945,4,2,2), paris) +@test Localized(DateTime(1945,4,2,3), paris).zone == FixedTimeZone("WEMT", 0, 7200) -@test_throws AmbiguousTimeError ZonedDateTime(DateTime(1945,9,16,2), paris) -@test ZonedDateTime(DateTime(1945,9,16,2), paris, 1).zone == FixedTimeZone("WEMT", 0, 7200) -@test ZonedDateTime(DateTime(1945,9,16,2), paris, 2).zone == FixedTimeZone("CET", 3600, 0) +@test_throws AmbiguousTimeError Localized(DateTime(1945,9,16,2), paris) +@test Localized(DateTime(1945,9,16,2), paris, 1).zone == FixedTimeZone("WEMT", 0, 7200) +@test Localized(DateTime(1945,9,16,2), paris, 2).zone == FixedTimeZone("CET", 3600, 0) # Ensure that dates are continuous when both a UTC offset and the DST offset change. -@test ZonedDateTime(DateTime(1945,9,16,1), paris).utc_datetime == DateTime(1945,9,15,23) -@test ZonedDateTime(DateTime(1945,9,16,2), paris, 1).utc_datetime == DateTime(1945,9,16,0) -@test ZonedDateTime(DateTime(1945,9,16,2), paris, 2).utc_datetime == DateTime(1945,9,16,1) -@test ZonedDateTime(DateTime(1945,9,16,3), paris).utc_datetime == DateTime(1945,9,16,2) +@test Localized(DateTime(1945,9,16,1), paris).utc_datetime == DateTime(1945,9,15,23) +@test Localized(DateTime(1945,9,16,2), paris, 1).utc_datetime == DateTime(1945,9,16,0) +@test Localized(DateTime(1945,9,16,2), paris, 2).utc_datetime == DateTime(1945,9,16,1) +@test Localized(DateTime(1945,9,16,3), paris).utc_datetime == DateTime(1945,9,16,2) # Transitions changes that exceed an hour. @@ -188,24 +188,24 @@ t = VariableTimeZone("Testing", [ ]) # A "spring forward" where 2 hours are skipped. -@test ZonedDateTime(DateTime(1950,3,31,23), t).zone == FixedTimeZone("TST",0,0) -@test_throws NonExistentTimeError ZonedDateTime(DateTime(1950,4,1,0), t) -@test_throws NonExistentTimeError ZonedDateTime(DateTime(1950,4,1,1), t) -@test ZonedDateTime(DateTime(1950,4,1,2), t).zone == FixedTimeZone("TDT",0,7200) +@test Localized(DateTime(1950,3,31,23), t).zone == FixedTimeZone("TST",0,0) +@test_throws NonExistentTimeError Localized(DateTime(1950,4,1,0), t) +@test_throws NonExistentTimeError Localized(DateTime(1950,4,1,1), t) +@test Localized(DateTime(1950,4,1,2), t).zone == FixedTimeZone("TDT",0,7200) # A "fall back" where 2 hours are duplicated. Never appears to occur in reality. -@test ZonedDateTime(DateTime(1950,8,31,23), t).utc_datetime == DateTime(1950,8,31,21) # TDT +@test Localized(DateTime(1950,8,31,23), t).utc_datetime == DateTime(1950,8,31,21) # TDT # First occurrences of duplicated hours. -@test ZonedDateTime(DateTime(1950,9,1,0), t, 1).utc_datetime == DateTime(1950,8,31,22) # TST -@test ZonedDateTime(DateTime(1950,9,1,1), t, 1).utc_datetime == DateTime(1950,8,31,23) # TST +@test Localized(DateTime(1950,9,1,0), t, 1).utc_datetime == DateTime(1950,8,31,22) # TST +@test Localized(DateTime(1950,9,1,1), t, 1).utc_datetime == DateTime(1950,8,31,23) # TST # Second occurrences of duplicated hours. -@test ZonedDateTime(DateTime(1950,9,1,0), t, 2).utc_datetime == DateTime(1950,9,1,0) # TDT -@test ZonedDateTime(DateTime(1950,9,1,1), t, 2).utc_datetime == DateTime(1950,9,1,1) # TDT +@test Localized(DateTime(1950,9,1,0), t, 2).utc_datetime == DateTime(1950,9,1,0) # TDT +@test Localized(DateTime(1950,9,1,1), t, 2).utc_datetime == DateTime(1950,9,1,1) # TDT -@test ZonedDateTime(DateTime(1950,9,1,2), t).utc_datetime == DateTime(1950,9,1,2) # TDT +@test Localized(DateTime(1950,9,1,2), t).utc_datetime == DateTime(1950,9,1,2) # TDT # Ambiguous local DateTime that has more than 2 solutions. Never occurs in reality. @@ -216,24 +216,24 @@ t = VariableTimeZone("Testing", [ Transition(DateTime(1960,9,1), FixedTimeZone("TST",0,0)), ]) -@test ZonedDateTime(DateTime(1960,8,31,23), t).utc_datetime == DateTime(1960,8,31,21) # TDT -@test ZonedDateTime(DateTime(1960,9,1,0), t, 1).utc_datetime == DateTime(1960,8,31,22) # TDT -@test ZonedDateTime(DateTime(1960,9,1,0), t, 2).utc_datetime == DateTime(1960,8,31,23) # TXT -@test ZonedDateTime(DateTime(1960,9,1,0), t, 3).utc_datetime == DateTime(1960,9,1,0) # TST -@test ZonedDateTime(DateTime(1960,9,1,1), t).utc_datetime == DateTime(1960,9,1,1) # TST +@test Localized(DateTime(1960,8,31,23), t).utc_datetime == DateTime(1960,8,31,21) # TDT +@test Localized(DateTime(1960,9,1,0), t, 1).utc_datetime == DateTime(1960,8,31,22) # TDT +@test Localized(DateTime(1960,9,1,0), t, 2).utc_datetime == DateTime(1960,8,31,23) # TXT +@test Localized(DateTime(1960,9,1,0), t, 3).utc_datetime == DateTime(1960,9,1,0) # TST +@test Localized(DateTime(1960,9,1,1), t).utc_datetime == DateTime(1960,9,1,1) # TST -@test_throws AmbiguousTimeError ZonedDateTime(DateTime(1960,9,1,0), t, true) -@test_throws AmbiguousTimeError ZonedDateTime(DateTime(1960,9,1,0), t, false) +@test_throws AmbiguousTimeError Localized(DateTime(1960,9,1,0), t, true) +@test_throws AmbiguousTimeError Localized(DateTime(1960,9,1,0), t, false) # Significant offset change: -11:00 -> 13:00. apia = resolve("Pacific/Apia", tzdata["australasia"]...) # Skips an entire day. -@test ZonedDateTime(DateTime(2011,12,29,23),apia).utc_datetime == DateTime(2011,12,30,9) -@test_throws NonExistentTimeError ZonedDateTime(DateTime(2011,12,30,0),apia) -@test_throws NonExistentTimeError ZonedDateTime(DateTime(2011,12,30,23),apia) -@test ZonedDateTime(DateTime(2011,12,31,0),apia).utc_datetime == DateTime(2011,12,30,10) +@test Localized(DateTime(2011,12,29,23),apia).utc_datetime == DateTime(2011,12,30,9) +@test_throws NonExistentTimeError Localized(DateTime(2011,12,30,0),apia) +@test_throws NonExistentTimeError Localized(DateTime(2011,12,30,23),apia) +@test Localized(DateTime(2011,12,31,0),apia).utc_datetime == DateTime(2011,12,30,10) # Redundant transitions should be ignored. @@ -253,25 +253,25 @@ dup = VariableTimeZone("DuplicateTest", [ ]) # Make sure that the duplicated hour only doesn't contain an additional entry. -@test_throws AmbiguousTimeError ZonedDateTime(DateTime(1935,9,1), dup) -@test ZonedDateTime(DateTime(1935,9,1), dup, 1).zone.name == Symbol("DTDT-2") -@test ZonedDateTime(DateTime(1935,9,1), dup, 2).zone.name == :DTST -@test_throws BoundsError ZonedDateTime(DateTime(1935,9,1), dup, 3) +@test_throws AmbiguousTimeError Localized(DateTime(1935,9,1), dup) +@test Localized(DateTime(1935,9,1), dup, 1).zone.name == Symbol("DTDT-2") +@test Localized(DateTime(1935,9,1), dup, 2).zone.name == :DTST +@test_throws BoundsError Localized(DateTime(1935,9,1), dup, 3) # Ensure that DTDT-1 is completely ignored. -@test_throws NonExistentTimeError ZonedDateTime(DateTime(1935,4,1), dup) -@test ZonedDateTime(DateTime(1935,4,1,1), dup).zone.name == Symbol("DTDT-2") -@test ZonedDateTime(DateTime(1935,8,31,23), dup).zone.name == Symbol("DTDT-2") +@test_throws NonExistentTimeError Localized(DateTime(1935,4,1), dup) +@test Localized(DateTime(1935,4,1,1), dup).zone.name == Symbol("DTDT-2") +@test Localized(DateTime(1935,8,31,23), dup).zone.name == Symbol("DTDT-2") # Check equality between ZonedDateTimes utc = FixedTimeZone("UTC", 0, 0) -spring_utc = ZonedDateTime(DateTime(2010, 5, 1, 12), utc) -spring_apia = ZonedDateTime(DateTime(2010, 5, 1, 1), apia) +spring_utc = Localized(DateTime(2010, 5, 1, 12), utc) +spring_apia = Localized(DateTime(2010, 5, 1, 1), apia) # The absolutely min DateTime you can create. Even smaller than typemin(DateTime) -early_utc = ZonedDateTime(DateTime(UTM(typemin(Int64))), utc) +early_utc = Localized(DateTime(UTM(typemin(Int64))), utc) @test spring_utc.zone == FixedTimeZone("UTC", 0, 0) @test spring_apia.zone == FixedTimeZone("SST", -39600, 0) @@ -279,13 +279,13 @@ early_utc = ZonedDateTime(DateTime(UTM(typemin(Int64))), utc) @test spring_utc !== spring_apia @test !isequal(spring_utc, spring_apia) @test hash(spring_utc) != hash(spring_apia) -@test astimezone(spring_utc, apia) === spring_apia # Since ZonedDateTime is immutable +@test astimezone(spring_utc, apia) === spring_apia # Since Localized is immutable @test astimezone(spring_apia, utc) === spring_utc @test isequal(astimezone(spring_utc, apia), spring_apia) @test hash(astimezone(spring_utc, apia)) == hash(spring_apia) -fall_utc = ZonedDateTime(DateTime(2010, 10, 1, 12), utc) -fall_apia = ZonedDateTime(DateTime(2010, 10, 1, 2), apia) +fall_utc = Localized(DateTime(2010, 10, 1, 12), utc) +fall_apia = Localized(DateTime(2010, 10, 1, 2), apia) @test fall_utc.zone == FixedTimeZone("UTC", 0, 0) @test fall_apia.zone == FixedTimeZone("SDT", -39600, 3600) @@ -295,13 +295,13 @@ fall_apia = ZonedDateTime(DateTime(2010, 10, 1, 2), apia) @test !(fall_utc > fall_apia) @test !isequal(fall_utc, fall_apia) @test hash(fall_utc) != hash(fall_apia) -@test astimezone(fall_utc, apia) === fall_apia # Since ZonedDateTime is immutable +@test astimezone(fall_utc, apia) === fall_apia # Since Localized is immutable @test astimezone(fall_apia, utc) === fall_utc @test isequal(astimezone(fall_utc, apia), fall_apia) @test hash(astimezone(fall_utc, apia)) == hash(fall_apia) # Issue #78 -x = ZonedDateTime(2017, 7, 6, 15, 44, 55, 28, warsaw) +x = Localized(2017, 7, 6, 15, 44, 55, 28, warsaw) y = deepcopy(x) @test x == y @@ -317,8 +317,8 @@ y = deepcopy(x) @test_throws NonExistentTimeError astimezone(early_utc, apia) -# ZonedDateTime constructor that takes any number of Period or TimeZone types -@test_throws ArgumentError ZonedDateTime(FixedTimeZone("UTC", 0, 0), FixedTimeZone("TMW", 86400, 0)) +# Localized constructor that takes any number of Period or TimeZone types +@test_throws ArgumentError Localized(FixedTimeZone("UTC", 0, 0), FixedTimeZone("TMW", 86400, 0)) # Equality for VariableTimeZones @@ -337,56 +337,56 @@ cutoff_tz = VariableTimeZone( "cutoff", [Transition(DateTime(1970, 1, 1), utc)], DateTime(1988, 5, 6), ) -ZonedDateTime(DateTime(1970, 1, 1), cutoff_tz) # pre cutoff -@test_throws UnhandledTimeError ZonedDateTime(DateTime(1988, 5, 6), cutoff_tz) # on cutoff -@test_throws UnhandledTimeError ZonedDateTime(DateTime(1989, 5, 7), cutoff_tz) -@test_throws UnhandledTimeError ZonedDateTime(DateTime(1988, 5, 5), cutoff_tz) + Hour(24) +Localized(DateTime(1970, 1, 1), cutoff_tz) # pre cutoff +@test_throws UnhandledTimeError Localized(DateTime(1988, 5, 6), cutoff_tz) # on cutoff +@test_throws UnhandledTimeError Localized(DateTime(1989, 5, 7), cutoff_tz) +@test_throws UnhandledTimeError Localized(DateTime(1988, 5, 5), cutoff_tz) + Hour(24) -zdt = ZonedDateTime(DateTime(2038, 3, 28), warsaw, from_utc=true) -@test_throws UnhandledTimeError zdt + Hour(1) +ldt = Localized(DateTime(2038, 3, 28), warsaw, from_utc=true) +@test_throws UnhandledTimeError ldt + Hour(1) # TimeZones that no longer have any transitions after the max_year shouldn't have a cutoff # eg. Asia/Hong_Kong, Pacific/Honolulu, Australia/Perth perth = resolve("Australia/Perth", tzdata["australasia"]...) -zdt = ZonedDateTime(DateTime(2200, 1, 1), perth, from_utc=true) +ldt = Localized(DateTime(2200, 1, 1), perth, from_utc=true) # Convenience constructors for making a DateTime on-the-fly digits = [2010, 1, 2, 3, 4, 5, 6] for i in eachindex(digits) - @test ZonedDateTime(digits[1:i]..., warsaw) == ZonedDateTime(DateTime(digits[1:i]...), warsaw) - @test ZonedDateTime(digits[1:i]..., utc) == ZonedDateTime(DateTime(digits[1:i]...), utc) + @test Localized(digits[1:i]..., warsaw) == Localized(DateTime(digits[1:i]...), warsaw) + @test Localized(digits[1:i]..., utc) == Localized(DateTime(digits[1:i]...), utc) end # Convenience constructor dealing with ambiguous time digits = [1916, 10, 1, 0, 2, 3, 4] # Fall DST transition in Europe/Warsaw for i in eachindex(digits) expected = [ - ZonedDateTime(DateTime(digits[1:i]...), warsaw, 1) - ZonedDateTime(DateTime(digits[1:i]...), warsaw, 2) + Localized(DateTime(digits[1:i]...), warsaw, 1) + Localized(DateTime(digits[1:i]...), warsaw, 2) ] if i > 1 - @test_throws AmbiguousTimeError ZonedDateTime(digits[1:i]..., warsaw) + @test_throws AmbiguousTimeError Localized(digits[1:i]..., warsaw) end - @test ZonedDateTime(digits[1:i]..., warsaw, 1) == expected[1] - @test ZonedDateTime(digits[1:i]..., warsaw, 2) == expected[2] - @test ZonedDateTime(digits[1:i]..., warsaw, true) == expected[1] - @test ZonedDateTime(digits[1:i]..., warsaw, false) == expected[2] + @test Localized(digits[1:i]..., warsaw, 1) == expected[1] + @test Localized(digits[1:i]..., warsaw, 2) == expected[2] + @test Localized(digits[1:i]..., warsaw, true) == expected[1] + @test Localized(digits[1:i]..., warsaw, false) == expected[2] end # Promotion -@test_throws ErrorException promote_type(ZonedDateTime, Date) -@test_throws ErrorException promote_type(ZonedDateTime, DateTime) -@test_throws ErrorException promote_type(Date, ZonedDateTime) -@test_throws ErrorException promote_type(DateTime, ZonedDateTime) -@test promote_type(ZonedDateTime, ZonedDateTime) == ZonedDateTime +@test_throws ErrorException promote_type(Localized, Date) +@test_throws ErrorException promote_type(Localized, DateTime) +@test_throws ErrorException promote_type(Date, Localized) +@test_throws ErrorException promote_type(DateTime, Localized) +@test promote_type(Localized, Localized) == Localized # Issue #52 dt = now() -@test_throws ErrorException ZonedDateTime(dt, warsaw) > dt +@test_throws ErrorException Localized(dt, warsaw) > dt # type extrema -@test typemin(ZonedDateTime) <= ZonedDateTime(typemin(DateTime), utc) -@test typemax(ZonedDateTime) >= ZonedDateTime(typemax(DateTime), utc) +@test typemin(Localized) <= Localized(typemin(DateTime), utc) +@test typemax(Localized) >= Localized(typemax(DateTime), utc) diff --git a/test/utils.jl b/test/utils.jl index e093e78dd..a37e3f33b 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -46,20 +46,20 @@ Base.isequal(a::Array{Expr}, b::Array{Expr}) = map(strip, a) == map(strip, b) const I = Integer @test isequal( optional( - :(function ZonedDateTime(y::I, m::I=1, d::I=1, h::I=0, mi::I=0, s::I=0, ms::I=0, tz::TimeZone) - ZonedDateTime(DateTime(y,m,d,h,mi,s,ms), tz) + :(function Localized(y::I, m::I=1, d::I=1, h::I=0, mi::I=0, s::I=0, ms::I=0, tz::TimeZone) + Localized(DateTime(y,m,d,h,mi,s,ms), tz) end) ), [ - :(function ZonedDateTime(y::I,m::I,d::I,h::I,mi::I,s::I,ms::I,tz::TimeZone) - ZonedDateTime(DateTime(y,m,d,h,mi,s,ms), tz) + :(function Localized(y::I,m::I,d::I,h::I,mi::I,s::I,ms::I,tz::TimeZone) + Localized(DateTime(y,m,d,h,mi,s,ms), tz) end), - :(ZonedDateTime(y::I,m::I,d::I,h::I,mi::I,s::I,tz::TimeZone) = ZonedDateTime(y,m,d,h,mi,s,0,tz)), - :(ZonedDateTime(y::I,m::I,d::I,h::I,mi::I,tz::TimeZone) = ZonedDateTime(y,m,d,h,mi,0,0,tz)), - :(ZonedDateTime(y::I,m::I,d::I,h::I,tz::TimeZone) = ZonedDateTime(y,m,d,h,0,0,0,tz)), - :(ZonedDateTime(y::I,m::I,d::I,tz::TimeZone) = ZonedDateTime(y,m,d,0,0,0,0,tz)), - :(ZonedDateTime(y::I,m::I,tz::TimeZone) = ZonedDateTime(y,m,1,0,0,0,0,tz)), - :(ZonedDateTime(y::I,tz::TimeZone) = ZonedDateTime(y,1,1,0,0,0,0,tz)), + :(Localized(y::I,m::I,d::I,h::I,mi::I,s::I,tz::TimeZone) = Localized(y,m,d,h,mi,s,0,tz)), + :(Localized(y::I,m::I,d::I,h::I,mi::I,tz::TimeZone) = Localized(y,m,d,h,mi,0,0,tz)), + :(Localized(y::I,m::I,d::I,h::I,tz::TimeZone) = Localized(y,m,d,h,0,0,0,tz)), + :(Localized(y::I,m::I,d::I,tz::TimeZone) = Localized(y,m,d,0,0,0,0,tz)), + :(Localized(y::I,m::I,tz::TimeZone) = Localized(y,m,1,0,0,0,0,tz)), + :(Localized(y::I,tz::TimeZone) = Localized(y,1,1,0,0,0,0,tz)), ], )