diff --git a/docs/current.md b/docs/current.md index 54f0223e9..7cf320419 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 method to the `now` function 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 `ZonedDateTime`: ```julia now(tz"Europe/Warsaw") @@ -11,3 +11,47 @@ To get the `TimeZone` currently specified on you system you can use `localzone() ```julia now(localzone()) ``` + +## Today + +Similar to `now` the TimeZones package also provides a `today(::TimeZone)` method which allows you to determine the current date as a `Date` in the specified `TimeZone`. + +```julia +julia> a, b = now(tz"Pacific/Midway"), now(tz"Pacific/Apia") +(2018-01-29T12:01:53.504-11:00, 2018-01-30T13:01:53.504+14:00) + +julia> a - b +0 milliseconds + +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: + +```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) +2018-01-29T11:00:00-11:00 + +julia> ZonedDateTime(today() + Time(11), apia) # Should be 2018-01-30 +2018-01-29T11:00:00+14:00 + +julia> ZonedDateTime(today(midway) + Time(11), midway) +2018-01-29T11:00:00-11:00 + +julia> ZonedDateTime(today(apia) + Time(11), apia) +2018-01-30T11:00:00+14:00 +``` + +Alternatively, you can use the `todayat` function which takes care of this for you: + +```julia +julia> todayat(Time(11), tz"Pacific/Midway") +2018-01-29T11:00:00-11:00 + +julia> todayat(Time(11), tz"Pacific/Apia") +2018-01-30T11:00:00+14:00 +``` diff --git a/src/TimeZones.jl b/src/TimeZones.jl index e4d7e9df1..782bed681 100644 --- a/src/TimeZones.jl +++ b/src/TimeZones.jl @@ -29,7 +29,7 @@ export TimeZone, @tz_str, istimezone, FixedTimeZone, VariableTimeZone, ZonedDate # Re-export from Dates yearmonthday, yearmonth, monthday, year, month, week, day, dayofmonth, # conversion.jl - now, astimezone, + now, today, todayat, astimezone, # local.jl localzone, # ranges.jl diff --git a/src/conversions.jl b/src/conversions.jl index c855797b7..556aeed61 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -1,4 +1,6 @@ -import Compat.Dates: now, julian2datetime, unix2datetime +import Compat.Dates: unix2datetime, datetime2unix, julian2datetime, datetime2julian, + now, today +using Mocking # UTC is an abstract type defined in Dates, for some reason const utc_tz = FixedTimeZone("UTC") @@ -20,6 +22,49 @@ function now(tz::TimeZone) ZonedDateTime(utc, tz, from_utc=true) end +""" + today(tz::TimeZone) -> Date + +Returns the date portion of `now(tz)` in local time. + +# Examples + +```julia +julia> a, b = now(tz"Pacific/Midway"), now(tz"Pacific/Apia") +(2017-11-09T03:47:04.226-11:00, 2017-11-10T04:47:04.226+14:00) + +julia> a - b +0 milliseconds + +julia> today(tz"Pacific/Midway"), today(tz"Pacific/Apia") +(2017-11-09, 2017-11-10) +``` +""" +today(tz::TimeZone) = Date(localtime(now(tz))) + +""" + todayat(tod::Time, tz::TimeZone, [amb]) -> ZonedDateTime + +Creates a `ZonedDateTime` 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 + +```julia +julia> today(tz"Europe/Warsaw") +2017-11-09 + +julia> todayat(Time(10, 30), tz"Europe/Warsaw") +2017-11-09T10:30:00+01:00 +``` +""" +function todayat(tod::Time, tz::VariableTimeZone, amb::Union{Integer,Bool}) + ZonedDateTime((@mock today(tz)) + tod, tz, amb) +end + +todayat(tod::Time, tz::TimeZone) = ZonedDateTime((@mock today(tz)) + tod, tz) + + """ astimezone(zdt::ZonedDateTime, tz::TimeZone) -> ZonedDateTime @@ -46,33 +91,33 @@ function astimezone(zdt::ZonedDateTime, tz::FixedTimeZone) end function zdt2julian(zdt::ZonedDateTime) - Dates.datetime2julian(utc(zdt)) + datetime2julian(utc(zdt)) end function zdt2julian(::Type{T}, zdt::ZonedDateTime) where T<:Integer - floor(T, Dates.datetime2julian(utc(zdt))) + floor(T, datetime2julian(utc(zdt))) end function zdt2julian(::Type{T}, zdt::ZonedDateTime) where T<:Number - convert(T, Dates.datetime2julian(utc(zdt))) + convert(T, datetime2julian(utc(zdt))) end function julian2zdt(jd::Real) - ZonedDateTime(Dates.julian2datetime(jd), utc_tz, from_utc=true) + ZonedDateTime(julian2datetime(jd), utc_tz, from_utc=true) end function zdt2unix(zdt::ZonedDateTime) - Dates.datetime2unix(utc(zdt)) + datetime2unix(utc(zdt)) end function zdt2unix(::Type{T}, zdt::ZonedDateTime) where T<:Integer - floor(T, Dates.datetime2unix(utc(zdt))) + floor(T, datetime2unix(utc(zdt))) end function zdt2unix(::Type{T}, zdt::ZonedDateTime) where T<:Number - convert(T, Dates.datetime2unix(utc(zdt))) + convert(T, datetime2unix(utc(zdt))) end function unix2zdt(seconds::Integer) - ZonedDateTime(Dates.unix2datetime(seconds), utc_tz, from_utc=true) + ZonedDateTime(unix2datetime(seconds), utc_tz, from_utc=true) end diff --git a/test/conversions.jl b/test/conversions.jl index 1b75ba53a..a8fbd3a7e 100644 --- a/test/conversions.jl +++ b/test/conversions.jl @@ -1,7 +1,10 @@ import Compat.Dates +using Mocking utc = FixedTimeZone("UTC") warsaw = resolve("Europe/Warsaw", tzdata["europe"]...) +apia = resolve("Pacific/Apia", tzdata["australasia"]...) +midway = resolve("Pacific/Midway", tzdata["australasia"]...) # Converting a ZonedDateTime into a DateTime dt = DateTime(2015, 1, 1, 0) @@ -22,6 +25,24 @@ zdt = now(warsaw) @test zdt.timezone == warsaw @test Dates.datetime2unix(TimeZones.utc(zdt)) ≈ Dates.datetime2unix(dt) +# today function +@test abs(today() - today(warsaw)) <= Dates.Day(1) +@test today(apia) - today(midway) == Dates.Day(1) + +# todayat function +zdt = now(warsaw) +now_time = Time(TimeZones.localtime(zdt)) +@test todayat(now_time, warsaw) == zdt + +if !compiled_modules_enabled + 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) + end +end + # Changing time zones dt = DateTime(2015, 1, 1, 0)