From 06fa32c40ca838152cac38182690266eaa3af60c Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Mon, 23 Jan 2017 23:17:21 -0500 Subject: [PATCH] Initial work to add a Time type to Base.Dates (#12274) * Add a Time type to the Base.Dates module --- base/dates/Dates.jl | 4 +- base/dates/accessors.jl | 18 +++++++ base/dates/adjusters.jl | 34 ++++++++++++ base/dates/arithmetic.jl | 33 +++++++++--- base/dates/conversions.jl | 14 ++++- base/dates/io.jl | 47 ++++++++++------ base/dates/periods.jl | 53 ++++++++++++------ base/dates/ranges.jl | 6 ++- base/dates/types.jl | 110 ++++++++++++++++++++++++++++++-------- doc/src/stdlib/dates.md | 9 ++++ test/dates/accessors.jl | 41 +++++++++----- test/dates/adjusters.jl | 15 ++++++ test/dates/arithmetic.jl | 49 +++++++++++++++++ test/dates/conversions.jl | 8 +++ test/dates/io.jl | 9 ++++ test/dates/periods.jl | 17 ++++++ test/dates/ranges.jl | 55 +++++++++++++++---- test/dates/types.jl | 39 ++++++++++++-- 18 files changed, 467 insertions(+), 94 deletions(-) diff --git a/base/dates/Dates.jl b/base/dates/Dates.jl index 41febb4e73def..d99166d0430ce 100644 --- a/base/dates/Dates.jl +++ b/base/dates/Dates.jl @@ -20,12 +20,14 @@ include("io.jl") export Period, DatePeriod, TimePeriod, Year, Month, Week, Day, Hour, Minute, Second, Millisecond, - TimeZone, UTC, TimeType, DateTime, Date, + Microsecond, Nanosecond, + TimeZone, UTC, TimeType, DateTime, Date, Time, # periods.jl canonicalize, # accessors.jl yearmonthday, yearmonth, monthday, year, month, week, day, hour, minute, second, millisecond, dayofmonth, + microsecond, nanosecond, # query.jl dayofweek, isleapyear, daysinmonth, daysinyear, dayofyear, dayname, dayabbr, dayofweekofmonth, daysofweekinmonth, monthname, monthabbr, diff --git a/base/dates/accessors.jl b/base/dates/accessors.jl index 71441a1e6fe6c..f3e9b6981f304 100644 --- a/base/dates/accessors.jl +++ b/base/dates/accessors.jl @@ -42,6 +42,7 @@ end # Accessor functions value(dt::TimeType) = dt.instant.periods.value +value(t::Time) = t.instant.value days(dt::Date) = value(dt) days(dt::DateTime) = fld(value(dt),86400000) year(dt::TimeType) = year(days(dt)) @@ -52,6 +53,12 @@ hour(dt::DateTime) = mod(fld(value(dt),3600000),24) minute(dt::DateTime) = mod(fld(value(dt),60000),60) second(dt::DateTime) = mod(fld(value(dt),1000),60) millisecond(dt::DateTime) = mod(value(dt),1000) +hour(t::Time) = mod(fld(value(t),3600000000000),Int64(24)) +minute(t::Time) = mod(fld(value(t),60000000000),Int64(60)) +second(t::Time) = mod(fld(value(t),1000000000),Int64(60)) +millisecond(t::Time) = mod(fld(value(t),Int64(1000000)),Int64(1000)) +microsecond(t::Time) = mod(fld(value(t),Int64(1000)),Int64(1000)) +nanosecond(t::Time) = mod(value(t),Int64(1000)) dayofmonth(dt::TimeType) = day(dt) @@ -122,3 +129,14 @@ for parts in (["year", "month"], ["month", "day"], ["year", "month", "day"]) """ $func(dt::TimeType) end end + +for func in (:hour, :minute, :second, :millisecond, :microsecond, :nanosecond) + name = string(func) + @eval begin + @doc """ + $($name)(t::Time) -> Int64 + + The $($name) of a `Time` as an `Int64`. + """ $func(t::Time) + end +end diff --git a/base/dates/adjusters.jl b/base/dates/adjusters.jl index 68929ed25e8d6..6e41e6b00eeff 100644 --- a/base/dates/adjusters.jl +++ b/base/dates/adjusters.jl @@ -13,6 +13,13 @@ Base.trunc(dt::DateTime, p::Type{Minute}) = dt - Second(dt) - Millisecond(dt) Base.trunc(dt::DateTime, p::Type{Second}) = dt - Millisecond(dt) Base.trunc(dt::DateTime, p::Type{Millisecond}) = dt +Base.trunc(t::Time, p::Type{Hour}) = Time(Hour(t)) +Base.trunc(t::Time, p::Type{Minute}) = Time(Hour(t), Minute(t)) +Base.trunc(t::Time, p::Type{Second}) = Time(Hour(t), Minute(t), Second(t)) +Base.trunc(t::Time, p::Type{Millisecond}) = t - Microsecond(t) - Nanosecond(t) +Base.trunc(t::Time, p::Type{Microsecond}) = t - Nanosecond(t) +Base.trunc(t::Time, p::Type{Nanosecond}) = t + """ trunc(dt::TimeType, ::Type{Period}) -> TimeType @@ -184,6 +191,33 @@ function DateTime(func::Function, y, m, d, h, mi, s; step::Period=Millisecond(1) return adjust(DateFunction(func, negate, DateTime(y)), DateTime(y, m, d, h, mi, s), step, limit) end +""" + Time(f::Function, h[, mi, s, ms, us]; step=Second(1), negate=false, limit=10000) -> Time + +Create a `Time` through the adjuster API. The starting point will be constructed from the +provided `h, mi, s, ms, us` arguments, and will be adjusted until `f::Function` returns `true`. The step +size in adjusting can be provided manually through the `step` keyword. If `negate=true`, +then the adjusting will stop when `f::Function` returns `false` instead of `true`. `limit` +provides a limit to the max number of iterations the adjustment API will pursue before +throwing an error (in the case that `f::Function` is never satisfied). Note that the default step +will adjust to allow for greater precision for the given arguments; i.e. if hour, minute, and second +arguments are provided, the default step will be `Millisecond(1)` instead of `Second(1)`. +""" +Time(::Function, args...) + +function Time(func::Function, h, mi=0; step::Period=Second(1), negate::Bool=false, limit::Int=10000) + return adjust(DateFunction(func, negate, Time(h, mi)), Time(h, mi), step, limit) +end +function Time(func::Function, h, mi, s; step::Period=Millisecond(1), negate::Bool=false, limit::Int=10000) + return adjust(DateFunction(func, negate, Time(h, mi, s)), Time(h, mi, s), step, limit) +end +function Time(func::Function, h, mi, s, ms; step::Period=Microsecond(1), negate::Bool=false, limit::Int=10000) + return adjust(DateFunction(func, negate,Time(h, mi, s, ms)),Time(h, mi, s, ms), step, limit) +end +function Time(func::Function, h, mi, s, ms, us; step::Period=Nanosecond(1), negate::Bool=false, limit::Int=10000) + return adjust(DateFunction(func, negate, Time(h, mi, s, ms, us)), Time(h, mi, s, ms, us), step, limit) +end + # Return the next TimeType that falls on dow ISDAYOFWEEK = Dict(Mon => DateFunction(ismonday, false, Date(0)), Tue => DateFunction(istuesday, false, Date(0)), diff --git a/base/dates/arithmetic.jl b/base/dates/arithmetic.jl index 72b5313cc004b..845268f19e27a 100644 --- a/base/dates/arithmetic.jl +++ b/base/dates/arithmetic.jl @@ -8,6 +8,21 @@ (+)(x::TimeType) = x (-){T<:TimeType}(x::T,y::T) = x.instant - y.instant +# Date-Time arithmetic +""" + dt::Date + t::Time -> DateTime + +The addition of a `Date` with a `Time` produces a `DateTime`. The hour, minute, second, and millisecond parts of +the `Time` are used along with the year, month, and day of the `Date` to create the new `DateTime`. +Non-zero microseconds or nanoseconds in the `Time` type will result in an `InexactError` being thrown. +""" +function (+)(dt::Date, t::Time) + (microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError()) + y, m, d = yearmonthday(dt) + return DateTime(y, m, d, hour(t), minute(t), second(t), millisecond(t)) +end +(+)(t::Time, dt::Date) = dt + t + # TimeType-Year arithmetic function (+)(dt::DateTime,y::Year) oy,m,d = yearmonthday(dt); ny = oy+value(y); ld = daysinmonth(ny,m) @@ -57,14 +72,16 @@ function (-)(dt::Date,z::Month) mm = monthwrap(m,-value(z)); ld = daysinmonth(ny,mm) return Date(ny,mm,d <= ld ? d : ld) end -(+)(x::Date,y::Week) = return Date(UTD(value(x) + 7*value(y))) -(-)(x::Date,y::Week) = return Date(UTD(value(x) - 7*value(y))) -(+)(x::Date,y::Day) = return Date(UTD(value(x) + value(y))) -(-)(x::Date,y::Day) = return Date(UTD(value(x) - value(y))) -(+)(x::DateTime,y::Period) = return DateTime(UTM(value(x)+toms(y))) -(-)(x::DateTime,y::Period) = return DateTime(UTM(value(x)-toms(y))) -(+)(y::Period,x::TimeType) = x + y -(-)(y::Period,x::TimeType) = x - y +(+)(x::Date, y::Week) = return Date(UTD(value(x) + 7*value(y))) +(-)(x::Date, y::Week) = return Date(UTD(value(x) - 7*value(y))) +(+)(x::Date, y::Day) = return Date(UTD(value(x) + value(y))) +(-)(x::Date, y::Day) = return Date(UTD(value(x) - value(y))) +(+)(x::DateTime, y::Period) = return DateTime(UTM(value(x) + toms(y))) +(-)(x::DateTime, y::Period) = return DateTime(UTM(value(x) - toms(y))) +(+)(x::Time, y::TimePeriod) = return Time(Nanosecond(value(x) + tons(y))) +(-)(x::Time, y::TimePeriod) = return Time(Nanosecond(value(x) - tons(y))) +(+)(y::Period, x::TimeType) = x + y +(-)(y::Period, x::TimeType) = x - y for op in (:+, :-) @eval begin diff --git a/base/dates/conversions.jl b/base/dates/conversions.jl index 7204fd8b18ec6..22614f355f94d 100644 --- a/base/dates/conversions.jl +++ b/base/dates/conversions.jl @@ -19,8 +19,18 @@ the new `DateTime` are assumed to be zero. """ DateTime(dt::TimeType) = convert(DateTime,dt) -Base.convert(::Type{DateTime},dt::Date) = DateTime(UTM(value(dt)*86400000)) -Base.convert(::Type{Date},dt::DateTime) = Date(UTD(days(dt))) +""" + Time(dt::DateTime) -> Time + +Converts a `DateTime` to a `Time`. The hour, minute, second, and millisecond parts of +the `DateTime` are used to create the new `Time`. Microsecond and nanoseconds are zero by default. +""" +Time(dt::DateTime) = convert(Time, dt) + +Base.convert(::Type{DateTime}, dt::Date) = DateTime(UTM(value(dt)*86400000)) +Base.convert(::Type{Date}, dt::DateTime) = Date(UTD(days(dt))) +Base.convert(::Type{Time}, dt::DateTime) = Time(Nanosecond((value(dt) % 86400000) * 1000000)) + """ convert{T<:Real}(::Type{T}, dt::DateTime) -> T Converts a DateTime value `dt` to a number of type `T`. The returned value corresponds to the number of Rata Die milliseconds since epoch. diff --git a/base/dates/io.jl b/base/dates/io.jl index bdd1d60a615a1..e6f7950eb77ae 100644 --- a/base/dates/io.jl +++ b/base/dates/io.jl @@ -2,26 +2,41 @@ # TODO: optimize this function Base.string(dt::DateTime) - y,m,d = yearmonthday(days(dt)) - h,mi,s = hour(dt),minute(dt),second(dt) - yy = y < 0 ? @sprintf("%05i",y) : lpad(y,4,"0") - mm = lpad(m,2,"0") - dd = lpad(d,2,"0") - hh = lpad(h,2,"0") - mii = lpad(mi,2,"0") - ss = lpad(s,2,"0") - ms = millisecond(dt) == 0 ? "" : string(millisecond(dt)/1000.0)[2:end] - return "$yy-$mm-$(dd)T$hh:$mii:$ss$(ms)" + y, m, d = yearmonthday(days(dt)) + h, mi, s = hour(dt), minute(dt), second(dt) + yy = y < 0 ? @sprintf("%05i", y) : lpad(y, 4, "0") + mm = lpad(m, 2, "0") + dd = lpad(d, 2, "0") + hh = lpad(h, 2, "0") + mii = lpad(mi, 2, "0") + ss = lpad(s, 2, "0") + ms = millisecond(dt) == 0 ? "" : string(millisecond(dt) / 1000.0)[2:end] + return "$yy-$mm-$(dd)T$hh:$mii:$ss$ms" end -Base.show(io::IO,x::DateTime) = print(io,string(x)) + +Base.show(io::IO, x::DateTime) = print(io, string(x)) + function Base.string(dt::Date) - y,m,d = yearmonthday(value(dt)) - yy = y < 0 ? @sprintf("%05i",y) : lpad(y,4,"0") - mm = lpad(m,2,"0") - dd = lpad(d,2,"0") + y, m, d = yearmonthday(value(dt)) + yy = y < 0 ? @sprintf("%05i", y) : lpad(y, 4, "0") + mm = lpad(m, 2, "0") + dd = lpad(d, 2, "0") return "$yy-$mm-$dd" end -Base.show(io::IO,x::Date) = print(io,string(x)) + +Base.show(io::IO, x::Date) = print(io, string(x)) + +function Base.string(t::Time) + h, mi, s = hour(t), minute(t), second(t) + hh = lpad(h, 2, "0") + mii = lpad(mi, 2, "0") + ss = lpad(s, 2, "0") + nss = tons(Millisecond(t)) + tons(Microsecond(t)) + tons(Nanosecond(t)) + ns = nss == 0 ? "" : rstrip(@sprintf("%.9f", nss / 1e+9)[2:end], '0') + return "$hh:$mii:$ss$ns" +end + +Base.show(io::IO, x::Time) = print(io, string(x)) ### Parsing const english = Dict{String,Int}("january"=>1,"february"=>2,"march"=>3,"april"=>4, diff --git a/base/dates/periods.jl b/base/dates/periods.jl index 6b48c248405e9..935a8363cfc45 100644 --- a/base/dates/periods.jl +++ b/base/dates/periods.jl @@ -6,7 +6,7 @@ value(x::Period) = x.value # The default constructors for Periods work well in almost all cases # P(x) = new((convert(Int64,x)) # The following definitions are for Period-specific safety -for period in (:Year, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond) +for period in (:Year, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond, :Microsecond, :Nanosecond) period_str = string(period) accessor_str = lowercase(period_str) # Convenience method for show() @@ -16,17 +16,19 @@ for period in (:Year, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond # AbstractString parsing (mainly for IO code) @eval $period(x::AbstractString) = $period(Base.parse(Int64,x)) # Period accessors - typ_str = period in (:Hour, :Minute, :Second, :Millisecond) ? "DateTime" : "TimeType" - description = typ_str == "TimeType" ? "`Date` or `DateTime`" : "`$typ_str`" - reference = period == :Week ? " For details see [`$accessor_str(::$typ_str)`](@ref)." : "" + typs = period in (:Microsecond, :Nanosecond) ? ["Time"] : + period in (:Hour, :Minute, :Second, :Millisecond) ? ["Time", "DateTime"] : ["Date","DateTime"] + reference = period == :Week ? " For details see [`$accessor_str(::Union{Date, DateTime})`](@ref)." : "" + for typ_str in typs + @eval begin + @doc """ + $($period_str)(dt::$($typ_str)) -> $($period_str) + + The $($accessor_str) part of a $($typ_str) as a `$($period_str)`.$($reference) + """ $period(dt::$(Symbol(typ_str))) = $period($(Symbol(accessor_str))(dt)) + end + end @eval begin - @doc """ - $($period_str)(dt::$($typ_str)) -> $($period_str) - - The $($accessor_str) part of a $($description) as a `$($period_str)`.$($reference) - """ -> - $period(dt::$(Symbol(typ_str))) = $period($(Symbol(accessor_str))(dt)) - @doc """ $($period_str)(v) @@ -117,15 +119,28 @@ periodisless(::Period,::Hour) = false periodisless(::Minute,::Hour) = true periodisless(::Second,::Hour) = true periodisless(::Millisecond,::Hour) = true +periodisless(::Microsecond,::Hour) = true +periodisless(::Nanosecond,::Hour) = true periodisless(::Period,::Minute) = false periodisless(::Second,::Minute) = true periodisless(::Millisecond,::Minute) = true +periodisless(::Microsecond,::Minute) = true +periodisless(::Nanosecond,::Minute) = true periodisless(::Period,::Second) = false periodisless(::Millisecond,::Second) = true +periodisless(::Microsecond,::Second) = true +periodisless(::Nanosecond,::Second) = true periodisless(::Period,::Millisecond) = false +periodisless(::Microsecond,::Millisecond) = true +periodisless(::Nanosecond,::Millisecond) = true +periodisless(::Period,::Microsecond) = false +periodisless(::Nanosecond,::Microsecond) = true +periodisless(::Period,::Nanosecond) = false # return (next coarser period, conversion factor): coarserperiod{P<:Period}(::Type{P}) = (P,1) +coarserperiod(::Type{Nanosecond}) = (Microsecond,1000) +coarserperiod(::Type{Microsecond}) = (Millisecond,1000) coarserperiod(::Type{Millisecond}) = (Second,1000) coarserperiod(::Type{Second}) = (Minute,60) coarserperiod(::Type{Minute}) = (Hour,60) @@ -202,6 +217,9 @@ julia> Dates.CompoundPeriod(Dates.Minute(50000)) """ CompoundPeriod{P<:Period}(p::Vector{P}) = CompoundPeriod(Array{Period}(p)) +CompoundPeriod(t::Time) = CompoundPeriod(Period[Hour(t), Minute(t), Second(t), Millisecond(t), + Microsecond(t), Nanosecond(t)]) + CompoundPeriod(p::Period...) = CompoundPeriod(Period[p...]) @@ -382,7 +400,7 @@ end # Fixed-value Periods (periods corresponding to a well-defined time interval, # as opposed to variable calendar intervals like Year). -typealias FixedPeriod Union{Week,Day,Hour,Minute,Second,Millisecond} +typealias FixedPeriod Union{Week,Day,Hour,Minute,Second,Millisecond,Microsecond,Nanosecond} # like div but throw an error if remainder is nonzero function divexact(x,y) @@ -392,10 +410,10 @@ function divexact(x,y) end # FixedPeriod conversions and promotion rules -const fixedperiod_conversions = [(Week,7),(Day,24),(Hour,60),(Minute,60),(Second,1000),(Millisecond,1)] +const fixedperiod_conversions = [(Week,7),(Day,24),(Hour,60),(Minute,60),(Second,1000),(Millisecond,1000),(Microsecond,1000),(Nanosecond,1)] for i = 1:length(fixedperiod_conversions) (T,n) = fixedperiod_conversions[i] - N = 1 + N = Int64(1) for j = i-1:-1:1 # less-precise periods (Tc,nc) = fixedperiod_conversions[j] N *= nc @@ -432,6 +450,8 @@ Base.promote_rule(::Type{Year}, ::Type{Month}) = Month Base.isless{T<:OtherPeriod,S<:OtherPeriod}(x::T,y::S) = isless(promote(x,y)...) # truncating conversions to milliseconds and days: +toms(c::Nanosecond) = div(value(c), 1000000) +toms(c::Microsecond) = div(value(c), 1000) toms(c::Millisecond) = value(c) toms(c::Second) = 1000*value(c) toms(c::Minute) = 60000*value(c) @@ -440,7 +460,10 @@ toms(c::Day) = 86400000*value(c) toms(c::Week) = 604800000*value(c) toms(c::Month) = 86400000.0*30.436875*value(c) toms(c::Year) = 86400000.0*365.2425*value(c) -toms(c::CompoundPeriod) = isempty(c.periods)?0.0 : Float64(sum(toms,c.periods)) +toms(c::CompoundPeriod) = isempty(c.periods)? 0.0 : Float64(sum(toms, c.periods)) +tons(x) = toms(x) * 1000000 +tons(x::Microsecond) = value(x) * 1000 +tons(x::Nanosecond) = value(x) days(c::Millisecond) = div(value(c),86400000) days(c::Second) = div(value(c),86400) days(c::Minute) = div(value(c),1440) diff --git a/base/dates/ranges.jl b/base/dates/ranges.jl index 9ea398869f049..17eed2054784d 100644 --- a/base/dates/ranges.jl +++ b/base/dates/ranges.jl @@ -4,10 +4,12 @@ # Override default step; otherwise it would be Millisecond(1) Base.colon{T<:DateTime}(start::T, stop::T) = StepRange(start, Day(1), stop) +Base.colon{T<:Time}(start::T, stop::T) = StepRange(start, Second(1), stop) # Given a start and end date, how many steps/periods are in between -guess(a::DateTime,b::DateTime,c) = floor(Int64,(Int128(b) - Int128(a))/toms(c)) -guess(a::Date,b::Date,c) = Int64(div(Int64(b - a),days(c))) +guess(a::DateTime,b::DateTime,c) = floor(Int64,(Int128(b) - Int128(a)) / toms(c)) +guess(a::Date,b::Date,c) = Int64(div(Int64(b - a), days(c))) +len(a::Time,b::Time,c) = Int64(div(Int64(b - a), tons(c))) function len(a,b,c) lo, hi, st = min(a,b), max(a,b), abs(c) i = guess(a,b,c)-1 diff --git a/base/dates/types.jl b/base/dates/types.jl index c053b19724d58..db85b10cdd7ad 100644 --- a/base/dates/types.jl +++ b/base/dates/types.jl @@ -12,6 +12,8 @@ abstract AbstractTime Minute Second Millisecond + Microsecond + Nanosecond `Period` types represent discrete, human representations of time. """ @@ -25,7 +27,7 @@ for T in (:Year,:Month,:Week,:Day) $T(v::Number) = new(v) end end -for T in (:Hour,:Minute,:Second,:Millisecond) +for T in (:Hour,:Minute,:Second,:Millisecond,:Microsecond,:Nanosecond) @eval immutable $T <: TimePeriod value::Int64 $T(v::Number) = new(v) @@ -41,6 +43,8 @@ end Minute(v) Second(v) Millisecond(v) + Microsecond(v) + Nanosecond(v) Construct a `Period` type with the given `v` value. Input must be losslessly convertible to an `Int64`. @@ -111,10 +115,15 @@ immutable Date <: TimeType Date(instant::UTInstant{Day}) = new(instant) end -# Fallback constructors -_c(x) = convert(Int64,x) -DateTime(y,m=1,d=1,h=0,mi=0,s=0,ms=0) = DateTime(_c(y),_c(m),_c(d),_c(h),_c(mi),_c(s),_c(ms)) -Date(y,m=1,d=1) = Date(_c(y),_c(m),_c(d)) +""" + Time + +`Time` wraps a `Nanosecond` and represents a specific moment in a 24-hour day. +""" +immutable Time <: TimeType + instant::Nanosecond + Time(instant::Nanosecond) = new(instant) +end # Convert y,m,d to # of Rata Die days # Works by shifting the beginning of the year to March 1, @@ -165,15 +174,36 @@ function Date(y::Int64,m::Int64=1,d::Int64=1) return Date(UTD(totaldays(y,m,d))) end +""" + Time(h, [mi, s, ms, us, ns]) -> Time + +Construct a `Time` type by parts. Arguments must be convertible to `Int64`. +""" +function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0) + -1 < h < 24 || throw(ArgumentError("Hour: $h out of range (0:23)")) + -1 < mi < 60 || throw(ArgumentError("Minute: $mi out of range (0:59)")) + -1 < s < 60 || throw(ArgumentError("Second: $s out of range (0:59)")) + -1 < ms < 1000 || throw(ArgumentError("Millisecond: $ms out of range (0:999)")) + -1 < us < 1000 || throw(ArgumentError("Microsecond: $us out of range (0:999)")) + -1 < ns < 1000 || throw(ArgumentError("Nanosecond: $ns out of range (0:999)")) + return Time(Nanosecond(ns + 1000us + 1000000ms + 1000000000s + 60000000000mi + 3600000000000h)) +end + # Convenience constructors from Periods -function DateTime(y::Year,m::Month=Month(1),d::Day=Day(1), - h::Hour=Hour(0),mi::Minute=Minute(0), - s::Second=Second(0),ms::Millisecond=Millisecond(0)) - return DateTime(value(y),value(m),value(d), - value(h),value(mi),value(s),value(ms)) +function DateTime(y::Year, m::Month=Month(1), d::Day=Day(1), + h::Hour=Hour(0), mi::Minute=Minute(0), + s::Second=Second(0), ms::Millisecond=Millisecond(0)) + return DateTime(value(y), value(m), value(d), + value(h), value(mi), value(s), value(ms)) end -Date(y::Year,m::Month=Month(1),d::Day=Day(1)) = Date(value(y),value(m),value(d)) +Date(y::Year, m::Month=Month(1), d::Day=Day(1)) = Date(value(y), value(m), value(d)) + +function Time(h::Hour, mi::Minute=Minute(0), s::Second=Second(0), + ms::Millisecond=Millisecond(0), + us::Microsecond=Microsecond(0), ns::Nanosecond=Nanosecond(0)) + return Time(value(h), value(mi), value(s), value(ms), value(us), value(ns)) +end # To allow any order/combination of Periods @@ -214,6 +244,31 @@ function Date(periods::Period...) return Date(y,m,d) end +""" + Time(period::TimePeriod...) -> Time + +Construct a `Time` type by `Period` type parts. Arguments may be in any order. `Time` parts +not provided will default to the value of `Dates.default(period)`. +""" +function Time(periods::TimePeriod...) + h = Hour(0); mi = Minute(0); s = Second(0) + ms = Millisecond(0); us = Microsecond(0); ns = Nanosecond(0) + for p in periods + isa(p, Hour) && (h = p::Hour) + isa(p, Minute) && (mi = p::Minute) + isa(p, Second) && (s = p::Second) + isa(p, Millisecond) && (ms = p::Millisecond) + isa(p, Microsecond) && (us = p::Microsecond) + isa(p, Nanosecond) && (ns = p::Nanosecond) + end + return Time(h, mi, s, ms, us, ns) +end + +# Fallback constructors +DateTime(y,m=1,d=1,h=0,mi=0,s=0,ms=0) = DateTime(Int64(y), Int64(m), Int64(d), Int64(h), Int64(mi), Int64(s), Int64(ms)) +Date(y,m=1,d=1) = Date(Int64(y), Int64(m), Int64(d)) +Time(h,mi=0,s=0,ms=0,us=0,ns=0) = Time(Int64(h), Int64(mi), Int64(s), Int64(ms), Int64(us), Int64(ns)) + # Traits, Equality Base.isfinite{T<:TimeType}(::Union{Type{T},T}) = true calendar(dt::DateTime) = ISOCalendar @@ -222,27 +277,36 @@ calendar(dt::Date) = ISOCalendar """ eps(::DateTime) -> Millisecond eps(::Date) -> Day + eps(::Time) -> Nanosecond -Returns `Millisecond(1)` for `DateTime` values and `Day(1)` for `Date` values. +Returns `Millisecond(1)` for `DateTime` values, `Day(1)` for `Date` values, and `Nanosecond(1)` for `Time` values. """ Base.eps Base.eps(dt::DateTime) = Millisecond(1) Base.eps(dt::Date) = Day(1) - -Base.typemax(::Union{DateTime,Type{DateTime}}) = DateTime(146138512,12,31,23,59,59) -Base.typemin(::Union{DateTime,Type{DateTime}}) = DateTime(-146138511,1,1,0,0,0) -Base.typemax(::Union{Date,Type{Date}}) = Date(252522163911149,12,31) -Base.typemin(::Union{Date,Type{Date}}) = Date(-252522163911150,1,1) +Base.eps(t::Time) = Nanosecond(1) + +Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(146138512, 12, 31, 23, 59, 59) +Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-146138511, 1, 1, 0, 0, 0) +Base.typemax(::Union{Date, Type{Date}}) = Date(252522163911149, 12, 31) +Base.typemin(::Union{Date, Type{Date}}) = Date(-252522163911150, 1, 1) +Base.typemax(::Union{Time, Type{Time}}) = Time(23, 59, 59, 999, 999, 999) +Base.typemin(::Union{Time, Type{Time}}) = Time(0) # Date-DateTime promotion, isless, == Base.eltype{T<:Period}(::Type{T}) = T -Base.promote_rule(::Type{Date},x::Type{DateTime}) = DateTime -Base.isless(x::Date,y::Date) = isless(value(x),value(y)) -Base.isless(x::DateTime,y::DateTime) = isless(value(x),value(y)) +Base.promote_rule(::Type{Date}, x::Type{DateTime}) = DateTime +Base.isless{T<:TimeType}(x::T, y::T) = isless(value(x), value(y)) Base.isless(x::TimeType,y::TimeType) = isless(Base.promote_noncircular(x,y)...) -==(x::TimeType,y::TimeType) = ===(promote(x,y)...) +=={T<:TimeType}(x::T, y::T) = ==(value(x), value(y)) +function ==(a::Time,b::Time) + return hour(a) == hour(b) && minute(a) == minute(b) && + second(a) == second(b) && millisecond(a) == millisecond(b) && + microsecond(a) == microsecond(b) && nanosecond(a) == nanosecond(b) +end +==(x::TimeType, y::TimeType) = ===(promote(x, y)...) -import Base: sleep,Timer,timedwait +import Base: sleep, Timer, timedwait sleep(time::Period) = sleep(toms(time) / 1000) -Timer(time::Period, repeat::Period=Second(0)) = Timer(toms(time) / 1000,toms(repeat) / 1000) +Timer(time::Period, repeat::Period=Second(0)) = Timer(toms(time) / 1000, toms(repeat) / 1000) timedwait(testcb::Function, time::Period) = timedwait(testcb, toms(time) / 1000) diff --git a/doc/src/stdlib/dates.md b/doc/src/stdlib/dates.md index ec90098f754e4..49c0bdc948c64 100644 --- a/doc/src/stdlib/dates.md +++ b/doc/src/stdlib/dates.md @@ -10,6 +10,7 @@ Base.Dates.UTInstant Base.Dates.TimeType Base.Dates.DateTime Base.Dates.Date +Base.Dates.Time ``` ## Dates Functions @@ -35,6 +36,10 @@ Base.Dates.Date(::Function, ::Any, ::Any, ::Any) Base.Dates.Date(::Base.Dates.TimeType) Base.Dates.Date(::AbstractString, ::AbstractString) Base.Dates.Date(::AbstractString, ::Base.Dates.DateFormat) +Base.Dates.Time(::Int64::Int64, ::Int64, ::Int64, ::Int64, ::Int64) +Base.Dates.Time(::Base.Dates.TimePeriod...) +Base.Dates.Time(::Function, ::Any...) +Base.Dates.Time(::Base.Dates.DateTime) Base.Dates.now() Base.Dates.now(::Type{Base.Dates.UTC}) Base.eps @@ -51,6 +56,8 @@ Base.Dates.hour Base.Dates.minute Base.Dates.second Base.Dates.millisecond +Base.Dates.microsecond +Base.Dates.nanosecond Base.Dates.Year(::Base.Dates.TimeType) Base.Dates.Month(::Base.Dates.TimeType) Base.Dates.Week(::Base.Dates.TimeType) @@ -59,6 +66,8 @@ Base.Dates.Hour(::DateTime) Base.Dates.Minute(::DateTime) Base.Dates.Second(::DateTime) Base.Dates.Millisecond(::DateTime) +Base.Dates.Microsecond(::Dates.Time) +Base.Dates.Nanosecond(::Dates.Time) Base.Dates.yearmonth Base.Dates.monthday Base.Dates.yearmonthday diff --git a/test/dates/accessors.jl b/test/dates/accessors.jl index a2e91199d07d3..e61d7fa38571c 100644 --- a/test/dates/accessors.jl +++ b/test/dates/accessors.jl @@ -40,8 +40,7 @@ # test_dates(-10000,10000) takes about 15 seconds # test_dates(year(typemin(Date)),year(typemax(Date))) is full range # and would take.......a really long time -function test_dates1(from,to) - y = m = d = 0 +let from=0, to=2100, y=0, m=0, d=0 test_day = Dates.totaldays(from,1,1) for y in from:to for m = 1:12 @@ -54,11 +53,9 @@ function test_dates1(from,to) end end end -test_dates1(0,2100) # Test year, month, day, hour, minute -function test_dates1() - y = m = d = h = mi = 0 +let y=0, m=0, d=0, h=0, mi=0 for m = 1:12 for d = 1:Dates.daysinmonth(y,m) for h = 0:23 @@ -71,18 +68,14 @@ function test_dates1() @test h == Dates.hour(dt) @test mi == Dates.minute(dt) @test (m,d) == Dates.monthday(dt) - #@test s == Dates.second(dt) - #@test ms == Dates.millisecond(dt) end end end end end -test_dates1() # Test second, millisecond -function test_dates2() - y = m = d = h = mi = s = ms = 0 +let y=0, m=0, d=0, h=0, mi=0, s=0, ms=0 for y in [-2013,-1,0,1,2013] for m in [1,6,12] for d in [1,15,Dates.daysinmonth(y,m)] @@ -103,10 +96,8 @@ function test_dates2() end end end -test_dates2() -function test_dates2(from,to) - y = m = d = 0 +let from=0, to=2100, y=0, m=0, d=0 for y in from:to for m = 1:12 for d = 1:Dates.daysinmonth(y,m) @@ -118,7 +109,20 @@ function test_dates2(from,to) end end end -test_dates2(0,2100) + +# test hour, minute, second +let h=0, mi=0, s=0, ms=0, us=0, ns=0 + for h = (0,23), mi = (0,59), s = (0,59), + ms in (0,1,500,999), us in (0,1,500,999), ns in (0,1,500,999) + t = Dates.Time(h, mi, s, ms, us, ns) + @test h == Dates.hour(t) + @test mi == Dates.minute(t) + @test s == Dates.second(t) + @test ms == Dates.millisecond(t) + @test us == Dates.microsecond(t) + @test ns == Dates.nanosecond(t) + end +end # week function # Tests from https://en.wikipedia.org/wiki/ISO_week_date @@ -193,3 +197,12 @@ dr = [a,a,a,a,a,a,a,a,a,a] @test Dates.minute.(dr) == repmat([0],10) @test Dates.second.(dr) == repmat([0],10) @test Dates.millisecond.(dr) == repmat([0],10) + +b = Dates.Time(1,2,3,4,5,6) +tr = [b,b,b,b,b,b,b,b,b,b] +@test Dates.hour.(tr) == repmat([1],10) +@test Dates.minute.(tr) == repmat([2],10) +@test Dates.second.(tr) == repmat([3],10) +@test Dates.millisecond.(tr) == repmat([4],10) +@test Dates.microsecond.(tr) == repmat([5],10) +@test Dates.nanosecond.(tr) == repmat([6],10) \ No newline at end of file diff --git a/test/dates/adjusters.jl b/test/dates/adjusters.jl index 1448b16dba3e4..5e2d2f8f6dc8d 100644 --- a/test/dates/adjusters.jl +++ b/test/dates/adjusters.jl @@ -13,6 +13,13 @@ dt = Dates.DateTime(2012,12,21,16,30,20,200) @test trunc(dt,Dates.Minute) == Dates.DateTime(2012,12,21,16,30) @test trunc(dt,Dates.Second) == Dates.DateTime(2012,12,21,16,30,20) @test trunc(dt,Dates.Millisecond) == Dates.DateTime(2012,12,21,16,30,20,200) +t = Dates.Time(1,2,3,4,5,6) +@test trunc(t,Dates.Hour) == Dates.Time(1) +@test trunc(t,Dates.Minute) == Dates.Time(1,2) +@test trunc(t,Dates.Second) == Dates.Time(1,2,3) +@test trunc(t,Dates.Millisecond) == Dates.Time(1,2,3,4) +@test trunc(t,Dates.Microsecond) == Dates.Time(1,2,3,4,5) +@test trunc(t,Dates.Nanosecond) == Dates.Time(1,2,3,4,5,6) # Date functions Jan = Dates.DateTime(2013,1,1) #Tuesday @@ -455,3 +462,11 @@ end) == 251 end return sum == 15 end) == 15 # On average, there's one of those months every year + +r = Dates.Time(x->Dates.second(x) == 5, 1) +@test r == Dates.Time(1,0,5) + +r = filter(x->Dates.second(x) == 5, Dates.Time(0):Dates.Time(10)) +@test length(r) == 600 +@test first(r) == Dates.Time(0,0,5) +@test last(r) == Dates.Time(9,59,5) \ No newline at end of file diff --git a/test/dates/arithmetic.jl b/test/dates/arithmetic.jl index d32adac047a81..961b2f031406f 100644 --- a/test/dates/arithmetic.jl +++ b/test/dates/arithmetic.jl @@ -1,5 +1,10 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license +# Time arithmetic +a = Dates.Time(23,59,59) +b = Dates.Time(11,59,59) +@test Dates.CompoundPeriod(a - b) == Dates.Hour(12) + # Dates.DateTime arithmetic a = Dates.DateTime(2013,1,1,0,0,0,1) b = Dates.DateTime(2013,1,1,0,0,0,0) @@ -264,6 +269,46 @@ dt = Dates.Date(1999,12,27) @test dt - Dates.Day(100) == Dates.Date(1999,9,18) @test dt - Dates.Day(1000) == Dates.Date(1997,4,1) +# Test Time-TimePeriod arithmetic +t = Dates.Time(0) +@test t + Dates.Hour(1) == Dates.Time(1) +@test t - Dates.Hour(1) == Dates.Time(23) +@test t - Dates.Nanosecond(1) == Dates.Time(23,59,59,999,999,999) +@test t + Dates.Nanosecond(-1) == Dates.Time(23,59,59,999,999,999) +@test t + Dates.Hour(24) == t +@test t + Dates.Nanosecond(86400000000000) == t +@test t - Dates.Nanosecond(86400000000000) == t +@test t + Dates.Minute(1) == Dates.Time(0,1) +@test t + Dates.Second(1) == Dates.Time(0,0,1) +@test t + Dates.Millisecond(1) == Dates.Time(0,0,0,1) +@test t + Dates.Microsecond(1) == Dates.Time(0,0,0,0,1) +@test_throws MethodError t + Dates.Day(1) + +# Vectorized Time arithmetic +a = Dates.Time(1,1,1) +dr = [a,a,a,a,a,a,a,a,a,a] +b = a + Dates.Hour(1) +@test dr .+ Dates.Hour(1) == repmat([b],10) +b = a + Dates.Second(1) +@test dr .+ Dates.Second(1) == repmat([b],10) +b = a + Dates.Millisecond(1) +@test dr .+ Dates.Millisecond(1) == repmat([b],10) +b = a + Dates.Microsecond(1) +@test dr .+ Dates.Microsecond(1) == repmat([b],10) +b = a + Dates.Nanosecond(1) +@test dr .+ Dates.Nanosecond(1) == repmat([b],10) + +b = a - Dates.Hour(1) +@test dr .- Dates.Hour(1) == repmat([b],10) +b = a - Dates.Second(1) +@test dr .- Dates.Second(1) == repmat([b],10) +b = a - Dates.Millisecond(1) +@test dr .- Dates.Millisecond(1) == repmat([b],10) +b = a - Dates.Microsecond(1) +@test dr .- Dates.Microsecond(1) == repmat([b],10) +b = a - Dates.Nanosecond(1) +@test dr .- Dates.Nanosecond(1) == repmat([b],10) + # Vectorized arithmetic a = Dates.Date(2014,1,1) dr = [a,a,a,a,a,a,a,a,a,a] @@ -335,6 +380,7 @@ t1 = [Dates.Date(2009,1,1) Dates.Date(2009,1,2) Dates.Date(2009,1,3); Dates.Date t2 = [Dates.Date(2009,1,2) Dates.Date(2009,2,2) Dates.Date(2010,1,3); Dates.Date(2010,2,1) Dates.Date(2009,3,2) Dates.Date(2009,2,4)] t3 = [Dates.DateTime(2009,1,1), Dates.DateTime(2009,1,2), Dates.DateTime(2009,1,3)] t4 = [Dates.DateTime(2009,1,1,0,0,1), Dates.DateTime(2009,1,2,0,1), Dates.DateTime(2009,1,3,1)] +t5 = [Dates.Time(0,0,0) Dates.Time(0,0,1) Dates.Time(0,0,2); Dates.Time(0,1,0) Dates.Time(0,2,0) Dates.Time(0,3,0)] # TimeType, Array{TimeType} @test Dates.Date(2010,1,1) .- t1 == [Dates.Day(365) Dates.Day(364) Dates.Day(363); Dates.Day(334) Dates.Day(333) Dates.Day(332)] @@ -345,6 +391,8 @@ t4 = [Dates.DateTime(2009,1,1,0,0,1), Dates.DateTime(2009,1,2,0,1), Dates.DateTi @test t1 - Dates.Date(2010,1,1) == [Dates.Day(-365) Dates.Day(-364) Dates.Day(-363); Dates.Day(-334) Dates.Day(-333) Dates.Day(-332)] @test Dates.DateTime(2009,1,1) - t3 == [Dates.Millisecond(0), Dates.Millisecond(-86400000), Dates.Millisecond(-172800000)] @test t3 - Dates.DateTime(2009,1,1) == [Dates.Millisecond(0), Dates.Millisecond(86400000), Dates.Millisecond(172800000)] +@test Dates.Time(2) .- t5 == [Dates.Nanosecond(7200000000000) Dates.Nanosecond(7199000000000) Dates.Nanosecond(7198000000000); + Dates.Nanosecond(7140000000000) Dates.Nanosecond(7080000000000) Dates.Nanosecond(7020000000000)] # GeneralPeriod, Array{TimeType} @test Dates.Day(1) .+ t1 == [Dates.Date(2009,1,2) Dates.Date(2009,1,3) Dates.Date(2009,1,4); Dates.Date(2009,2,2) Dates.Date(2009,2,3) Dates.Date(2009,2,4)] @@ -355,6 +403,7 @@ t4 = [Dates.DateTime(2009,1,1,0,0,1), Dates.DateTime(2009,1,2,0,1), Dates.DateTi @test Dates.Hour(1) + t3 == [Dates.DateTime(2009,1,1,1), Dates.DateTime(2009,1,2,1), Dates.DateTime(2009,1,3,1)] @test t1 + Dates.Day(1) == [Dates.Date(2009,1,2) Dates.Date(2009,1,3) Dates.Date(2009,1,4); Dates.Date(2009,2,2) Dates.Date(2009,2,3) Dates.Date(2009,2,4)] @test t3 + Dates.Hour(1) == [Dates.DateTime(2009,1,1,1), Dates.DateTime(2009,1,2,1), Dates.DateTime(2009,1,3,1)] +@test t5 + Dates.Hour(1) == [Dates.Time(1,0,0) Dates.Time(1,0,1) Dates.Time(1,0,2); Dates.Time(1,1,0) Dates.Time(1,2,0) Dates.Time(1,3,0)] @test (Dates.Month(1) + Dates.Day(1)) .+ t1 == [Dates.Date(2009,2,2) Dates.Date(2009,2,3) Dates.Date(2009,2,4); Dates.Date(2009,3,2) Dates.Date(2009,3,3) Dates.Date(2009,3,4)] @test (Dates.Hour(1) + Dates.Minute(1)) .+ t3 == [Dates.DateTime(2009,1,1,1,1), Dates.DateTime(2009,1,2,1,1), Dates.DateTime(2009,1,3,1,1)] diff --git a/test/dates/conversions.jl b/test/dates/conversions.jl index 008156fcd89ad..e35b8d84982c5 100644 --- a/test/dates/conversions.jl +++ b/test/dates/conversions.jl @@ -104,3 +104,11 @@ b = Dates.Date(2000) @test convert(Date,730120.0) == b @test convert(Date,Int32(730120)) == b +dt = Dates.DateTime(2000,1,1,23,59,59,50) +t = Dates.Time(dt) +@test Dates.hour(t) == 23 +@test Dates.minute(t) == 59 +@test Dates.second(t) == 59 +@test Dates.millisecond(t) == 50 +@test Dates.microsecond(t) == 0 +@test Dates.nanosecond(t) == 0 \ No newline at end of file diff --git a/test/dates/io.jl b/test/dates/io.jl index f17ae1a8d8b4f..b5056cb600dac 100644 --- a/test/dates/io.jl +++ b/test/dates/io.jl @@ -16,6 +16,15 @@ @test string(Dates.DateTime(2000,1,1,0,0,0,500)) == "2000-01-01T00:00:00.5" @test string(Dates.DateTime(2000,1,1,0,0,0,998)) == "2000-01-01T00:00:00.998" @test string(Dates.DateTime(2000,1,1,0,0,0,999)) == "2000-01-01T00:00:00.999" +@test string(Dates.Time(0)) == "00:00:00" +@test string(Dates.Time(0,1)) == "00:01:00" +@test string(Dates.Time(0,1,2)) == "00:01:02" +@test string(Dates.Time(0,1,2,3)) == "00:01:02.003" +@test string(Dates.Time(0,1,2,3,4)) == "00:01:02.003004" +@test string(Dates.Time(0,1,2,3,4,5)) == "00:01:02.003004005" +@test string(Dates.Time(0,0,0,0,1)) == "00:00:00.000001" +@test string(Dates.Time(0,0,0,0,0,1)) == "00:00:00.000000001" +@test string(Dates.Time(0,0,0,1)) == "00:00:00.001" # DateTime parsing # Useful reference for different locales: http://library.princeton.edu/departments/tsd/katmandu/reference/months.html diff --git a/test/dates/periods.jl b/test/dates/periods.jl index a51dd9300a1e0..55b35de23e1e9 100644 --- a/test/dates/periods.jl +++ b/test/dates/periods.jl @@ -49,6 +49,8 @@ h = Dates.Hour(1) mi = Dates.Minute(1) s = Dates.Second(1) ms = Dates.Millisecond(1) +us = Dates.Microsecond(1) +ns = Dates.Nanosecond(1) @test Dates.Year(y) == y @test Dates.Month(m) == m @test Dates.Week(w) == w @@ -57,6 +59,8 @@ ms = Dates.Millisecond(1) @test Dates.Minute(mi) == mi @test Dates.Second(s) == s @test Dates.Millisecond(ms) == ms +@test Dates.Microsecond(us) == us +@test Dates.Nanosecond(ns) == ns @test typeof(Int8(y)) <: Int8 @test typeof(UInt8(y)) <: UInt8 @test typeof(Int16(y)) <: Int16 @@ -99,6 +103,8 @@ ms = Dates.Millisecond(1) @test mi == mi @test s == s @test ms == ms +@test us == us +@test ns == ns y2 = Dates.Year(2) @test y < y2 @test y2 > y @@ -147,11 +153,18 @@ y2 = Dates.Year(2) @test typeof(y+mi) <: Dates.CompoundPeriod @test typeof(y+s) <: Dates.CompoundPeriod @test typeof(y+ms) <: Dates.CompoundPeriod +@test typeof(y+us) <: Dates.CompoundPeriod +@test typeof(y+ns) <: Dates.CompoundPeriod @test y > m @test d < w @test mi < h @test ms < h @test ms < mi +@test us < ms +@test ns < ms +@test ns < us +@test ns < w +@test us < w @test typemax(Dates.Year) == Dates.Year(typemax(Int64)) @test typemax(Dates.Year) + y == Dates.Year(-9223372036854775808) @test typemin(Dates.Year) == Dates.Year(-9223372036854775808) @@ -261,6 +274,8 @@ test = ((((((((dt + y) - m) + w) - d) + h) - mi) + s) - ms) @test Dates.Minute("1") == mi @test Dates.Second("1") == s @test Dates.Millisecond("1") == ms +@test Dates.Microsecond("1") == us +@test Dates.Nanosecond("1") == ns @test_throws ArgumentError Dates.Year("1.0") @test Dates.Year(parse(Float64,"1.0")) == y @@ -283,6 +298,8 @@ dt = Dates.DateTime(2014) @test Dates.default(Dates.Minute) == zero(Dates.Minute) @test Dates.default(Dates.Second) == zero(Dates.Second) @test Dates.default(Dates.Millisecond) == zero(Dates.Millisecond) +@test Dates.default(Dates.Microsecond) == zero(Dates.Microsecond) +@test Dates.default(Dates.Nanosecond) == zero(Dates.Nanosecond) # Conversions @test Dates.toms(ms) == Dates.value(Dates.Millisecond(ms)) == 1 diff --git a/test/dates/ranges.jl b/test/dates/ranges.jl index d9f3dff71856d..49a0f65f95bd6 100644 --- a/test/dates/ranges.jl +++ b/test/dates/ranges.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license -function test_all_combos() +let for T in (Dates.Date,Dates.DateTime) f1 = T(2014); l1 = T(2013,12,31) f2 = T(2014); l2 = T(2014) @@ -113,6 +113,7 @@ function test_all_combos() end if T == Dates.DateTime for P in subtypes(Dates.TimePeriod) + P in (Dates.Microsecond, Dates.Nanosecond) && continue for pos_step in (P(1),P(2),P(50),P(2048),P(10000)) # empty range dr = f1:pos_step:l1 @@ -221,7 +222,6 @@ function test_all_combos() end end end -test_all_combos() # All the range representations we want to test # Date ranges @@ -407,14 +407,13 @@ c = Dates.Date(2013,6,1) @test [c:Dates.Month(-1):a;] == reverse([a:Dates.Month(1):c;]) @test length(range(Date(2000),366)) == 366 -function testlengths(n) +let n=100000 a = Dates.Date(2000) for i = 1:n @test length(range(a,i)) == i end return a+Dates.Day(n) end -testlengths(100000) # Custom definition to override default step of DateTime ranges @test typeof(step(Dates.DateTime(2000):Dates.DateTime(2001))) == Dates.Day @@ -446,16 +445,15 @@ b = Dates.Date(2013,2,1) @test length(Dates.Date(2000,6,23):Dates.Year(-10):Dates.Date(1900,2,28)) == 11 @test length(Dates.Date(2000,1,1):Dates.Year(1):Dates.Date(2000,2,1)) == 1 -function testyearranges(n) +let n=100000 a = b = Dates.Date(0) for i = 1:n @test length(a:Dates.Year(1):b) == i b += Dates.Year(1) end end -testyearranges(100000) -function testmonthranges(n) +let n=10000 a = Dates.Date(1985,12,5) b = Dates.Date(1986,12,27) c = Dates.DateTime(1985,12,5) @@ -470,9 +468,8 @@ function testmonthranges(n) end return b end -testmonthranges(10000) -function testmonthranges2(n) +let n=100000 a = b = Dates.Date(2000) for i = 1:n @test length(a:Dates.Month(1):b) == i @@ -480,7 +477,6 @@ function testmonthranges2(n) end return b end -testmonthranges2(100000) @test length(Dates.Year(1):Dates.Year(10)) == 10 @test length(Dates.Year(10):Dates.Year(-1):Dates.Year(1)) == 10 @@ -503,3 +499,42 @@ let d = Dates.Day(1) @test (Dates.Date(2000):d:Dates.Date(2001))+d == (Dates.Date(2000)+d:d:Dates.Date(2001)+d) @test (Dates.Date(2000):d:Dates.Date(2001))-d == (Dates.Date(2000)-d:d:Dates.Date(2001)-d) end + +# Time ranges +dr = Dates.Time(23,1,1):Dates.Time(23,2,1) +dr1 = Dates.Time(23,1,1):Dates.Time(23,1,1) +dr2 = Dates.Time(23,1,1):Dates.Time(22,2,1) # empty range +dr3 = Dates.Time(23,1,1):Dates.Minute(-1):Dates.Time(22,1,1) # negative step +# Big ranges +dr8 = typemin(Dates.Time):typemax(Dates.Time) +dr9 = typemin(Dates.Time):Dates.Nanosecond(1):typemax(Dates.Time) +# Non-default steps +dr10 = typemax(Dates.Time):Dates.Microsecond(-1):typemin(Dates.Time) +dr11 = typemin(Dates.Time):Dates.Millisecond(1):typemax(Dates.Time) +dr12 = typemin(Dates.Time):Dates.Minute(1):typemax(Dates.Time) +dr13 = typemin(Dates.Time):Dates.Hour(1):typemax(Dates.Time) +dr14 = typemin(Dates.Time):Dates.Millisecond(10):typemax(Dates.Time) +dr15 = typemin(Dates.Time):Dates.Minute(100):typemax(Dates.Time) +dr16 = typemin(Dates.Time):Dates.Hour(1000):typemax(Dates.Time) +dr17 = typemax(Dates.Time):Dates.Millisecond(-10000):typemin(Dates.Time) +dr18 = typemax(Dates.Time):Dates.Minute(-100):typemin(Dates.Time) +dr19 = typemax(Dates.Time):Dates.Hour(-10):typemin(Dates.Time) +dr20 = typemin(Dates.Time):Dates.Microsecond(2):typemax(Dates.Time) + +drs = Any[dr,dr1,dr2,dr3,dr8,dr9,dr10, + dr11,dr12,dr13,dr14,dr15,dr16,dr17,dr18,dr19,dr20] + +@test map(length,drs) == map(x->size(x)[1],drs) +@test all(x->findin(x,x) == [1:length(x);], drs[1:4]) +@test isempty(dr2) +@test all(x->reverse(x) == last(x):-step(x):first(x),drs) +@test all(x->minimum(x) == (step(x) < zero(step(x)) ? last(x) : first(x)),drs[4:end]) +@test all(x->maximum(x) == (step(x) < zero(step(x)) ? first(x) : last(x)),drs[4:end]) +@test_throws MethodError dr + 1 + +a = Dates.Time(23,1,1) +@test map(x->a in x,drs[1:4]) == [true,true,false,true] +@test a in dr + +@test all(x->sort(x) == (step(x) < zero(step(x)) ? reverse(x) : x),drs) +@test all(x->step(x) < zero(step(x)) ? issorted(reverse(x)) : issorted(x),drs) diff --git a/test/dates/types.jl b/test/dates/types.jl index 87dd04795e659..3222f7fcfaef7 100644 --- a/test/dates/types.jl +++ b/test/dates/types.jl @@ -49,6 +49,14 @@ test = Dates.Date(Dates.UTD(734869)) @test Dates.Date(2013) == test @test Dates.Date(2013,1) == test @test Dates.Date(2013,1,1) == test +# Test Time construction by parts +t = Dates.Time(Dates.Nanosecond(82800000000000)) +@test Dates.Time(23) == t +@test Dates.Time(23,0) == t +@test Dates.Time(23,0,0) == t +@test Dates.Time(23,0,0,0) == t +@test Dates.Time(23,0,0,0,0) == t +@test Dates.Time(23,0,0,0,0,0) == t # Test various input types for Date/DateTime test = Dates.Date(1,1,1) @@ -101,19 +109,38 @@ test = Dates.Date(1,1,1) @test_throws ArgumentError Dates.DateTime(2013,1,1,0,0,60) @test_throws ArgumentError Dates.DateTime(2013,1,1,0,0,0,-1) @test_throws ArgumentError Dates.DateTime(2013,1,1,0,0,0,1000) +@test_throws ArgumentError Dates.Time(24) +@test_throws ArgumentError Dates.Time(-1) +@test_throws ArgumentError Dates.Time(0,-1) +@test_throws ArgumentError Dates.Time(0,60) +@test_throws ArgumentError Dates.Time(0,0,-1) +@test_throws ArgumentError Dates.Time(0,0,60) +@test_throws ArgumentError Dates.Time(0,0,0,-1) +@test_throws ArgumentError Dates.Time(0,0,0,1000) +@test_throws ArgumentError Dates.Time(0,0,0,0,-1) +@test_throws ArgumentError Dates.Time(0,0,0,0,1000) +@test_throws ArgumentError Dates.Time(0,0,0,0,0,-1) +@test_throws ArgumentError Dates.Time(0,0,0,0,0,1000) # Test DateTime traits a = Dates.DateTime(2000) b = Dates.Date(2000) +c = Dates.Time(0) @test Dates.calendar(a) == Dates.ISOCalendar @test Dates.calendar(b) == Dates.ISOCalendar @test eps(a) == Dates.Millisecond(1) @test eps(b) == Dates.Day(1) +@test eps(c) == Dates.Nanosecond(1) @test string(typemax(Dates.DateTime)) == "146138512-12-31T23:59:59" @test string(typemin(Dates.DateTime)) == "-146138511-01-01T00:00:00" @test typemax(Dates.DateTime) - typemin(Dates.DateTime) == Dates.Millisecond(9223372017043199000) @test string(typemax(Dates.Date)) == "252522163911149-12-31" @test string(typemin(Dates.Date)) == "-252522163911150-01-01" +@test string(typemax(Dates.Time)) == "23:59:59.999999999" +@test string(typemin(Dates.Time)) == "00:00:00" +@test isfinite(Dates.Date) +@test isfinite(Dates.DateTime) +@test isfinite(Dates.Time) # Date-DateTime conversion/promotion @test Dates.DateTime(a) == a @@ -168,6 +195,12 @@ ms = Dates.Millisecond(1) @test Dates.Date(d,y) == Dates.Date(1,1,1) @test Dates.Date(d,m) == Dates.Date(1,1,1) @test Dates.Date(m,y) == Dates.Date(1,1,1) - -@test isfinite(Dates.Date) -@test isfinite(Dates.DateTime) +us = Dates.Microsecond(1) +ns = Dates.Nanosecond(1) +@test Dates.Time(h) == Dates.Time(1) +@test Dates.Time(h,mi) == Dates.Time(1,1) +@test Dates.Time(h,mi,s) == Dates.Time(1,1,1) +@test Dates.Time(h,mi,s,ms) == Dates.Time(1,1,1,1) +@test Dates.Time(h,mi,s,ms,us) == Dates.Time(1,1,1,1,1) +@test Dates.Time(h,mi,s,ms,us,ns) == Dates.Time(1,1,1,1,1,1) +@test Dates.Time(us,h,s,ns,mi,ms) == Dates.Time(1,1,1,1,1,1) \ No newline at end of file