-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial work to add a Time type to Base.Dates #12274
Changes from all commits
b8b33f9
73eafe9
58338da
8881760
673d2b8
e383f20
6cd4ea5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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)`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd either leave out the default in the docstring signature and say something like "the default value of
(edited to fix Microsecond typo) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was not addressed, docstring should not list a default value that isn't correct |
||
""" | ||
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)), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are these magnitudes then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I understand your comment. These are generic fallbacks that will end up dispatching to the definitions above. This is just for the switched order of operators. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks wrong that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, got it. Yes, these are magnitudes. Order doesn't matter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. worth a comment then, in case anyone else gets confused looking at this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we already do julia> Dates.Day(1) - Date(2016, 1, 1)
2015-12-31 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wat? why allow that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember if it was an explicit decision to allow or not. We can open another issue to remove. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm in favor of removing this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opened #20205 |
||
|
||
for op in (:+, :-) | ||
@eval begin | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstrings seem a little strange here as the docstring specifically for
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I've got them better sorted out now. |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here and below, I think the docstring convention has one blank line between the signature and the body There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for catching. I think I've managed to clean up all the doc changes. |
||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't work when julia> Dates.toms(Dates.CompoundPeriod(Dates.Nanosecond(1)))
ERROR: MethodError: no method matching toms(::Base.Dates.Nanosecond)
Closest candidates are:
toms(::Base.Dates.CompoundPeriod) at dates/periods.jl:460
toms(::Base.Dates.Year) at dates/periods.jl:459
toms(::Base.Dates.Month) at dates/periods.jl:458
... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed. |
||
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you want these docstrings to be in the stdlib docs or repl-only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the default step isn't always one second in the implementations below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the docstrings be in stdlib or repl-only? I don't have a preference either way, but if there's a convention we should follow, happy to do it. Yeah, I would hope it would be obvious that if you're doing sub-Second Time ranges, it wouldn't default to a Second (since that would potentially lose precision in range steps).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Dates section of the stdlib docs includes other docstrings for non-exported types, then this should be added.
Docstring saying one thing and code doing another isn't obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So does it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added Time docs to the stdlib.