IntervalSets.jl
IntervalSets.jl copied to clipboard
findall for Date(Time) ranges
Many thanks for getting this done.
I just found another edge case, which I'm not really sure this ought to try to support, but anyway here it is:
julia> using IntervalSets
julia> using Dates
julia> findall(in(Interval(Date(2020, 1, 8), Date(2020, 1, 22))), Date(2020):Week(1):Date(2021))
ERROR: MethodError: no method matching isless(::Week, ::Int64)
Closest candidates are:
isless(::Missing, ::Any) at missing.jl:87
isless(::DoubleFloats.DoubleFloat{T}, ::Integer) where T<:Union{Float16, Float32, Float64} at /Users/me/.julia/packages/DoubleFloats/8fO4c/src/type/compare.jl:135
isless(::AbstractFloat, ::Real) at operators.jl:158
...
Stacktrace:
[1] <(::Week, ::Int64) at ./operators.jl:268
[2] findall(::Base.Fix2{typeof(in),Interval{:closed,:closed,Date}}, ::StepRange{Date,Week}) at /Users/me/.julia/packages/IntervalSets/D282g/src/findall.jl:43
Originally posted by @mcabbott in https://github.com/JuliaMath/IntervalSets.jl/pull/63#issuecomment-631140661
This ought to be an easy fix along the lines of
function Base.findall(interval_d::Base.Fix2{typeof(in),Interval{L,R,T}}, x::AbstractRange) where {L,R,T}
isempty(x) && return 1:0
interval = interval_d.x
il, ir = firstindex(x), lastindex(x)
δx = step(x)
a,b = if δx < zero(δx)
rev = findall(in(interval), reverse(x))
isempty(rev) && return rev
a = (il+ir)-last(rev)
b = (il+ir)-first(rev)
a,b
else
lx, rx = first(x), last(x)
l = max(leftendpoint(interval), lx-oneunit(δx))
r = min(rightendpoint(interval), rx+oneunit(δx))
(l > rx || r < lx) && return 1:0
a = il + max(0, round(Int, cld(l-lx, δx)))
a += (a ≤ ir && (x[a] == l && L == :open || x[a] < l))
b = min(ir, round(Int, cld(r-lx, δx)) + il)
b -= (b ≥ il && (x[b] == r && R == :open || x[b] > r))
a,b
end
# Reversing a range could change sign of values close to zero (cf
# sign of the smallest element in x and reverse(x), where x =
# range(BigFloat(-0.5),stop=BigFloat(1.0),length=10)), or more
# generally push elements in or out of the interval (as can cld),
# so we need to check once again.
a += +(a < ir && x[a] ∉ interval) - (il < a && x[a-1] ∈ interval)
b += -(il < b && x[b] ∉ interval) + (b < ir && x[b+1] ∈ interval)
a:b
end
but we get this error:
julia> i = Interval(Date(2020, 1, 8), Date(2020, 1, 22))
2020-01-08..2020-01-22
julia> x = Date(2020):Week(1):Date(2021)
2020-01-01:1 week:2020-12-30
julia> findall(in(i), x)
ERROR: MethodError: no method matching div(::Day, ::Week, ::RoundingMode{:Up})
Closest candidates are:
div(::P, ::P, ::RoundingMode) where P<:Period at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/Dates/src/periods.jl:80
div(::P, ::Real, ::RoundingMode) where P<:Period at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/Dates/src/periods.jl:81
div(::Any, ::Any) at div.jl:37
...
Stacktrace:
[1] cld(::Day, ::Week) at ./div.jl:99
[2] findall(::Base.Fix2{typeof(in),Interval{:closed,:closed,Date}}, ::StepRange{Date,Week}) at /Users/jagot/.julia/dev/IntervalSets/src/findall.jl:60
[3] top-level scope at REPL[16]:1
[4] eval(::Module, ::Any) at ./boot.jl:331
[5] eval_user_input(::Any, ::REPL.REPLBackend) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
[6] run_backend(::REPL.REPLBackend) at /Users/jagot/.julia/packages/Revise/MgvIv/src/Revise.jl:1023
[7] top-level scope at REPL[3]:0
This happens because l-lx becomes days, and cld is not defined for dividing days by weeks.
This issue was already solved in the current master branch.
julia> using IntervalSets
julia> using Dates
julia> findall(in(Interval(Date(2020, 1, 8), Date(2020, 1, 22))), Date(2020):Week(1):Date(2021))
2:4