Skip to content
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

Merged
merged 7 commits into from
Jan 24, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add a Time type to the Base.Dates module
  • Loading branch information
quinnj committed Jan 19, 2017
commit b8b33f9790423bafd8a4f3e465b4276aed369361
4 changes: 3 additions & 1 deletion base/dates/Dates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions base/dates/accessors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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),24)
minute(t::Time) = mod(fld(value(t),60000000000),60)
second(t::Time) = mod(fld(value(t),1000000000),60)
millisecond(t::Time) = mod(fld(value(t),1000000),1000)
microsecond(t::Time) = mod(fld(value(t),1000),1000)
nanosecond(t::Time) = mod(value(t),1000)

dayofmonth(dt::TimeType) = day(dt)

Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing is off

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so? This follows the spacing of the block directly above it....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L134: 4 spaces
L135: 4 spaces
L136: 8 spaces
L137: 12 spaces
L138: NA
L139: 9 spaces <- should be 8
L140: 9 spaces <- should be 8
L141: 4 spaces

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks. Sorry for missing this. Fixed.

end
end
32 changes: 32 additions & 0 deletions base/dates/adjusters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -184,6 +191,31 @@ 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
Copy link
Contributor

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?

Copy link
Contributor

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

Copy link
Member Author

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).

Copy link
Contributor

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.

Copy link
Contributor

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.

So does it?

Copy link
Member Author

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.


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).
"""
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)),
Expand Down
33 changes: 25 additions & 8 deletions base/dates/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for the spaces here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better style, more legible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll elaborate as the inline example isn't showing the surrounding context. Maybe we should remove the spaces before = on some of these methods? That way they would line up

(+)(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

vs.

(+)(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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it.

(+)(y::Period, x::TimeType) = x + y
(-)(y::Period, x::TimeType) = x - y
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these magnitudes then?

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks wrong that y - x = x - y

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it. Yes, these are magnitudes. Order doesn't matter.

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The 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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wat? why allow that?

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in favor of removing this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened #20205


for op in (:+, :-)
@eval begin
Expand Down
14 changes: 12 additions & 2 deletions base/dates/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
47 changes: 31 additions & 16 deletions base/dates/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
64 changes: 42 additions & 22 deletions base/dates/periods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -16,23 +16,24 @@ 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)." : ""
@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)

Construct a `$($period_str)` object with the given `v` value. Input must be
losslessly convertible to an `Int64`.
""" $period(v)
typs = period in (:Microsecond, :Nanosecond) ? ["Time"] : (period in (:Hour, :Minute, :Second, :Millisecond) ? ["DateTime","Time"] : ["TimeType"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be renamed to types?

description = typs == ["TimeType"] ? "`Date` or `DateTime`" : (typs == ["Time"] ? "`Time`" : "`Time` or `DateTime`")
reference = period == :Week ? " For details see [`$accessor_str(::TimeType)`](:func:`$accessor_str`)." : ""
for typ_str in typs
@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)

Construct a `$($period_str)` object with the given `v` value. Input must be
losslessly convertible to an `Int64`.
""" $period(v)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstrings seem a little strange here as the docstring specifically for Hour(::DateTime) references Time.

Hour(dt::DateTime) -> Hour

The hour part of a Time or DateTime as a Hour.

Hour(dt::Time) -> Hour

The hour part of a Time or DateTime as a Hour.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've got them better sorted out now.

end
end
# Now we're safe to define Period-Number conversions
Expand Down Expand Up @@ -117,15 +118,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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The 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{Microsecond}) = (Millisecond,1000)
coarserperiod(::Type{Millisecond}) = (Second,1000)
coarserperiod(::Type{Second}) = (Minute,60)
coarserperiod(::Type{Minute}) = (Hour,60)
Expand Down Expand Up @@ -202,6 +216,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...])


Expand Down Expand Up @@ -382,7 +399,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)
Expand All @@ -392,10 +409,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
Expand Down Expand Up @@ -440,7 +457,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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't work when CompoundPeriod contains a Microsecond or a Nanosecond.

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
  ...

Copy link
Member Author

Choose a reason for hiding this comment

The 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)
Expand Down
6 changes: 4 additions & 2 deletions base/dates/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading