Range of Date without a defined step (unit step supposed)

Hello,

The following code raises an error

julia> Date(2020,1,1):Date(2020,2,1)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Day
Closest candidates are:
  convert(::Type{Day}, ::Week) at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Dates\src\periods.jl:410
  convert(::Type{Day}, ::Hour) at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Dates\src\periods.jl:418
  convert(::Type{Day}, ::Minute) at C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Dates\src\periods.jl:418
  ...
Stacktrace:
 [1] oftype(::Day, ::Int64) at .\essentials.jl:367
 [2] (::Colon)(::Date, ::Date) at .\range.jl:7
 [3] top-level scope at REPL[99]:100: 

but not

Date(2020,1,1):Dates.Day(1):Date(2020,2,1)

My opinion is that current implementation of range assume an integer step of 1 but it should assume what a unit step of a Date is.

I’m facing a similar issue with some custom struct also, not only Date.

Maybe the default unit step could be calculated in most cases using

julia> start = Date(2020,1,1)
2020-01-01

julia> stop = Date(2020,2,1)
2020-02-01

julia> (stop - start) / (stop - start).value
1 day

or by defining a unit(::Type{Date}) or a unit(::Date) function

What is you opinion about that?

I dislike it.

What would you want it to assume for and evaluate this?
Date(2020,2):Date(2020,5)

Also, the default step is 1 even in instances where 1 clearly doesn’t make sense (see 0.1:0.5). There is no logic, it isn’t a calculated default, it is just a default value. If you want the step to be something else, you have to change it.

1 Like

What would you want it to assume for and evaluate this?
Date(2020,2):Date(2020,5)

For Date(2020,2):Date(2020,5) or Date(2020,2,1):Date(2020,5,1) I would simply assume that the unit step of a Date is a Day… why ? simply because between 2 consecutive days a day occurs.

For any discrete variable I would assume that step is resolution ie the smallest difference between 2 consecutive values.

But for any number which inherit from AbstractFloat that’s a bit different, I would assume step is 1.0.

Maybe having an interface to define what default “unit step” should be for a given type will help.

You can find my use case GitHub - scls19fr/AIRAC.jl: Julia library to deal with AIRAC cycle dates

I can have 2 Airac struct (an AIRAC cycle duration is 28 days)

julia> using AIRAC
julia> a1 = parse(Airac, "2101")
Airac(2101, 2021-01-28)

julia> a2 = parse(Airac, "2103")
Airac(2103, 2021-03-25)

I can compute the difference between 2 AIRAC cycles and it returns an AiracDiff (which simply contains an integer… the number of 28 days durations between the 2 Airac data)

julia> a2 - a1
AiracDiff(2)

but I can’t get simply a range

julia> a1:a2
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type AiracDiff
Closest candidates are:
  convert(::Type{T}, ::T) where T at essentials.jl:171
  AiracDiff(::Int64) at C:\Users\Admin-pc\.julia\dev\AIRAC\src\AIRAC.jl:93
  AiracDiff(::Any) at C:\Users\Admin-pc\.julia\dev\AIRAC\src\AIRAC.jl:93
Stacktrace:
 [1] oftype(::AiracDiff, ::Int64) at .\essentials.jl:367
 [2] (::Colon)(::Airac, ::Airac) at .\range.jl:7
 [3] top-level scope at REPL[7]:1

Same problem also occurs when specifying step.

julia> a1:AiracDiff():a2
ERROR: MethodError: no method matching -(::Airac, ::Int64)
Closest candidates are:
  -(::Base.CoreLogging.LogLevel, ::Integer) at logging.jl:117
  -(::Missing, ::Number) at missing.jl:115
  -(::Airac, ::AiracDiff) at C:\Users\Admin-pc\.julia\dev\AIRAC\src\AIRAC.jl:120
  ...
Stacktrace:
 [1] steprange_last(::Airac, ::AiracDiff, ::Airac) at .\range.jl:235
 [2] StepRange at .\range.jl:205 [inlined]
 [3] StepRange at .\range.jl:256 [inlined]
 [4] _colon at .\range.jl:46 [inlined]
 [5] (::Colon)(::Airac, ::AiracDiff, ::Airac) at .\range.jl:40
 [6] top-level scope at REPL[8]:1

The reason why range function would like to convert an Airac to Int64 is really weird to me.

I understand that I can directly overload range definition. But that shouldn’t be necessary if we could defined what a unit step is for a given type.

Feel free to provide some implementation idea.

You say this but then you quickly prove how inconvenient this would be. For example, the smallest unit of difference between 2 consecutive floats is not 1.0, but epsilon. This would be a terrible default of course! So you would prefer 1.0 there. But what about the range I suggested? A default of 1.0 doesn’t make sense there either.

The smallest unit of difference between 2 dates is actually…nanoseconds? picoseconds? But you would prefer 1 day (why not 24 hours?)

The issue is that the appropriate default is context dependent, and (I’m guessing) that is why it is required to be specified.

2 Likes

You could take a look at the code. Most likely rem(::AiracDiff, ::AiracDiff) returns an Int

Theres oneunit() and UnitRange, I think you can do something like that already, just not necessarily with the colon syntax. Okay, UnitRange is for Reals only, my bad.

The “1-colon-syntax” for ranges uses oftype(stop-start, 1) as a default value for the stepsize, so you can either overload that or convert with your respective types.

1 Like