`range` of `DateTime`s

I want to create a uniformly-spaced range of DateTime values with e.g. 100 points between two given values. range function doesn’t work for me: range(DateTime(2000, 1, 1, 0, 0), DateTime(2000, 1, 2, 0, 0), length=100) gives MethodError: no method matching *(::Float64, ::DateTime), and I cannot find an easy way to do this. Note that range with step instead of length does work, e.g.: range(DateTime(2000, 1, 1, 0, 0), DateTime(2000, 1, 2, 0, 0), step=Minute(1)).
Am I missing some obvious solution here?

There was a similar question two years ago here - Linspace with DateTime - but there is no answer, and lots of stuff have changed in Julia since then.

4 Likes

The solution is in that same post:

function mylinspace(d1::DateTime, d2::DateTime, n::Int)
    Δ = d2 - d1
    T = typeof(Δ)
    δ = T(round(Int, Dates.value(Δ)/(n - 1)))
    d2 = d1 + δ*(n - 1)
    return d1:δ:d2
end
1 Like

I found this one, because I love comprehensions:

[ DateTime(Dates.UTInstant{Millisecond}(Millisecond(Int64(floor(x))))) for x in range(Dates.value(d1),Dates.value(d2),length=100)]

But its not a range, just an array.

A major difference between this implementation and builtin range(a, b, length) (e.g. for numbers) is that the last element of mylinspace isn’t equal to d2 argument. Even more, the last element can be larger that d2! And these are just the obvious things I noticed.

Anyway, it’s strange that range is not supported for DateTimes. This cannot be intentional, right?

Yea, I know. You are right that the last element can be larger, that’s a problem. But as to it being equal to d2 range doesn’t do that either:

julia> range(0, 1, step = 0.11)[end]
0.99

range with step doesn’t, but that’s fine - it’s impossible if b-a is not divisible by step. But range with length does have the property that range(a, b, length)[end] == b as far as I can see.

Just because the calculated step can be a Float. So range is exact only in the limits of Floats.

DateTime has a similar limitation in the sense that Nanosecond is the smallset step it can encode.

Perhaps it would be good to create an issue for this?

You can use range to create a LinRange{DateTime}, though it fails on show, or getindex. Only first seems to work, here on julia 1.3 rc2:

using Dates
starttime = DateTime(2000, 1, 1, 0, 0)
endtime = DateTime(2000, 1, 2, 0, 0)
r = range(starttime, endtime, length=5);  # no error as long as you suppress show with a ;
typeof(r)  # -> LinRange{DateTime}
first(r)  # -> 2000-01-01T00:00:00
r[1]
ERROR: MethodError: no method matching *(::Float64, ::DateTime)
Closest candidates are:
  *(::Any, ::Any, ::Any, ::Any...) at operators.jl:529
  *(::Float64, ::Float64) at float.jl:405
  *(::AbstractFloat, ::Bool) at bool.jl:112
  ...
Stacktrace:
 [1] unsafe_getindex(::LinRange{DateTime}, ::Int64) at .\range.jl:663
 [2] getindex(::LinRange{DateTime}, ::Int64) at .\range.jl:642
1 Like

I filed the issue: https://github.com/JuliaLang/julia/issues/35446

3 Likes

If you are fine with a uniformly-spaced (as good as possible) vector of DateTime values you can compute the range in unix and convert back to DateTime objects. For example:

function GetTimestampRange(firstTimestamp, lastTimestamp, length)
firstTimestampAsUnix = datetime2unix(firstTimestamp)
lastTimestampAsUnix = datetime2unix(lastTimestamp)
vecOfUnixTimestamps = [range(firstTimestampAsUnix, lastTimestampAsUnix, length = length)…]
return unix2datetime.(vecOfUnixTimestamps)
end

1 Like

If you’re willing to try NanoDates.jl, you can get a range similar to what you wanted originally. However, instead of giving range an integer length, you have to give it a Period.

using Dates, NanoDates

nd1 = NanoDate(2000, 1, 1, 0, 0)
nd2 = NanoDate(2000, 1, 2, 0, 0)
step = Nanosecond((nd2 - nd1).value ÷ 100) # The division must return an integer.
r100 = nd1:step:nd2
r15m = nd1:Minute(15):nd2
r4h = nd1:Hour(4):nd2

What’s the benefit here compared to Dates? They also allow ranges with manually specified steps, as I understand.

I should have experimented a little bit more before posting. My newness to Julia is showing, and I didn’t realize DateTime could do nearly the same thing in this case.