From ba28eba7435b7757626b6f74135220574bcd44aa Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 9 Mar 2018 18:48:26 -0600 Subject: [PATCH 1/4] Add tests for Date conversion --- test/anchoredinterval.jl | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index e466a398..d67c8798 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -37,16 +37,26 @@ using Intervals: canonicalize end @testset "conversion" begin - he = HourEnding(dt) - hb = HourBeginning(dt) - @test DateTime(he) == dt - @test DateTime(hb) == dt - @test convert(Interval, he) == Interval(dt - Hour(1), dt, Inclusivity(false, true)) - @test convert(Interval, hb) == Interval(dt, dt + Hour(1), Inclusivity(true, false)) - @test Interval(he) == Interval(dt - Hour(1), dt, Inclusivity(false, true)) - @test Interval(hb) == Interval(dt, dt + Hour(1), Inclusivity(true, false)) - @test Interval{DateTime}(he) == Interval(dt - Hour(1), dt, Inclusivity(false, true)) - @test Interval{DateTime}(hb) == Interval(dt, dt + Hour(1), Inclusivity(true, false)) + @testset "Date" begin + d = Date(dt) + de = AnchoredInterval{Day(-1)}(d) + db = AnchoredInterval{Day(1)}(d) + @test Date(de) == d + @test Date(db) == d + end + + @testset "DateTime" begin + he = HourEnding(dt) + hb = HourBeginning(dt) + @test DateTime(he) == dt + @test DateTime(hb) == dt + @test convert(Interval, he) == Interval(dt - Hour(1), dt, false, true) + @test convert(Interval, hb) == Interval(dt, dt + Hour(1), true, false) + @test Interval(he) == Interval(dt - Hour(1), dt, false, true) + @test Interval(hb) == Interval(dt, dt + Hour(1), true, false) + @test Interval{DateTime}(he) == Interval(dt - Hour(1), dt, false, true) + @test Interval{DateTime}(hb) == Interval(dt, dt + Hour(1), true, false) + end end @testset "accessors" begin From 24ea040ea1f31b81bfc0110d882df005412a645e Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 7 Mar 2018 14:49:55 -0600 Subject: [PATCH 2/4] Switch type parameters for AnchoredInterval --- README.md | 6 +- docs/src/index.md | 6 +- src/anchoredinterval.jl | 93 +++++++++--------- src/description.jl | 8 +- test/anchoredinterval.jl | 201 ++++++++++++++++++++------------------- 5 files changed, 162 insertions(+), 152 deletions(-) diff --git a/README.md b/README.md index 95c13f4a..46ed994c 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ This package defines: * `AbstractInterval`, along with its subtypes: * `Interval{T}`, which represents a non-iterable range between two endpoints of type `T` - * `AnchoredInterval{P, T}`, which represents a non-iterable range defined by a single + * `AnchoredInterval{T, P}`, which represents a non-iterable range defined by a single value `anchor::T` and the value type `P` which represents the size of the range - * `HourEnding`, a type alias for `AnchoredInterval{Hour(-1)}` - * `HourBeginning`, a type alias for `AnchoredInterval{Hour(1)}` + * `HourEnding`, a type alias for `AnchoredInterval{T, Hour(-1)}` + * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour(1)}` * `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the anchor up (`HE`) or down (`HB`) to the nearest hour * `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or diff --git a/docs/src/index.md b/docs/src/index.md index d68f85ac..5c74de0e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -3,10 +3,10 @@ This package defines: * `AbstractInterval`, along with its subtypes: * `Interval{T}`, which represents a non-iterable range between two endpoints of type `T` - * `AnchoredInterval{P, T}`, which represents a non-iterable range defined by a single + * `AnchoredInterval{T, P}`, which represents a non-iterable range defined by a single value `anchor::T` and the value type `P` which represents the size of the range - * `HourEnding`, a type alias for `AnchoredInterval{Hour(-1), T}` - * `HourBeginning`, a type alias for `AnchoredInterval{Hour(1), T}` + * `HourEnding`, a type alias for `AnchoredInterval{T, Hour(-1)}` + * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour(1)}` * `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the anchor up (`HE`) or down (`HB`) to the nearest hour * `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index 5970f8c8..7ffe7457 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -1,8 +1,11 @@ using Base.Dates: value, coarserperiod """ - AnchoredInterval{P, T}(anchor::T, [inclusivity::Inclusivity]) where {P, T} -> AnchoredInterval{P, T} - AnchoredInterval{P, T}(anchor::T, [closed_left::Bool, closed_right::Bool]) where {P, T} -> AnchoredInterval{P, T} + AnchoredInterval{T, P}(anchor::T, [inclusivity::Inclusivity]) -> AnchoredInterval{T, P} + AnchoredInterval{T, P}(anchor::T, [closed_left::Bool, closed_right::Bool]) -> AnchoredInterval{T, P} + + AnchoredInterval(anchor::T, P, [inclusivity::Inclusivity]) -> AnchoredInterval{T, P} + AnchoredInterval(anchor::T, P, [closed_left::Bool, closed_right::Bool]) -> AnchoredInterval{T, P} `AnchoredInterval` is a subtype of `AbstractInterval` that represents a non-iterable range or span of values defined not by two endpoints but instead by a single `anchor` point and @@ -21,8 +24,8 @@ included for positive values of `P` and the greater endpoint included for negati range of values. This happens most often with dates and times, where "HE15" is often used as shorthand for (14:00..15:00]. -To this end, `HourEnding` is a type alias for `AnchoredInterval{Hour(-1)}`. Similarly, -`HourBeginning` is a type alias for `AnchoredInterval{Hour(1)}`. +To this end, `HourEnding` is a type alias for `AnchoredInterval{T, Hour(-1)} where T`. +Similarly, `HourBeginning` is a type alias for `AnchoredInterval{T, Hour(1)} where T`. ### Rounding @@ -48,44 +51,42 @@ HourBeginning{DateTime}(2016-08-11T02:00:00, Inclusivity(true, false)) ### Example ```julia -julia> AnchoredInterval{Hour(-1)}(DateTime(2016, 8, 11, 12)) +julia> AnchoredInterval(DateTime(2016, 8, 11, 12), Hour(-1)) HourEnding{DateTime}(2016-08-11T12:00:00, Inclusivity(false, true)) -julia> AnchoredInterval{Day(1)}(DateTime(2016, 8, 11)) -AnchoredInterval{1 day, DateTime}(2016-08-11T00:00:00, Inclusivity(true, false)) +julia> AnchoredInterval(DateTime(2016, 8, 11), Day(1)) +AnchoredInterval{DateTime, 1 day}(2016-08-11T00:00:00, Inclusivity(true, false)) -julia> AnchoredInterval{Minute(5)}(DateTime(2016, 8, 11, 12, 30), true, true) -AnchoredInterval{5 minutes, DateTime}(2016-08-11T12:30:00, Inclusivity(true, true)) +julia> AnchoredInterval(DateTime(2016, 8, 11, 12, 30), Minute(5), true, true) +AnchoredInterval{DateTime, 5 minutes}(2016-08-11T12:30:00, Inclusivity(true, true)) ``` See also: [`Interval`](@ref), [`Inclusivity`](@ref), [`HE`](@ref), [`HB`](@ref) """ -struct AnchoredInterval{P, T} <: AbstractInterval{T} +struct AnchoredInterval{T, P} <: AbstractInterval{T} anchor::T inclusivity::Inclusivity end # When an interval is anchored to the lesser endpoint, default to Inclusivity(false, true) # When an interval is anchored to the greater endpoint, default to Inclusivity(true, false) -function AnchoredInterval{P, T}(i::T) where {P, T} - return AnchoredInterval{P, T}(i::T, Inclusivity(P ≥ zero(P), P ≤ zero(P))) +function AnchoredInterval{T, P}(i::T) where {T, P} + return AnchoredInterval{T, P}(i::T, Inclusivity(P ≥ zero(P), P ≤ zero(P))) end -AnchoredInterval{P}(i::T, inc::Inclusivity) where {P, T} = AnchoredInterval{P, T}(i, inc) -AnchoredInterval{P}(i::T) where {P, T} = AnchoredInterval{P, T}(i) - -function AnchoredInterval{P, T}(i::T, x::Bool, y::Bool) where {P, T} - return AnchoredInterval{P, T}(i, Inclusivity(x, y)) +function AnchoredInterval{T, P}(i::T, x::Bool, y::Bool) where {T, P} + return AnchoredInterval{T, P}(i, Inclusivity(x, y)) end -function AnchoredInterval{P}(i::T, x::Bool, y::Bool) where {P, T} - return AnchoredInterval{P, T}(i, Inclusivity(x, y)) -end +AnchoredInterval(i::T, span, inc::Inclusivity) where T = AnchoredInterval{T, span}(i, inc) +AnchoredInterval(i::T, span, x::Bool, y::Bool) where T = AnchoredInterval{T, span}(i, x, y) +AnchoredInterval(i::T, span) where T = AnchoredInterval{T, span}(i) + -const HourEnding{T} = AnchoredInterval{Hour(-1), T} where T <: TimeType +const HourEnding{T} = AnchoredInterval{T, Hour(-1)} where T <: TimeType HourEnding(a::T, args...) where T = HourEnding{T}(a, args...) -const HourBeginning{T} = AnchoredInterval{Hour(1), T} where T <: TimeType +const HourBeginning{T} = AnchoredInterval{T, Hour(1)} where T <: TimeType HourBeginning(a::T, args...) where T = HourBeginning{T}(a, args...) """ @@ -104,32 +105,38 @@ nearest hour. """ HB(a, args...) = HourBeginning(floor(a, Hour), args...) -function Base.copy(x::AnchoredInterval{P, T}) where {P, T} - return AnchoredInterval{P, T}(anchor(x), inclusivity(x)) +function Base.copy(x::AnchoredInterval{T, P}) where {T, P} + return AnchoredInterval{T, P}(anchor(x), inclusivity(x)) end ##### ACCESSORS ##### -Base.first(interval::AnchoredInterval{P}) where P = min(interval.anchor, interval.anchor+P) -Base.last(interval::AnchoredInterval{P}) where P = max(interval.anchor, interval.anchor+P) +function Base.first(interval::AnchoredInterval{T, P}) where {T, P} + min(interval.anchor, interval.anchor + P) +end + +function Base.last(interval::AnchoredInterval{T, P}) where {T, P} + max(interval.anchor, interval.anchor + P) +end + anchor(interval::AnchoredInterval) = interval.anchor -span(interval::AnchoredInterval{P}) where P = abs(P) +span(interval::AnchoredInterval{T, P}) where {T, P} = abs(P) ##### CONVERSION ##### -function Base.convert(::Type{Interval}, interval::AnchoredInterval{P, T}) where {P, T} +function Base.convert(::Type{Interval}, interval::AnchoredInterval{T}) where T return Interval{T}(first(interval), last(interval), inclusivity(interval)) end -function Base.convert(::Type{Interval{T}}, interval::AnchoredInterval{P, T}) where {P, T} +function Base.convert(::Type{Interval{T}}, interval::AnchoredInterval{T}) where T return Interval{T}(first(interval), last(interval), inclusivity(interval)) end -Base.convert(::Type{T}, interval::AnchoredInterval{P, T}) where {P, T} = anchor(interval) +Base.convert(::Type{T}, interval::AnchoredInterval{T}) where T = anchor(interval) # Date/DateTime attempt to convert to Int64 instead of falling back to convert(T, ...) -Base.Date(interval::AnchoredInterval{P, Date}) where P = convert(Date, interval) -Base.DateTime(interval::AnchoredInterval{P, DateTime}) where P = convert(DateTime, interval) +Base.Date(interval::AnchoredInterval{Date}) = convert(Date, interval) +Base.DateTime(interval::AnchoredInterval{DateTime}) = convert(DateTime, interval) ##### DISPLAY ##### @@ -139,8 +146,8 @@ Base.show(io::IO, ::Type{HourBeginning}) = print(io, "HourBeginning{T}") Base.show(io::IO, ::Type{HourEnding{T}}) where T <: TimeType = print(io, "HourEnding{$T}") Base.show(io::IO, ::Type{HourBeginning{T}}) where T <: TimeType = print(io, "HourBeginning{$T}") -function Base.show(io::IO, ::Type{AnchoredInterval{P, T}}) where {P, T} - print(io, "AnchoredInterval{$P, $T}") +function Base.show(io::IO, ::Type{AnchoredInterval{T, P}}) where {T, P} + print(io, "AnchoredInterval{$T, $P}") end function Base.show(io::IO, interval::T) where T <: AnchoredInterval @@ -155,7 +162,7 @@ function Base.show(io::IO, interval::T) where T <: AnchoredInterval end end -function Base.print(io::IO, interval::AnchoredInterval{P, T}) where {P, T <: TimeType} +function Base.print(io::IO, interval::AnchoredInterval{<:TimeType}) # Print to io in order to keep properties like :limit and :compact if get(io, :compact, false) io = IOContext(io, :limit=>true) @@ -174,17 +181,17 @@ Base.:-(a::AnchoredInterval, b) = a + -b # Required for StepRange{<:AnchoredInterval} Base.:-(a::AnchoredInterval, b::AnchoredInterval) = anchor(a) - anchor(b) -Base.:-(a::T, b::AnchoredInterval{P, T}) where {P, T <: Number} = a + -b +Base.:-(a::T, b::AnchoredInterval{T}) where {T <: Number} = a + -b -function Base.:-(a::AnchoredInterval{P, T}) where {P, T <: Number} +function Base.:-(a::AnchoredInterval{T, P}) where {T <: Number, P} inc = inclusivity(a) - AnchoredInterval{-P, T}(-anchor(a), Inclusivity(last(inc), first(inc))) + AnchoredInterval{T, -P}(-anchor(a), Inclusivity(last(inc), first(inc))) end ##### EQUALITY ##### # Required for min/max of AnchoredInterval{LaxZonedDateTime} when the anchor is AMB or DNE -function Base.:<(a::AnchoredInterval{P, T}, b::AnchoredInterval{P, T}) where {P, T} +function Base.:<(a::AnchoredInterval{T, P}, b::AnchoredInterval{T, P}) where {T, P} return anchor(a) < anchor(b) end @@ -196,7 +203,7 @@ function Base.steprem(a::T, b::T, c) where {T <: AnchoredInterval} end # Infer step for two-argument StepRange{<:AnchoredInterval} -function Base.colon(start::AnchoredInterval{P, T}, stop::AnchoredInterval{P, T}) where {P,T} +function Base.colon(start::AnchoredInterval{T, P}, stop::AnchoredInterval{T, P}) where {T,P} return colon(start, abs(P), stop) end @@ -206,11 +213,11 @@ end ##### SET OPERATIONS ##### -function Base.isempty(interval::AnchoredInterval{P, T}) where {P, T} +function Base.isempty(interval::AnchoredInterval{T, P}) where {T, P} return P == zero(P) && !isclosed(interval) end -function Base.intersect(a::AnchoredInterval{P, T}, b::AnchoredInterval{Q, T}) where {P,Q,T} +function Base.intersect(a::AnchoredInterval{T, P}, b::AnchoredInterval{T, Q}) where {T,P,Q} interval = invoke(intersect, Tuple{AbstractInterval{T}, AbstractInterval{T}}, a, b) sp = isa(P, Period) ? canonicalize(typeof(P), span(interval)) : span(interval) @@ -222,7 +229,7 @@ function Base.intersect(a::AnchoredInterval{P, T}, b::AnchoredInterval{Q, T}) wh new_P = sp end - return AnchoredInterval{new_P, T}(anchor, inclusivity(interval)) + return AnchoredInterval{T, new_P}(anchor, inclusivity(interval)) end ##### UTILITIES ##### diff --git a/src/description.jl b/src/description.jl index 8ad85aa4..17a51944 100644 --- a/src/description.jl +++ b/src/description.jl @@ -1,8 +1,10 @@ using Base.Dates: value, coarserperiod -description(interval::AnchoredInterval{P}) where P = description(interval, P > zero(P) ? "B" : "E") +function description(interval::AnchoredInterval{T, P}) where {T, P} + description(interval, P > zero(P) ? "B" : "E") +end -function description(interval::AnchoredInterval{P, T}, s::String) where {P, T} +function description(interval::AnchoredInterval{T, P}, s::String) where {T, P} return string( first(inclusivity(interval)) ? '[' : '(', description(anchor(interval), abs(P), s), @@ -10,7 +12,7 @@ function description(interval::AnchoredInterval{P, T}, s::String) where {P, T} ) end -function description(interval::AnchoredInterval{P, ZonedDateTime}, s::String) where P +function description(interval::AnchoredInterval{ZonedDateTime, P}, s::String) where P return string( first(inclusivity(interval)) ? '[' : '(', description(anchor(interval), abs(P), s), diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index d67c8798..b6c0a53f 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -4,18 +4,19 @@ using Intervals: canonicalize dt = DateTime(2016, 8, 11, 2) @testset "constructor" begin - expected = AnchoredInterval{Hour(-1), DateTime}(dt, Inclusivity(false, true)) - @test AnchoredInterval{Hour(-1), DateTime}(dt) == expected - @test AnchoredInterval{Hour(-1)}(dt) == expected + expected = AnchoredInterval{DateTime, Hour(-1)}(dt, Inclusivity(false, true)) + @test AnchoredInterval{DateTime, Hour(-1)}(dt) == expected + @test_throws MethodError AnchoredInterval{Hour(-1)}(dt) + @test AnchoredInterval(dt, Hour(-1)) == expected @test HourEnding{DateTime}(dt) == expected @test HourEnding(dt) == expected @test HE(dt) == expected @test HE(dt - Minute(59)) == expected - expected = AnchoredInterval{Hour(1), DateTime}(dt, Inclusivity(true, false)) - @test AnchoredInterval{Hour(1), DateTime}(dt) == expected - @test AnchoredInterval{Hour(1)}(dt) == expected - @test HourBeginning{DateTime}(dt) == expected + expected = AnchoredInterval{DateTime, Hour(1)}(dt, Inclusivity(true, false)) + @test AnchoredInterval{DateTime, Hour(1)}(dt) == expected + @test_throws MethodError AnchoredInterval{Hour(1)}(dt) + @test AnchoredInterval(dt, Hour(1)) == expected @test HourBeginning(dt) == expected @test HB(dt) == expected @test HB(dt + Minute(59)) == expected @@ -24,23 +25,23 @@ using Intervals: canonicalize @test HourEnding{DateTime}(dt, true, false) == HourEnding{DateTime}(dt, Inclusivity(true, false)) @test HourEnding(dt, true, false) == HourEnding(dt, Inclusivity(true, false)) - @test AnchoredInterval{Day(1), DateTime}(dt, false, false) == - AnchoredInterval{Day(1), DateTime}(dt, Inclusivity(false, false)) - @test AnchoredInterval{Day(1)}(dt, false, false) == - AnchoredInterval{Day(1)}(dt, Inclusivity(false, false)) + @test AnchoredInterval{DateTime, Day(1)}(dt, false, false) == + AnchoredInterval{DateTime, Day(1)}(dt, Inclusivity(false, false)) + @test AnchoredInterval(dt, Day(1), false, false) == + AnchoredInterval(dt, Day(1), Inclusivity(false, false)) @test HE(dt, true, true) == HourEnding(dt, Inclusivity(true, true)) @test HB(dt, true, true) == HourBeginning(dt, Inclusivity(true, true)) # Non-period AnchoredIntervals - @test AnchoredInterval{-10}(10) isa AnchoredInterval - @test AnchoredInterval{25}('a') isa AnchoredInterval + @test AnchoredInterval(10, -10) isa AnchoredInterval + @test AnchoredInterval('a', 25) isa AnchoredInterval end @testset "conversion" begin @testset "Date" begin d = Date(dt) - de = AnchoredInterval{Day(-1)}(d) - db = AnchoredInterval{Day(1)}(d) + de = AnchoredInterval(d, Day(-1)) + db = AnchoredInterval(d, Day(1)) @test Date(de) == d @test Date(db) == d end @@ -61,52 +62,52 @@ using Intervals: canonicalize @testset "accessors" begin inc = Inclusivity(true, true) - P = Minute(-15) - interval = AnchoredInterval{P}(dt, inc) + p = Minute(-15) + interval = AnchoredInterval(dt, p, inc) @test first(interval) == DateTime(2016, 8, 11, 1, 45) @test last(interval) == dt - @test span(interval) == -P + @test span(interval) == -p @test inclusivity(interval) == inc inc = Inclusivity(false, false) - P = Day(1) - interval = AnchoredInterval{P}(Date(dt), inc) + p = Day(1) + interval = AnchoredInterval(Date(dt), p, inc) @test first(interval) == Date(2016, 8, 11) @test last(interval) == Date(2016, 8, 12) - @test span(interval) == P + @test span(interval) == p @test inclusivity(interval) == inc # DST transition endpoint = ZonedDateTime(2018, 3, 11, 3, tz"America/Winnipeg") - interval = AnchoredInterval{Hour(-2)}(endpoint) + interval = AnchoredInterval(endpoint, Hour(-2)) @test span(interval) == Hour(2) startpoint = ZonedDateTime(2018, 3, 11, tz"America/Winnipeg") - interval = AnchoredInterval{Day(1)}(startpoint) + interval = AnchoredInterval(startpoint, Day(1)) @test first(interval) == startpoint @test last(interval) == ZonedDateTime(2018, 3, 12, tz"America/Winnipeg") @test span(interval) == Day(1) endpoint = ZonedDateTime(2018, 11, 4, 2, tz"America/Winnipeg") - interval = AnchoredInterval{Hour(-2)}(endpoint) + interval = AnchoredInterval(endpoint, Hour(-2)) @test span(interval) == Hour(2) startpoint = ZonedDateTime(2018, 11, 4, tz"America/Winnipeg") - interval = AnchoredInterval{Day(1)}(startpoint) + interval = AnchoredInterval(startpoint, Day(1)) @test first(interval) == startpoint @test last(interval) == ZonedDateTime(2018, 11, 5, tz"America/Winnipeg") @test span(interval) == Day(1) # Non-period AnchoredIntervals - interval = AnchoredInterval{-10}(10) + interval = AnchoredInterval(10, -10) @test first(interval) == 0 @test last(interval) == 10 @test inclusivity(interval) == Inclusivity(false, true) @test span(interval) == 10 - interval = AnchoredInterval{25}('a') + interval = AnchoredInterval('a', 25) @test first(interval) == 'a' @test last(interval) == 'z' @test inclusivity(interval) == Inclusivity(true, false) @@ -116,24 +117,24 @@ using Intervals: canonicalize @testset "display" begin @test sprint(show, HourEnding) == "HourEnding{T}" @test sprint(show, HourBeginning) == "HourBeginning{T}" - @test sprint(show, AnchoredInterval{Hour(-1)}) == - "Intervals.AnchoredInterval{-1 hour,T} where T" - @test sprint(show, AnchoredInterval{Hour(1)}) == - "Intervals.AnchoredInterval{1 hour,T} where T" + @test sprint(show, AnchoredInterval{T, Hour(-1)} where T) == + "Intervals.AnchoredInterval{T,-1 hour} where T" + @test sprint(show, AnchoredInterval{T, Hour(1)} where T) == + "Intervals.AnchoredInterval{T,1 hour} where T" @test sprint(show, HourEnding{DateTime}) == "HourEnding{DateTime}" @test sprint(show, HourBeginning{DateTime}) == "HourBeginning{DateTime}" - @test sprint(show, AnchoredInterval{Hour(-1), DateTime}) == "HourEnding{DateTime}" - @test sprint(show, AnchoredInterval{Hour(1), DateTime}) == "HourBeginning{DateTime}" - - @test sprint(show, AnchoredInterval{Day(-1)}) == - "Intervals.AnchoredInterval{-1 day,T} where T" - @test sprint(show, AnchoredInterval{Day(1)}) == - "Intervals.AnchoredInterval{1 day,T} where T" - @test sprint(show, AnchoredInterval{Day(-1), DateTime}) == - "AnchoredInterval{-1 day, DateTime}" - @test sprint(show, AnchoredInterval{Day(1), DateTime}) == - "AnchoredInterval{1 day, DateTime}" + @test sprint(show, AnchoredInterval{DateTime, Hour(-1)}) == "HourEnding{DateTime}" + @test sprint(show, AnchoredInterval{DateTime, Hour(1)}) == "HourBeginning{DateTime}" + + @test sprint(show, AnchoredInterval{T, Day(-1)} where T) == + "Intervals.AnchoredInterval{T,-1 day} where T" + @test sprint(show, AnchoredInterval{T, Day(1)} where T) == + "Intervals.AnchoredInterval{T,1 day} where T" + @test sprint(show, AnchoredInterval{DateTime, Day(-1)}) == + "AnchoredInterval{DateTime, -1 day}" + @test sprint(show, AnchoredInterval{DateTime, Day(1)}) == + "AnchoredInterval{DateTime, 1 day}" interval = HourEnding(dt) @test string(interval) == "(2016-08-11 HE02]" @@ -185,99 +186,99 @@ using Intervals: canonicalize "Inclusivity(false, true))", ) - interval = AnchoredInterval{Year(-1)}(Date(dt)) + interval = AnchoredInterval(Date(dt), Year(-1)) @test string(interval) == "(YE 2016-08-11]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{-1 year, Date}(2016-08-11, Inclusivity(false, true))" + "AnchoredInterval{Date, -1 year}(2016-08-11, Inclusivity(false, true))" - interval = AnchoredInterval{Year(-1)}(ceil(Date(dt), Year)) + interval = AnchoredInterval(ceil(Date(dt), Year), Year(-1)) @test string(interval) == "(YE 2017-01-01]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{-1 year, Date}(2017-01-01, Inclusivity(false, true))" + "AnchoredInterval{Date, -1 year}(2017-01-01, Inclusivity(false, true))" - interval = AnchoredInterval{Month(-1)}(dt) + interval = AnchoredInterval(dt, Month(-1)) @test string(interval) == "(MoE 2016-08-11 02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-1 month, DateTime}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, -1 month}(2016-08-11T02:00:00, ", "Inclusivity(false, true))", ) - interval = AnchoredInterval{Month(-1)}(ceil(dt, Month)) + interval = AnchoredInterval(ceil(dt, Month), Month(-1)) @test string(interval) == "(MoE 2016-09-01]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-1 month, DateTime}(2016-09-01T00:00:00, ", + "AnchoredInterval{DateTime, -1 month}(2016-09-01T00:00:00, ", "Inclusivity(false, true))", ) - interval = AnchoredInterval{Day(-1)}(DateTime(dt)) + interval = AnchoredInterval(DateTime(dt), Day(-1)) @test string(interval) == "(DE 2016-08-11 02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-1 day, DateTime}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, -1 day}(2016-08-11T02:00:00, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval{Day(-1)}(ceil(DateTime(dt), Day)) + interval = AnchoredInterval(ceil(DateTime(dt), Day), Day(-1)) @test string(interval) == "(DE 2016-08-12]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-1 day, DateTime}(2016-08-12T00:00:00, ", + "AnchoredInterval{DateTime, -1 day}(2016-08-12T00:00:00, ", "Inclusivity(false, true))" ) # Date(dt) will truncate the DateTime to the nearest day - interval = AnchoredInterval{Day(-1)}(Date(dt)) + interval = AnchoredInterval(Date(dt), Day(-1)) @test string(interval) == "(DE 2016-08-11]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{-1 day, Date}(2016-08-11, Inclusivity(false, true))" + "AnchoredInterval{Date, -1 day}(2016-08-11, Inclusivity(false, true))" - interval = AnchoredInterval{Minute(-5)}(dt) + interval = AnchoredInterval(dt, Minute(-5)) @test string(interval) == "(2016-08-11 5ME02:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-5 minutes, DateTime}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, -5 minutes}(2016-08-11T02:00:00, ", "Inclusivity(false, true))", ) - interval = AnchoredInterval{Second(-30)}(dt) + interval = AnchoredInterval(dt, Second(-30)) @test string(interval) == "(2016-08-11 30SE02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-30 seconds, DateTime}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, -30 seconds}(2016-08-11T02:00:00, ", "Inclusivity(false, true))", ) - interval = AnchoredInterval{Millisecond(-10)}(dt) + interval = AnchoredInterval(dt, Millisecond(-10)) @test string(interval) == "(2016-08-11 10msE02:00:00.000]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{-10 milliseconds, DateTime}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, -10 milliseconds}(2016-08-11T02:00:00, ", "Inclusivity(false, true))", ) # Non-period AnchoredIntervals - interval = AnchoredInterval{-10}(10) + interval = AnchoredInterval(10, -10) @test string(interval) == "(0..10]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{-10, $Int}(10, Inclusivity(false, true))" + "AnchoredInterval{$Int, -10}(10, Inclusivity(false, true))" - interval = AnchoredInterval{25}('a') + interval = AnchoredInterval('a', 25) @test string(interval) == "[a..z)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{25, Char}('a', Inclusivity(true, false))" + "AnchoredInterval{Char, 25}('a', Inclusivity(true, false))" end @testset "equality" begin he = HourEnding(dt) hb = HourBeginning(dt) - me = AnchoredInterval{Minute(-1)}(dt) + me = AnchoredInterval(dt, Minute(-1)) cpe = copy(he) cpb = copy(hb) @@ -361,9 +362,9 @@ using Intervals: canonicalize @test he + Minute(30) == HourEnding(dt + Minute(30)) @test he - Minute(30) == HourEnding(dt - Minute(30)) - ai = AnchoredInterval{Minute(-60)}(dt) - @test ai + Minute(30) == AnchoredInterval{Minute(-60)}(dt + Minute(30)) - @test ai - Minute(30) == AnchoredInterval{Minute(-60)}(dt - Minute(30)) + ai = AnchoredInterval(dt, Minute(-60)) + @test ai + Minute(30) == AnchoredInterval(dt + Minute(30), Minute(-60)) + @test ai - Minute(30) == AnchoredInterval(dt - Minute(30), Minute(-60)) # Subtracting AnchoredInterval{P, T} from T doesn't make sense if T is a TimeType @test_throws MethodError Hour(1) - he @@ -371,31 +372,31 @@ using Intervals: canonicalize # DST transition endpoint = ZonedDateTime(2018, 3, 11, 3, tz"America/Winnipeg") - interval = AnchoredInterval{Hour(-2)}(endpoint) + interval = AnchoredInterval(endpoint, Hour(-2)) @test span(interval) == Hour(2) endpoint = ZonedDateTime(2018, 11, 4, 2, tz"America/Winnipeg") - interval = AnchoredInterval{Hour(-2)}(endpoint) + interval = AnchoredInterval(endpoint, Hour(-2)) @test span(interval) == Hour(2) # Non-period AnchoredIntervals - @test AnchoredInterval{10}('a') + 2 == AnchoredInterval{10}('c') - @test AnchoredInterval{10}('d') - 2 == AnchoredInterval{10}('b') + @test AnchoredInterval('a', 10) + 2 == AnchoredInterval('c', 10) + @test AnchoredInterval('d', 10) - 2 == AnchoredInterval('b', 10) - @test AnchoredInterval{-1}(20) + 2 == AnchoredInterval{-1}(22) - @test AnchoredInterval{-1}(20) - 2 == AnchoredInterval{-1}(18) + @test AnchoredInterval(20, -1) + 2 == AnchoredInterval(22, -1) + @test AnchoredInterval(20, -1) - 2 == AnchoredInterval(18, -1) - @test -AnchoredInterval{2}(10) == AnchoredInterval{-2}(-10) # -[10,12)==(-12,-10] - @test -AnchoredInterval{-2}(10) == AnchoredInterval{2}(-10) # -(8,10]==[-10,-8) + @test -AnchoredInterval(10, 2) == AnchoredInterval(-10, -2) # -[10,12)==(-12,-10] + @test -AnchoredInterval(10, -2) == AnchoredInterval(-10, 2) # -(8,10]==[-10,-8) - @test 15 - AnchoredInterval{-2}(10) == AnchoredInterval{2}(5) # 15-(8,10]==[5,8) - @test 15 - AnchoredInterval{2}(10) == AnchoredInterval{-2}(5) # 15-[10,12)==(3,5] + @test 15 - AnchoredInterval(10, -2) == AnchoredInterval(5, 2) # 15-(8,10]==[5,8) + @test 15 - AnchoredInterval(10, 2) == AnchoredInterval(5, -2) # 15-[10,12)==(3,5] - @test_throws MethodError -AnchoredInterval{10}('a') - @test_throws MethodError -AnchoredInterval{-10}('z') + @test_throws MethodError -AnchoredInterval('a', 10) + @test_throws MethodError -AnchoredInterval('z', -10) - @test_throws MethodError 10 - AnchoredInterval{10}('a') - @test_throws MethodError 10 - AnchoredInterval{-10}('z') + @test_throws MethodError 10 - AnchoredInterval('a', 10) + @test_throws MethodError 10 - AnchoredInterval('z', -10) end @testset "range" begin @@ -426,20 +427,20 @@ using Intervals: canonicalize end @testset "isempty" begin - for P in [Year(1), Month(1), Day(1), Hour(1), Minute(1), Second(1)] + for p in [Year(1), Month(1), Day(1), Hour(1), Minute(1), Second(1)] for sign in [+, -] - @test !isempty(AnchoredInterval{sign(P)}(dt, Inclusivity(false, false))) - @test !isempty(AnchoredInterval{sign(P)}(dt, Inclusivity(false, true))) - @test !isempty(AnchoredInterval{sign(P)}(dt, Inclusivity(true, false))) - @test !isempty(AnchoredInterval{sign(P)}(dt, Inclusivity(true, true))) + @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(false, false))) + @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(false, true))) + @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(true, false))) + @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(true, true))) end end - for P in [Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)] - @test isempty(AnchoredInterval{P}(dt, Inclusivity(false, false))) - @test isempty(AnchoredInterval{P}(dt, Inclusivity(false, true))) - @test isempty(AnchoredInterval{P}(dt, Inclusivity(true, false))) - @test !isempty(AnchoredInterval{P}(dt, Inclusivity(true, true))) + for p in [Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)] + @test isempty(AnchoredInterval(dt, p, Inclusivity(false, false))) + @test isempty(AnchoredInterval(dt, p, Inclusivity(false, true))) + @test isempty(AnchoredInterval(dt, p, Inclusivity(true, false))) + @test !isempty(AnchoredInterval(dt, p, Inclusivity(true, true))) end end @@ -473,7 +474,7 @@ using Intervals: canonicalize @test isempty(intersect(HourEnding(dt), HourEnding(dt + Hour(1)))) # Single point overlap - expected = AnchoredInterval{Hour(0)}(dt, Inclusivity(true, true)) + expected = AnchoredInterval(dt, Hour(0), Inclusivity(true, true)) @test intersect( HourEnding(dt, Inclusivity(true, true)), HourEnding(dt + Hour(1), Inclusivity(true, true)), @@ -481,8 +482,8 @@ using Intervals: canonicalize # Hour overlap he = HourEnding(dt) - @test intersect(he, AnchoredInterval{Hour(-2)}(dt)) == he - @test intersect(AnchoredInterval{Hour(-3)}(dt + Hour(1)), he) == he + @test intersect(he, AnchoredInterval(dt, Hour(-2))) == he + @test intersect(AnchoredInterval(dt + Hour(1), Hour(-3)), he) == he @test intersect(HourBeginning(dt - Hour(1)), he) == HourBeginning(dt - Hour(1), Inclusivity(false, false)) @@ -499,11 +500,11 @@ using Intervals: canonicalize # This should probably be an AnchoredInterval{Hour(0)}, but it's not important @test intersect(HourEnding(dt), HourBeginning(dt)) == - AnchoredInterval{Hour(0)}(dt, Inclusivity(true, true)) + AnchoredInterval(dt, Hour(0), Inclusivity(true, true)) # Non-period AnchoredIntervals - @test intersect(AnchoredInterval{-2}(3), AnchoredInterval{-2}(4)) == - AnchoredInterval{-1}(3) + @test intersect(AnchoredInterval(3, -2), AnchoredInterval(4, -2)) == + AnchoredInterval(3, -1) end @testset "canonicalize" begin From 9d46dbf6a3a737644401a187ec5cc46cd6979c00 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 7 Mar 2018 16:32:55 -0600 Subject: [PATCH 3/4] AnchoredInterval refactored to use span as field --- README.md | 10 ++- docs/src/index.md | 10 ++- src/anchoredinterval.jl | 154 ++++++++++++++++++++++++++------------ src/description.jl | 12 +-- src/endpoint.jl | 3 + test/anchoredinterval.jl | 156 ++++++++++++++++++++++----------------- 6 files changed, 219 insertions(+), 126 deletions(-) diff --git a/README.md b/README.md index 46ed994c..ca315e64 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,12 @@ This package defines: * `AbstractInterval`, along with its subtypes: * `Interval{T}`, which represents a non-iterable range between two endpoints of type `T` - * `AnchoredInterval{T, P}`, which represents a non-iterable range defined by a single - value `anchor::T` and the value type `P` which represents the size of the range - * `HourEnding`, a type alias for `AnchoredInterval{T, Hour(-1)}` - * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour(1)}` + * `AnchoredInterval{T, S, E}`, which represents a non-iterable range defined by a single + value `anchor::T` and `span::S` which represents the size of the range. The type + parameter `E` is an instance of `Direction` and indicate whether the anchor is a + left-endpoint (`Beginning`) or a right-endpoing (`Ending`). + * `HourEnding`, a type alias for `AnchoredInterval{T, Hour, Ending}` + * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour, Beginning}` * `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the anchor up (`HE`) or down (`HB`) to the nearest hour * `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or diff --git a/docs/src/index.md b/docs/src/index.md index 5c74de0e..e9707fc0 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -3,10 +3,12 @@ This package defines: * `AbstractInterval`, along with its subtypes: * `Interval{T}`, which represents a non-iterable range between two endpoints of type `T` - * `AnchoredInterval{T, P}`, which represents a non-iterable range defined by a single - value `anchor::T` and the value type `P` which represents the size of the range - * `HourEnding`, a type alias for `AnchoredInterval{T, Hour(-1)}` - * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour(1)}` + * `AnchoredInterval{T, S, E}`, which represents a non-iterable range defined by a single + value `anchor::T` and `span::S` which represents the size of the range. The type + parameter `E` is an instance of `Direction` and indicate whether the anchor is a + left-endpoint (`Beginning`) or a right-endpoing (`Ending`). + * `HourEnding`, a type alias for `AnchoredInterval{T, Hour, Ending}` + * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour, Beginning}` * `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the anchor up (`HE`) or down (`HB`) to the nearest hour * `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index 7ffe7457..ecfeeb68 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -1,11 +1,11 @@ using Base.Dates: value, coarserperiod """ - AnchoredInterval{T, P}(anchor::T, [inclusivity::Inclusivity]) -> AnchoredInterval{T, P} - AnchoredInterval{T, P}(anchor::T, [closed_left::Bool, closed_right::Bool]) -> AnchoredInterval{T, P} + AnchoredInterval{T, S, E}(anchor::T, [span::S, inclusivity::Inclusivity]) + AnchoredInterval{T, S, E}(anchor::T, [span::S, closed_left::Bool, closed_right::Bool]) - AnchoredInterval(anchor::T, P, [inclusivity::Inclusivity]) -> AnchoredInterval{T, P} - AnchoredInterval(anchor::T, P, [closed_left::Bool, closed_right::Bool]) -> AnchoredInterval{T, P} + AnchoredInterval{T, S, E}(anchor::T, [inclusivity::Inclusivity]) + AnchoredInterval{T, S, E}(anchor::T, [closed_left::Bool, closed_right::Bool]) `AnchoredInterval` is a subtype of `AbstractInterval` that represents a non-iterable range or span of values defined not by two endpoints but instead by a single `anchor` point and @@ -24,8 +24,9 @@ included for positive values of `P` and the greater endpoint included for negati range of values. This happens most often with dates and times, where "HE15" is often used as shorthand for (14:00..15:00]. -To this end, `HourEnding` is a type alias for `AnchoredInterval{T, Hour(-1)} where T`. -Similarly, `HourBeginning` is a type alias for `AnchoredInterval{T, Hour(1)} where T`. +To this end, `HourEnding` is a type alias for `AnchoredInterval{T, Hour, Ending} where T`. +Similarly, `HourBeginning` is a type alias for +`AnchoredInterval{T, Hour, Beginning} where T`. ### Rounding @@ -52,41 +53,80 @@ HourBeginning{DateTime}(2016-08-11T02:00:00, Inclusivity(true, false)) ```julia julia> AnchoredInterval(DateTime(2016, 8, 11, 12), Hour(-1)) -HourEnding{DateTime}(2016-08-11T12:00:00, Inclusivity(false, true)) +HourEnding{DateTime}(2016-08-11T12:00:00, -1 hour, Inclusivity(false, true)) julia> AnchoredInterval(DateTime(2016, 8, 11), Day(1)) -AnchoredInterval{DateTime, 1 day}(2016-08-11T00:00:00, Inclusivity(true, false)) +AnchoredInterval{DateTime, Day, Beginning}(2016-08-11T00:00:00, 1 day, Inclusivity(true, false)) julia> AnchoredInterval(DateTime(2016, 8, 11, 12, 30), Minute(5), true, true) -AnchoredInterval{DateTime, 5 minutes}(2016-08-11T12:30:00, Inclusivity(true, true)) +AnchoredInterval{DateTime, Minute, Beginning}(2016-08-11T12:30:00, 5 minutes, Inclusivity(true, true)) ``` See also: [`Interval`](@ref), [`Inclusivity`](@ref), [`HE`](@ref), [`HB`](@ref) """ -struct AnchoredInterval{T, P} <: AbstractInterval{T} +struct AnchoredInterval{T, S, E} <: AbstractInterval{T} anchor::T + span::S inclusivity::Inclusivity + + function AnchoredInterval{T, S, E}(anchor::T, span::S, inc::Inclusivity) where {T, S, E} + @assert E isa Direction + if span < zero(S) + @assert E == Ending + elseif span > zero(S) + @assert E == Beginning + else + @assert E isa Direction + end + @assert typeof(anchor + span) == T + new{T, S, E}(anchor, span, inc) + end +end + +function AnchoredInterval{T, S, E}(anchor::T, span::S, x::Bool, y::Bool) where {T, S, E} + AnchoredInterval{T, S, E}(anchor, span, Inclusivity(x, y)) +end +function AnchoredInterval{T, S, E}(anchor::T, span::S) where {T, S, E} + # If an interval is anchored to the lesser endpoint, default to Inclusivity(false, true) + # If an interval is anchored to the greater endpoint, default to Inclusivity(true, false) + AnchoredInterval{T, S, E}(anchor, span, Inclusivity(span ≥ zero(S), span ≤ zero(S))) +end +function AnchoredInterval{T, S, E}(anchor::T, inc::Inclusivity) where {T, S, E} + span = E == Ending ? -oneunit(S) : oneunit(S) + AnchoredInterval{T, S, E}(anchor, span, inc) +end +function AnchoredInterval{T, S, E}(anchor::T, x::Bool, y::Bool) where {T, S, E} + AnchoredInterval{T, S, E}(anchor, Inclusivity(x, y)) +end +function AnchoredInterval{T, S, E}(anchor::T) where {T, S, E} + span = E == Ending ? -oneunit(S) : oneunit(S) + AnchoredInterval{T, S, E}(anchor, span) end -# When an interval is anchored to the lesser endpoint, default to Inclusivity(false, true) -# When an interval is anchored to the greater endpoint, default to Inclusivity(true, false) -function AnchoredInterval{T, P}(i::T) where {T, P} - return AnchoredInterval{T, P}(i::T, Inclusivity(P ≥ zero(P), P ≤ zero(P))) +function AnchoredInterval{T, S}(anchor::T, span::S, args...) where {T, S} + E = if span < zero(S) + Ending + elseif span > zero(S) + Beginning + else + throw(ArgumentError("Must specify endpoint type when span is zero")) + end + AnchoredInterval{T, S, E}(anchor, span, args...) end -function AnchoredInterval{T, P}(i::T, x::Bool, y::Bool) where {T, P} - return AnchoredInterval{T, P}(i, Inclusivity(x, y)) +function AnchoredInterval{T}(anchor::T, span::S, args...) where {T, S} + AnchoredInterval{T, S}(anchor, span, args...) end -AnchoredInterval(i::T, span, inc::Inclusivity) where T = AnchoredInterval{T, span}(i, inc) -AnchoredInterval(i::T, span, x::Bool, y::Bool) where T = AnchoredInterval{T, span}(i, x, y) -AnchoredInterval(i::T, span) where T = AnchoredInterval{T, span}(i) +function AnchoredInterval(anchor::T, span::S, args...) where {T, S} + AnchoredInterval{T, S}(anchor, span, args...) +end -const HourEnding{T} = AnchoredInterval{T, Hour(-1)} where T <: TimeType +const HourEnding{T} = AnchoredInterval{T, Hour, Ending} where T <: TimeType HourEnding(a::T, args...) where T = HourEnding{T}(a, args...) -const HourBeginning{T} = AnchoredInterval{T, Hour(1)} where T <: TimeType +const HourBeginning{T} = AnchoredInterval{T, Hour, Beginning} where T <: TimeType HourBeginning(a::T, args...) where T = HourBeginning{T}(a, args...) """ @@ -105,22 +145,22 @@ nearest hour. """ HB(a, args...) = HourBeginning(floor(a, Hour), args...) -function Base.copy(x::AnchoredInterval{T, P}) where {T, P} - return AnchoredInterval{T, P}(anchor(x), inclusivity(x)) +function Base.copy(x::AnchoredInterval{T, S, E}) where {T, S, E} + return AnchoredInterval{T, S, E}(anchor(x), inclusivity(x)) end ##### ACCESSORS ##### -function Base.first(interval::AnchoredInterval{T, P}) where {T, P} - min(interval.anchor, interval.anchor + P) +function Base.first(interval::AnchoredInterval) + min(interval.anchor, interval.anchor + interval.span) end -function Base.last(interval::AnchoredInterval{T, P}) where {T, P} - max(interval.anchor, interval.anchor + P) +function Base.last(interval::AnchoredInterval) + max(interval.anchor, interval.anchor + interval.span) end anchor(interval::AnchoredInterval) = interval.anchor -span(interval::AnchoredInterval{T, P}) where {T, P} = abs(P) +span(interval::AnchoredInterval) = abs(interval.span) ##### CONVERSION ##### @@ -132,6 +172,19 @@ function Base.convert(::Type{Interval{T}}, interval::AnchoredInterval{T}) where return Interval{T}(first(interval), last(interval), inclusivity(interval)) end +# Conversion methods which currently aren't needed but could prove useful. Commented out +# since these are untested. + +#= +function Base.convert(::Type{AnchoredInterval{Ending}}, interval::Interval{T}) where T + AnchoredInterval{T}(last(interval), -span(interval), inclusivity(interval)) +end + +function Base.convert(::Type{AnchoredInterval{Beginning}}, interval::Interval{T}) where T + AnchoredInterval{T}(first(interval), span(interval), inclusivity(interval)) +end +=# + Base.convert(::Type{T}, interval::AnchoredInterval{T}) where T = anchor(interval) # Date/DateTime attempt to convert to Int64 instead of falling back to convert(T, ...) @@ -146,17 +199,21 @@ Base.show(io::IO, ::Type{HourBeginning}) = print(io, "HourBeginning{T}") Base.show(io::IO, ::Type{HourEnding{T}}) where T <: TimeType = print(io, "HourEnding{$T}") Base.show(io::IO, ::Type{HourBeginning{T}}) where T <: TimeType = print(io, "HourBeginning{$T}") -function Base.show(io::IO, ::Type{AnchoredInterval{T, P}}) where {T, P} - print(io, "AnchoredInterval{$T, $P}") +function Base.show(io::IO, ::Type{AnchoredInterval{T, S, E}}) where {T, S, E} + d = E == Beginning ? "Beginning" : "Ending" + print(io, "AnchoredInterval{$T, $S, $d}") end -function Base.show(io::IO, interval::T) where T <: AnchoredInterval +function Base.show(io::IO, interval::AnchoredInterval) if get(io, :compact, false) print(io, interval) else - print(io, "$T(") + show(io, typeof(interval)) + print(io, "(") show(io, anchor(interval)) print(io, ", ") + show(io, interval.span) + print(io, ", ") show(io, inclusivity(interval)) print(io, ")") end @@ -173,7 +230,7 @@ end ##### ARITHMETIC ##### -Base.:+(a::T, b) where {T <: AnchoredInterval} = T(anchor(a) + b, inclusivity(a)) +Base.:+(a::T, b) where {T <: AnchoredInterval} = T(anchor(a) + b, a.span, inclusivity(a)) Base.:+(a, b::AnchoredInterval) = b + a Base.:-(a::AnchoredInterval, b) = a + -b @@ -183,15 +240,20 @@ Base.:-(a::AnchoredInterval, b::AnchoredInterval) = anchor(a) - anchor(b) Base.:-(a::T, b::AnchoredInterval{T}) where {T <: Number} = a + -b -function Base.:-(a::AnchoredInterval{T, P}) where {T <: Number, P} +function Base.:-(a::AnchoredInterval{T, S, Ending}) where {T <: Number, S} + inc = inclusivity(a) + AnchoredInterval{T, S, Beginning}(-anchor(a), -a.span, Inclusivity(last(inc), first(inc))) +end + +function Base.:-(a::AnchoredInterval{T, S, Beginning}) where {T <: Number, S} inc = inclusivity(a) - AnchoredInterval{T, -P}(-anchor(a), Inclusivity(last(inc), first(inc))) + AnchoredInterval{T, S, Ending}(-anchor(a), -a.span, Inclusivity(last(inc), first(inc))) end ##### EQUALITY ##### # Required for min/max of AnchoredInterval{LaxZonedDateTime} when the anchor is AMB or DNE -function Base.:<(a::AnchoredInterval{T, P}, b::AnchoredInterval{T, P}) where {T, P} +function Base.:<(a::AnchoredInterval{T, S, E}, b::AnchoredInterval{T, S, E}) where {T, S, E} return anchor(a) < anchor(b) end @@ -203,8 +265,8 @@ function Base.steprem(a::T, b::T, c) where {T <: AnchoredInterval} end # Infer step for two-argument StepRange{<:AnchoredInterval} -function Base.colon(start::AnchoredInterval{T, P}, stop::AnchoredInterval{T, P}) where {T,P} - return colon(start, abs(P), stop) +function Base.colon(start::AnchoredInterval{T, S}, stop::AnchoredInterval{T, S}) where {T,S} + return colon(start, oneunit(S), stop) end function Base.length(r::StepRange{<:AnchoredInterval}) @@ -213,23 +275,23 @@ end ##### SET OPERATIONS ##### -function Base.isempty(interval::AnchoredInterval{T, P}) where {T, P} - return P == zero(P) && !isclosed(interval) +function Base.isempty(interval::AnchoredInterval{T, S}) where {T, S} + return span(interval) == zero(S) && !isclosed(interval) end -function Base.intersect(a::AnchoredInterval{T, P}, b::AnchoredInterval{T, Q}) where {T,P,Q} +function Base.intersect(a::AnchoredInterval{T, S, E}, b::AnchoredInterval{T, S, E}) where {T, S, E} interval = invoke(intersect, Tuple{AbstractInterval{T}, AbstractInterval{T}}, a, b) - sp = isa(P, Period) ? canonicalize(typeof(P), span(interval)) : span(interval) - if P ≤ zero(P) + sp = S <: Period ? canonicalize(S, span(interval)) : span(interval) + if E == Ending anchor = last(interval) - new_P = -sp + sp = -sp else anchor = first(interval) - new_P = sp + sp = sp end - return AnchoredInterval{T, new_P}(anchor, inclusivity(interval)) + return AnchoredInterval{T, S, E}(anchor, sp, inclusivity(interval)) end ##### UTILITIES ##### diff --git a/src/description.jl b/src/description.jl index 17a51944..895ea96a 100644 --- a/src/description.jl +++ b/src/description.jl @@ -1,21 +1,21 @@ using Base.Dates: value, coarserperiod -function description(interval::AnchoredInterval{T, P}) where {T, P} - description(interval, P > zero(P) ? "B" : "E") +function description(interval::AnchoredInterval{T, S, E}) where {T, S, E} + description(interval, E == Beginning ? "B" : "E") end -function description(interval::AnchoredInterval{T, P}, s::String) where {T, P} +function description(interval::AnchoredInterval, s::String) return string( first(inclusivity(interval)) ? '[' : '(', - description(anchor(interval), abs(P), s), + description(anchor(interval), span(interval), s), last(inclusivity(interval)) ? ']' : ')', ) end -function description(interval::AnchoredInterval{ZonedDateTime, P}, s::String) where P +function description(interval::AnchoredInterval{ZonedDateTime}, s::String) return string( first(inclusivity(interval)) ? '[' : '(', - description(anchor(interval), abs(P), s), + description(anchor(interval), span(interval), s), anchor(interval).zone.offset, last(inclusivity(interval)) ? ']' : ')', ) diff --git a/src/endpoint.jl b/src/endpoint.jl index c6695564..075096ee 100644 --- a/src/endpoint.jl +++ b/src/endpoint.jl @@ -2,6 +2,9 @@ struct Direction{T} end const Left = Direction{:Left}() const Right = Direction{:Right}() +const Beginning = Left +const Ending = Right + struct Endpoint{T, D} endpoint::T included::Bool diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index b6c0a53f..89b36280 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -1,11 +1,11 @@ -using Intervals: canonicalize +using Intervals: Beginning, Ending, canonicalize @testset "AnchoredInterval" begin dt = DateTime(2016, 8, 11, 2) @testset "constructor" begin - expected = AnchoredInterval{DateTime, Hour(-1)}(dt, Inclusivity(false, true)) - @test AnchoredInterval{DateTime, Hour(-1)}(dt) == expected + expected = AnchoredInterval{DateTime, Hour}(dt, Hour(-1), Inclusivity(false, true)) + @test AnchoredInterval{DateTime, Hour}(dt, Hour(-1)) == expected @test_throws MethodError AnchoredInterval{Hour(-1)}(dt) @test AnchoredInterval(dt, Hour(-1)) == expected @test HourEnding{DateTime}(dt) == expected @@ -13,8 +13,8 @@ using Intervals: canonicalize @test HE(dt) == expected @test HE(dt - Minute(59)) == expected - expected = AnchoredInterval{DateTime, Hour(1)}(dt, Inclusivity(true, false)) - @test AnchoredInterval{DateTime, Hour(1)}(dt) == expected + expected = AnchoredInterval{DateTime, Hour}(dt, Hour(1), Inclusivity(true, false)) + @test AnchoredInterval{DateTime, Hour}(dt, Hour(1)) == expected @test_throws MethodError AnchoredInterval{Hour(1)}(dt) @test AnchoredInterval(dt, Hour(1)) == expected @test HourBeginning(dt) == expected @@ -25,8 +25,8 @@ using Intervals: canonicalize @test HourEnding{DateTime}(dt, true, false) == HourEnding{DateTime}(dt, Inclusivity(true, false)) @test HourEnding(dt, true, false) == HourEnding(dt, Inclusivity(true, false)) - @test AnchoredInterval{DateTime, Day(1)}(dt, false, false) == - AnchoredInterval{DateTime, Day(1)}(dt, Inclusivity(false, false)) + @test AnchoredInterval{DateTime}(dt, Day(1), false, false) == + AnchoredInterval{DateTime}(dt, Day(1), Inclusivity(false, false)) @test AnchoredInterval(dt, Day(1), false, false) == AnchoredInterval(dt, Day(1), Inclusivity(false, false)) @test HE(dt, true, true) == HourEnding(dt, Inclusivity(true, true)) @@ -117,108 +117,114 @@ using Intervals: canonicalize @testset "display" begin @test sprint(show, HourEnding) == "HourEnding{T}" @test sprint(show, HourBeginning) == "HourBeginning{T}" - @test sprint(show, AnchoredInterval{T, Hour(-1)} where T) == - "Intervals.AnchoredInterval{T,-1 hour} where T" - @test sprint(show, AnchoredInterval{T, Hour(1)} where T) == - "Intervals.AnchoredInterval{T,1 hour} where T" + @test sprint(show, AnchoredInterval{T, Hour, Ending} where T) == + "Intervals.AnchoredInterval{T,$Hour,$Ending} where T" + @test sprint(show, AnchoredInterval{T, Hour, Beginning} where T) == + "Intervals.AnchoredInterval{T,$Hour,$Beginning} where T" @test sprint(show, HourEnding{DateTime}) == "HourEnding{DateTime}" @test sprint(show, HourBeginning{DateTime}) == "HourBeginning{DateTime}" - @test sprint(show, AnchoredInterval{DateTime, Hour(-1)}) == "HourEnding{DateTime}" - @test sprint(show, AnchoredInterval{DateTime, Hour(1)}) == "HourBeginning{DateTime}" - - @test sprint(show, AnchoredInterval{T, Day(-1)} where T) == - "Intervals.AnchoredInterval{T,-1 day} where T" - @test sprint(show, AnchoredInterval{T, Day(1)} where T) == - "Intervals.AnchoredInterval{T,1 day} where T" - @test sprint(show, AnchoredInterval{DateTime, Day(-1)}) == - "AnchoredInterval{DateTime, -1 day}" - @test sprint(show, AnchoredInterval{DateTime, Day(1)}) == - "AnchoredInterval{DateTime, 1 day}" + @test sprint(show, AnchoredInterval{DateTime, Hour, Ending}) == + "HourEnding{DateTime}" + @test sprint(show, AnchoredInterval{DateTime, Hour, Beginning}) == + "HourBeginning{DateTime}" + + @test sprint(show, AnchoredInterval{T, Day, Ending} where T) == + "Intervals.AnchoredInterval{T,$Day,$Ending} where T" + @test sprint(show, AnchoredInterval{T, Day, Beginning} where T) == + "Intervals.AnchoredInterval{T,$Day,$Beginning} where T" + @test sprint(show, AnchoredInterval{DateTime, Day, Ending}) == + "AnchoredInterval{DateTime, $Day, Ending}" + @test sprint(show, AnchoredInterval{DateTime, Day, Beginning}) == + "AnchoredInterval{DateTime, $Day, Beginning}" interval = HourEnding(dt) @test string(interval) == "(2016-08-11 HE02]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2016-08-11T02:00:00, Inclusivity(false, true))" + "HourEnding{DateTime}(2016-08-11T02:00:00, -1 hour, Inclusivity(false, true))" interval = HourEnding(DateTime(2013, 2, 13), Inclusivity(true, false)) @test string(interval) == "[2013-02-12 HE24)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2013-02-13T00:00:00, Inclusivity(true, false))" + "HourEnding{DateTime}(2013-02-13T00:00:00, -1 hour, Inclusivity(true, false))" interval = HourEnding(dt + Minute(15) + Second(30)) @test string(interval) == "(2016-08-11 HE02:15:30]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2016-08-11T02:15:30, Inclusivity(false, true))" + "HourEnding{DateTime}(2016-08-11T02:15:30, -1 hour, Inclusivity(false, true))" interval = HourEnding(dt + Millisecond(2)) @test string(interval) == "(2016-08-11 HE02:00:00.002]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2016-08-11T02:00:00.002, Inclusivity(false, true))" + "HourEnding{DateTime}(2016-08-11T02:00:00.002, -1 hour, Inclusivity(false, true))" interval = HourEnding(DateTime(2013, 2, 13, 0, 1), Inclusivity(true, false)) @test string(interval) == "[2013-02-13 HE00:01:00)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2013-02-13T00:01:00, Inclusivity(true, false))" + "HourEnding{DateTime}(2013-02-13T00:01:00, -1 hour, Inclusivity(true, false))" interval = HourBeginning(dt) @test string(interval) == "[2016-08-11 HB02)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourBeginning{DateTime}(2016-08-11T02:00:00, Inclusivity(true, false))" + "HourBeginning{DateTime}(2016-08-11T02:00:00, 1 hour, Inclusivity(true, false))" interval = HourBeginning(DateTime(2013, 2, 13), Inclusivity(false, true)) @test string(interval) == "(2013-02-13 HB00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourBeginning{DateTime}(2013-02-13T00:00:00, Inclusivity(false, true))" + "HourBeginning{DateTime}(2013-02-13T00:00:00, 1 hour, Inclusivity(false, true))" interval = HourEnding(ZonedDateTime(dt, tz"America/Winnipeg")) @test string(interval) == "(2016-08-11 HE02-05:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "HourEnding{$ZonedDateTime}(2016-08-11T02:00:00-05:00, ", + "HourEnding{$ZonedDateTime}(2016-08-11T02:00:00-05:00, -1 hour, ", "Inclusivity(false, true))", ) interval = AnchoredInterval(Date(dt), Year(-1)) @test string(interval) == "(YE 2016-08-11]" @test sprint(showcompact, interval) == string(interval) - @test sprint(show, interval) == - "AnchoredInterval{Date, -1 year}(2016-08-11, Inclusivity(false, true))" + @test sprint(show, interval) == string( + "AnchoredInterval{Date, $Year, Ending}(2016-08-11, -1 year, ", + "Inclusivity(false, true))" + ) interval = AnchoredInterval(ceil(Date(dt), Year), Year(-1)) @test string(interval) == "(YE 2017-01-01]" @test sprint(showcompact, interval) == string(interval) - @test sprint(show, interval) == - "AnchoredInterval{Date, -1 year}(2017-01-01, Inclusivity(false, true))" + @test sprint(show, interval) == string( + "AnchoredInterval{Date, $Year, Ending}(2017-01-01, -1 year, ", + "Inclusivity(false, true))" + ) interval = AnchoredInterval(dt, Month(-1)) @test string(interval) == "(MoE 2016-08-11 02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -1 month}(2016-08-11T02:00:00, ", - "Inclusivity(false, true))", + "AnchoredInterval{DateTime, $Month, Ending}(2016-08-11T02:00:00, -1 month, ", + "Inclusivity(false, true))" ) interval = AnchoredInterval(ceil(dt, Month), Month(-1)) @test string(interval) == "(MoE 2016-09-01]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -1 month}(2016-09-01T00:00:00, ", - "Inclusivity(false, true))", + "AnchoredInterval{DateTime, $Month, Ending}(2016-09-01T00:00:00, -1 month, ", + "Inclusivity(false, true))" ) interval = AnchoredInterval(DateTime(dt), Day(-1)) @test string(interval) == "(DE 2016-08-11 02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -1 day}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, $Day, Ending}(2016-08-11T02:00:00, -1 day, ", "Inclusivity(false, true))" ) @@ -226,7 +232,7 @@ using Intervals: canonicalize @test string(interval) == "(DE 2016-08-12]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -1 day}(2016-08-12T00:00:00, ", + "AnchoredInterval{DateTime, $Day, Ending}(2016-08-12T00:00:00, -1 day, ", "Inclusivity(false, true))" ) @@ -234,14 +240,16 @@ using Intervals: canonicalize interval = AnchoredInterval(Date(dt), Day(-1)) @test string(interval) == "(DE 2016-08-11]" @test sprint(showcompact, interval) == string(interval) - @test sprint(show, interval) == - "AnchoredInterval{Date, -1 day}(2016-08-11, Inclusivity(false, true))" + @test sprint(show, interval) == string( + "AnchoredInterval{Date, $Day, Ending}(2016-08-11, -1 day, ", + "Inclusivity(false, true))" + ) interval = AnchoredInterval(dt, Minute(-5)) @test string(interval) == "(2016-08-11 5ME02:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -5 minutes}(2016-08-11T02:00:00, ", + "AnchoredInterval{DateTime, $Minute, Ending}(2016-08-11T02:00:00, -5 minutes, ", "Inclusivity(false, true))", ) @@ -249,16 +257,16 @@ using Intervals: canonicalize @test string(interval) == "(2016-08-11 30SE02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -30 seconds}(2016-08-11T02:00:00, ", - "Inclusivity(false, true))", + "AnchoredInterval{DateTime, $Second, Ending}(2016-08-11T02:00:00, ", + "-30 seconds, Inclusivity(false, true))", ) interval = AnchoredInterval(dt, Millisecond(-10)) @test string(interval) == "(2016-08-11 10msE02:00:00.000]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, -10 milliseconds}(2016-08-11T02:00:00, ", - "Inclusivity(false, true))", + "AnchoredInterval{DateTime, $Millisecond, Ending}(2016-08-11T02:00:00, ", + "-10 milliseconds, Inclusivity(false, true))", ) # Non-period AnchoredIntervals @@ -266,13 +274,13 @@ using Intervals: canonicalize @test string(interval) == "(0..10]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{$Int, -10}(10, Inclusivity(false, true))" + "AnchoredInterval{$Int, $Int, Ending}(10, -10, Inclusivity(false, true))" interval = AnchoredInterval('a', 25) @test string(interval) == "[a..z)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{Char, 25}('a', Inclusivity(true, false))" + "AnchoredInterval{Char, $Int, Beginning}('a', 25, Inclusivity(true, false))" end @testset "equality" begin @@ -427,20 +435,26 @@ using Intervals: canonicalize end @testset "isempty" begin - for p in [Year(1), Month(1), Day(1), Hour(1), Minute(1), Second(1)] - for sign in [+, -] - @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(false, false))) - @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(false, true))) - @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(true, false))) - @test !isempty(AnchoredInterval(dt, sign(p), Inclusivity(true, true))) - end + for P in [Year, Month, Day, Hour, Minute, Second], sign in [+, -] + span = sign(oneunit(P)) + + @test !isempty(AnchoredInterval(dt, span, Inclusivity(false, false))) + @test !isempty(AnchoredInterval(dt, span, Inclusivity(false, true))) + @test !isempty(AnchoredInterval(dt, span, Inclusivity(true, false))) + @test !isempty(AnchoredInterval(dt, span, Inclusivity(true, true))) end - for p in [Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)] - @test isempty(AnchoredInterval(dt, p, Inclusivity(false, false))) - @test isempty(AnchoredInterval(dt, p, Inclusivity(false, true))) - @test isempty(AnchoredInterval(dt, p, Inclusivity(true, false))) - @test !isempty(AnchoredInterval(dt, p, Inclusivity(true, true))) + for P in [Year, Month, Day, Hour, Minute, Second] + span = zero(P) + @test_throws ArgumentError AnchoredInterval(dt, span) + + for E in [Ending, Beginning] + T = AnchoredInterval{DateTime, P, E} + @test isempty(T(dt, span, Inclusivity(false, false))) + @test isempty(T(dt, span, Inclusivity(false, true))) + @test isempty(T(dt, span, Inclusivity(true, false))) + @test !isempty(T(dt, span, Inclusivity(true, true))) + end end end @@ -472,9 +486,10 @@ using Intervals: canonicalize @testset "intersect" begin # Adjacent @test isempty(intersect(HourEnding(dt), HourEnding(dt + Hour(1)))) + @test isempty(intersect(HourBeginning(dt), HourBeginning(dt + Hour(1)))) # Single point overlap - expected = AnchoredInterval(dt, Hour(0), Inclusivity(true, true)) + expected = HourEnding(dt, Hour(0), Inclusivity(true, true)) @test intersect( HourEnding(dt, Inclusivity(true, true)), HourEnding(dt + Hour(1), Inclusivity(true, true)), @@ -485,7 +500,7 @@ using Intervals: canonicalize @test intersect(he, AnchoredInterval(dt, Hour(-2))) == he @test intersect(AnchoredInterval(dt + Hour(1), Hour(-3)), he) == he @test intersect(HourBeginning(dt - Hour(1)), he) == - HourBeginning(dt - Hour(1), Inclusivity(false, false)) + Interval(dt - Hour(1), dt, Inclusivity(false, false)) # Identical save for inclusivity expected = HourEnding(dt, Inclusivity(false, false)) @@ -498,9 +513,18 @@ using Intervals: canonicalize HourEnding(dt, Inclusivity(true, false)), ) == expected - # This should probably be an AnchoredInterval{Hour(0)}, but it's not important - @test intersect(HourEnding(dt), HourBeginning(dt)) == - AnchoredInterval(dt, Hour(0), Inclusivity(true, true)) + expected = HourBeginning(dt, Inclusivity(false, false)) + @test intersect( + HourBeginning(dt, Inclusivity(false, false)), + HourBeginning(dt, Inclusivity(true, true)), + ) == expected + @test intersect( + HourBeginning(dt, Inclusivity(false, true)), + HourBeginning(dt, Inclusivity(true, false)), + ) == expected + + # Intersection of HourEnding and HourBeginning + @test intersect(HourEnding(dt), HourBeginning(dt)) == Interval(dt, dt) # Non-period AnchoredIntervals @test intersect(AnchoredInterval(3, -2), AnchoredInterval(4, -2)) == From cc6f3df990c096ef0a59cddc9f4213d075c1a344 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Thu, 8 Mar 2018 10:59:10 -0600 Subject: [PATCH 4/4] Make AnchoredInterval span field always postive --- README.md | 6 +- docs/src/index.md | 6 +- src/Intervals.jl | 2 + src/anchoredinterval.jl | 98 ++++++++++------------ test/anchoredinterval.jl | 176 ++++++++++++++++++++------------------- 5 files changed, 143 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index ca315e64..60bbfc86 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ This package defines: value `anchor::T` and `span::S` which represents the size of the range. The type parameter `E` is an instance of `Direction` and indicate whether the anchor is a left-endpoint (`Beginning`) or a right-endpoing (`Ending`). - * `HourEnding`, a type alias for `AnchoredInterval{T, Hour, Ending}` - * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour, Beginning}` + * `IntervalEnding`, a type alias for `AnchoredInterval{T, S, Ending}` + * `IntervalBeginning`, a type alias for `AnchoredInterval{T, S, Beginning}` + * `HourEnding`, a type alias for `IntervalEnding{T, Hour}` + * `HourBeginning`, a type alias for `IntervalBeginning{T, Hour}` * `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the anchor up (`HE`) or down (`HB`) to the nearest hour * `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or diff --git a/docs/src/index.md b/docs/src/index.md index e9707fc0..dcdce5af 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -7,8 +7,10 @@ This package defines: value `anchor::T` and `span::S` which represents the size of the range. The type parameter `E` is an instance of `Direction` and indicate whether the anchor is a left-endpoint (`Beginning`) or a right-endpoing (`Ending`). - * `HourEnding`, a type alias for `AnchoredInterval{T, Hour, Ending}` - * `HourBeginning`, a type alias for `AnchoredInterval{T, Hour, Beginning}` + * `IntervalEnding`, a type alias for `AnchoredInterval{T, S, Ending}` + * `IntervalBeginning`, a type alias for `AnchoredInterval{T, S, Beginning}` + * `HourEnding`, a type alias for `IntervalEnding{T, Hour}` + * `HourBeginning`, a type alias for `IntervalBeginning{T, Hour}` * `HE` and `HB`, pseudoconstructors for `HourEnding` and `HourBeginning` that round the anchor up (`HE`) or down (`HB`) to the nearest hour * `Inclusivity`, which represents whether an `AbstractInterval` is open, half-open, or diff --git a/src/Intervals.jl b/src/Intervals.jl index 04354287..82fbbe52 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -19,6 +19,8 @@ include("description.jl") export AbstractInterval, Interval, AnchoredInterval, + IntervalEnding, + IntervalBeginning, HourEnding, HourBeginning, HE, diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index ecfeeb68..837991b4 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -52,13 +52,13 @@ HourBeginning{DateTime}(2016-08-11T02:00:00, Inclusivity(true, false)) ### Example ```julia -julia> AnchoredInterval(DateTime(2016, 8, 11, 12), Hour(-1)) -HourEnding{DateTime}(2016-08-11T12:00:00, -1 hour, Inclusivity(false, true)) +julia> IntervalEnding(DateTime(2016, 8, 11, 12), Hour(1)) +HourEnding{DateTime}(2016-08-11T12:00:00, 1 hour, Inclusivity(false, true)) -julia> AnchoredInterval(DateTime(2016, 8, 11), Day(1)) +julia> IntervalBeginning(DateTime(2016, 8, 11), Day(1)) AnchoredInterval{DateTime, Day, Beginning}(2016-08-11T00:00:00, 1 day, Inclusivity(true, false)) -julia> AnchoredInterval(DateTime(2016, 8, 11, 12, 30), Minute(5), true, true) +julia> IntervalBeginning(DateTime(2016, 8, 11, 12, 30), Minute(5), true, true) AnchoredInterval{DateTime, Minute, Beginning}(2016-08-11T12:30:00, 5 minutes, Inclusivity(true, true)) ``` @@ -71,13 +71,7 @@ struct AnchoredInterval{T, S, E} <: AbstractInterval{T} function AnchoredInterval{T, S, E}(anchor::T, span::S, inc::Inclusivity) where {T, S, E} @assert E isa Direction - if span < zero(S) - @assert E == Ending - elseif span > zero(S) - @assert E == Beginning - else - @assert E isa Direction - end + @assert span == abs(span) @assert typeof(anchor + span) == T new{T, S, E}(anchor, span, inc) end @@ -87,46 +81,40 @@ function AnchoredInterval{T, S, E}(anchor::T, span::S, x::Bool, y::Bool) where { AnchoredInterval{T, S, E}(anchor, span, Inclusivity(x, y)) end function AnchoredInterval{T, S, E}(anchor::T, span::S) where {T, S, E} - # If an interval is anchored to the lesser endpoint, default to Inclusivity(false, true) - # If an interval is anchored to the greater endpoint, default to Inclusivity(true, false) - AnchoredInterval{T, S, E}(anchor, span, Inclusivity(span ≥ zero(S), span ≤ zero(S))) + # Ending intervals default to Inclusivity(false, true) + # Beginning intervals default to Inclusivity(true, false) + AnchoredInterval{T, S, E}(anchor, span, Inclusivity(E == Beginning, E == Ending)) end function AnchoredInterval{T, S, E}(anchor::T, inc::Inclusivity) where {T, S, E} - span = E == Ending ? -oneunit(S) : oneunit(S) - AnchoredInterval{T, S, E}(anchor, span, inc) + AnchoredInterval{T, S, E}(anchor, oneunit(S), inc) end function AnchoredInterval{T, S, E}(anchor::T, x::Bool, y::Bool) where {T, S, E} AnchoredInterval{T, S, E}(anchor, Inclusivity(x, y)) end function AnchoredInterval{T, S, E}(anchor::T) where {T, S, E} - span = E == Ending ? -oneunit(S) : oneunit(S) - AnchoredInterval{T, S, E}(anchor, span) + AnchoredInterval{T, S, E}(anchor, oneunit(S)) end -function AnchoredInterval{T, S}(anchor::T, span::S, args...) where {T, S} - E = if span < zero(S) - Ending - elseif span > zero(S) - Beginning - else - throw(ArgumentError("Must specify endpoint type when span is zero")) - end +function AnchoredInterval{T, <:Any, E}(anchor::T, span::S, args...) where {T, S, E} AnchoredInterval{T, S, E}(anchor, span, args...) end -function AnchoredInterval{T}(anchor::T, span::S, args...) where {T, S} - AnchoredInterval{T, S}(anchor, span, args...) + +const IntervalEnding{T, S} = AnchoredInterval{T, S, Ending} +function IntervalEnding(anchor::T, span::S, args...) where {T, S} + IntervalEnding{T, S}(anchor, span, args...) end -function AnchoredInterval(anchor::T, span::S, args...) where {T, S} - AnchoredInterval{T, S}(anchor, span, args...) +const IntervalBeginning{T, S} = AnchoredInterval{T, S, Beginning} +function IntervalBeginning(anchor::T, span::S, args...) where {T, S} + IntervalBeginning{T, S}(anchor, span, args...) end -const HourEnding{T} = AnchoredInterval{T, Hour, Ending} where T <: TimeType +const HourEnding{T} = IntervalEnding{T, Hour} where T <: TimeType HourEnding(a::T, args...) where T = HourEnding{T}(a, args...) -const HourBeginning{T} = AnchoredInterval{T, Hour, Beginning} where T <: TimeType +const HourBeginning{T} = IntervalBeginning{T, Hour} where T <: TimeType HourBeginning(a::T, args...) where T = HourBeginning{T}(a, args...) """ @@ -151,16 +139,14 @@ end ##### ACCESSORS ##### -function Base.first(interval::AnchoredInterval) - min(interval.anchor, interval.anchor + interval.span) -end +Base.first(interval::IntervalEnding) = interval.anchor - interval.span +Base.last(interval::IntervalEnding) = interval.anchor -function Base.last(interval::AnchoredInterval) - max(interval.anchor, interval.anchor + interval.span) -end +Base.first(interval::IntervalBeginning) = interval.anchor +Base.last(interval::IntervalBeginning) = interval.anchor + interval.span anchor(interval::AnchoredInterval) = interval.anchor -span(interval::AnchoredInterval) = abs(interval.span) +span(interval::AnchoredInterval) = interval.span ##### CONVERSION ##### @@ -176,12 +162,20 @@ end # since these are untested. #= -function Base.convert(::Type{AnchoredInterval{Ending}}, interval::Interval{T}) where T - AnchoredInterval{T}(last(interval), -span(interval), inclusivity(interval)) +function Base.convert(::Type{IntervalEnding{T, S}}, interval::Interval{T}) where {T, S} + IntervalEnding{T, S}(last(interval), span(interval), inclusivity(interval)) end -function Base.convert(::Type{AnchoredInterval{Beginning}}, interval::Interval{T}) where T - AnchoredInterval{T}(first(interval), span(interval), inclusivity(interval)) +function Base.convert(::Type{IntervalBeginning{T, S}}, interval::Interval{T}) where {T, S} + IntervalBeginning{T, S}(first(interval), span(interval), inclusivity(interval)) +end + +function Base.convert(::Type{IntervalEnding}, interval::Interval{T}) where T + IntervalEnding{T}(last(interval), span(interval), inclusivity(interval)) +end + +function Base.convert(::Type{IntervalBeginning}, interval::Interval{T}) where T + IntervalBeginning{T}(first(interval), span(interval), inclusivity(interval)) end =# @@ -212,7 +206,7 @@ function Base.show(io::IO, interval::AnchoredInterval) print(io, "(") show(io, anchor(interval)) print(io, ", ") - show(io, interval.span) + show(io, span(interval)) print(io, ", ") show(io, inclusivity(interval)) print(io, ")") @@ -230,7 +224,7 @@ end ##### ARITHMETIC ##### -Base.:+(a::T, b) where {T <: AnchoredInterval} = T(anchor(a) + b, a.span, inclusivity(a)) +Base.:+(a::T, b) where {T <: AnchoredInterval} = T(anchor(a) + b, span(a), inclusivity(a)) Base.:+(a, b::AnchoredInterval) = b + a Base.:-(a::AnchoredInterval, b) = a + -b @@ -242,19 +236,19 @@ Base.:-(a::T, b::AnchoredInterval{T}) where {T <: Number} = a + -b function Base.:-(a::AnchoredInterval{T, S, Ending}) where {T <: Number, S} inc = inclusivity(a) - AnchoredInterval{T, S, Beginning}(-anchor(a), -a.span, Inclusivity(last(inc), first(inc))) + AnchoredInterval{T, S, Beginning}(-anchor(a), span(a), Inclusivity(last(inc), first(inc))) end function Base.:-(a::AnchoredInterval{T, S, Beginning}) where {T <: Number, S} inc = inclusivity(a) - AnchoredInterval{T, S, Ending}(-anchor(a), -a.span, Inclusivity(last(inc), first(inc))) + AnchoredInterval{T, S, Ending}(-anchor(a), span(a), Inclusivity(last(inc), first(inc))) end ##### EQUALITY ##### # Required for min/max of AnchoredInterval{LaxZonedDateTime} when the anchor is AMB or DNE function Base.:<(a::AnchoredInterval{T, S, E}, b::AnchoredInterval{T, S, E}) where {T, S, E} - return anchor(a) < anchor(b) + return span(a) == span(b) && anchor(a) < anchor(b) end ##### RANGE ##### @@ -282,14 +276,8 @@ end function Base.intersect(a::AnchoredInterval{T, S, E}, b::AnchoredInterval{T, S, E}) where {T, S, E} interval = invoke(intersect, Tuple{AbstractInterval{T}, AbstractInterval{T}}, a, b) + anchor = E == Ending ? last(interval) : first(interval) sp = S <: Period ? canonicalize(S, span(interval)) : span(interval) - if E == Ending - anchor = last(interval) - sp = -sp - else - anchor = first(interval) - sp = sp - end return AnchoredInterval{T, S, E}(anchor, sp, inclusivity(interval)) end diff --git a/test/anchoredinterval.jl b/test/anchoredinterval.jl index 89b36280..e732febb 100644 --- a/test/anchoredinterval.jl +++ b/test/anchoredinterval.jl @@ -4,44 +4,49 @@ using Intervals: Beginning, Ending, canonicalize dt = DateTime(2016, 8, 11, 2) @testset "constructor" begin - expected = AnchoredInterval{DateTime, Hour}(dt, Hour(-1), Inclusivity(false, true)) - @test AnchoredInterval{DateTime, Hour}(dt, Hour(-1)) == expected - @test_throws MethodError AnchoredInterval{Hour(-1)}(dt) - @test AnchoredInterval(dt, Hour(-1)) == expected + expected = AnchoredInterval{DateTime, Hour, Ending}(dt, Hour(1), Inclusivity(false, true)) + @test AnchoredInterval{DateTime, Hour, Ending}(dt, Hour(1)) == expected + @test AnchoredInterval{DateTime, Hour, Ending}(dt) == expected + @test IntervalEnding{DateTime, Hour}(dt) == expected + @test IntervalEnding{DateTime}(dt, Hour(1)) == expected @test HourEnding{DateTime}(dt) == expected @test HourEnding(dt) == expected @test HE(dt) == expected @test HE(dt - Minute(59)) == expected - expected = AnchoredInterval{DateTime, Hour}(dt, Hour(1), Inclusivity(true, false)) - @test AnchoredInterval{DateTime, Hour}(dt, Hour(1)) == expected - @test_throws MethodError AnchoredInterval{Hour(1)}(dt) - @test AnchoredInterval(dt, Hour(1)) == expected + expected = AnchoredInterval{DateTime, Hour, Beginning}(dt, Hour(1), Inclusivity(true, false)) + @test AnchoredInterval{DateTime, Hour, Beginning}(dt, Hour(1)) == expected + @test AnchoredInterval{DateTime, Hour, Beginning}(dt) == expected + @test IntervalBeginning{DateTime, Hour}(dt) == expected + @test IntervalBeginning{DateTime}(dt, Hour(1)) == expected @test HourBeginning(dt) == expected @test HB(dt) == expected @test HB(dt + Minute(59)) == expected + @test_throws MethodError AnchoredInterval{Hour(-1)}(dt) + @test_throws MethodError AnchoredInterval{Hour(1)}(dt) + # Lazy inclusivity constructor @test HourEnding{DateTime}(dt, true, false) == HourEnding{DateTime}(dt, Inclusivity(true, false)) @test HourEnding(dt, true, false) == HourEnding(dt, Inclusivity(true, false)) - @test AnchoredInterval{DateTime}(dt, Day(1), false, false) == - AnchoredInterval{DateTime}(dt, Day(1), Inclusivity(false, false)) - @test AnchoredInterval(dt, Day(1), false, false) == - AnchoredInterval(dt, Day(1), Inclusivity(false, false)) + @test IntervalEnding{DateTime}(dt, Day(1), false, false) == + IntervalEnding{DateTime}(dt, Day(1), Inclusivity(false, false)) + @test IntervalEnding(dt, Day(1), false, false) == + IntervalEnding(dt, Day(1), Inclusivity(false, false)) @test HE(dt, true, true) == HourEnding(dt, Inclusivity(true, true)) @test HB(dt, true, true) == HourBeginning(dt, Inclusivity(true, true)) # Non-period AnchoredIntervals - @test AnchoredInterval(10, -10) isa AnchoredInterval - @test AnchoredInterval('a', 25) isa AnchoredInterval + @test IntervalEnding(10, 10) isa AnchoredInterval + @test IntervalBeginning('a', 25) isa AnchoredInterval end @testset "conversion" begin @testset "Date" begin d = Date(dt) - de = AnchoredInterval(d, Day(-1)) - db = AnchoredInterval(d, Day(1)) + de = IntervalEnding(d, Day(1)) + db = IntervalBeginning(d, Day(1)) @test Date(de) == d @test Date(db) == d end @@ -62,17 +67,17 @@ using Intervals: Beginning, Ending, canonicalize @testset "accessors" begin inc = Inclusivity(true, true) - p = Minute(-15) - interval = AnchoredInterval(dt, p, inc) + p = Minute(15) + interval = IntervalEnding(dt, p, inc) @test first(interval) == DateTime(2016, 8, 11, 1, 45) @test last(interval) == dt - @test span(interval) == -p + @test span(interval) == p @test inclusivity(interval) == inc inc = Inclusivity(false, false) p = Day(1) - interval = AnchoredInterval(Date(dt), p, inc) + interval = IntervalBeginning(Date(dt), p, inc) @test first(interval) == Date(2016, 8, 11) @test last(interval) == Date(2016, 8, 12) @@ -81,33 +86,33 @@ using Intervals: Beginning, Ending, canonicalize # DST transition endpoint = ZonedDateTime(2018, 3, 11, 3, tz"America/Winnipeg") - interval = AnchoredInterval(endpoint, Hour(-2)) + interval = IntervalEnding(endpoint, Hour(2)) @test span(interval) == Hour(2) startpoint = ZonedDateTime(2018, 3, 11, tz"America/Winnipeg") - interval = AnchoredInterval(startpoint, Day(1)) + interval = IntervalBeginning(startpoint, Day(1)) @test first(interval) == startpoint @test last(interval) == ZonedDateTime(2018, 3, 12, tz"America/Winnipeg") @test span(interval) == Day(1) endpoint = ZonedDateTime(2018, 11, 4, 2, tz"America/Winnipeg") - interval = AnchoredInterval(endpoint, Hour(-2)) + interval = IntervalEnding(endpoint, Hour(2)) @test span(interval) == Hour(2) startpoint = ZonedDateTime(2018, 11, 4, tz"America/Winnipeg") - interval = AnchoredInterval(startpoint, Day(1)) + interval = IntervalBeginning(startpoint, Day(1)) @test first(interval) == startpoint @test last(interval) == ZonedDateTime(2018, 11, 5, tz"America/Winnipeg") @test span(interval) == Day(1) # Non-period AnchoredIntervals - interval = AnchoredInterval(10, -10) + interval = IntervalEnding(10, 10) @test first(interval) == 0 @test last(interval) == 10 @test inclusivity(interval) == Inclusivity(false, true) @test span(interval) == 10 - interval = AnchoredInterval('a', 25) + interval = IntervalBeginning('a', 25) @test first(interval) == 'a' @test last(interval) == 'z' @test inclusivity(interval) == Inclusivity(true, false) @@ -142,31 +147,31 @@ using Intervals: Beginning, Ending, canonicalize @test string(interval) == "(2016-08-11 HE02]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2016-08-11T02:00:00, -1 hour, Inclusivity(false, true))" + "HourEnding{DateTime}(2016-08-11T02:00:00, 1 hour, Inclusivity(false, true))" interval = HourEnding(DateTime(2013, 2, 13), Inclusivity(true, false)) @test string(interval) == "[2013-02-12 HE24)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2013-02-13T00:00:00, -1 hour, Inclusivity(true, false))" + "HourEnding{DateTime}(2013-02-13T00:00:00, 1 hour, Inclusivity(true, false))" interval = HourEnding(dt + Minute(15) + Second(30)) @test string(interval) == "(2016-08-11 HE02:15:30]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2016-08-11T02:15:30, -1 hour, Inclusivity(false, true))" + "HourEnding{DateTime}(2016-08-11T02:15:30, 1 hour, Inclusivity(false, true))" interval = HourEnding(dt + Millisecond(2)) @test string(interval) == "(2016-08-11 HE02:00:00.002]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2016-08-11T02:00:00.002, -1 hour, Inclusivity(false, true))" + "HourEnding{DateTime}(2016-08-11T02:00:00.002, 1 hour, Inclusivity(false, true))" interval = HourEnding(DateTime(2013, 2, 13, 0, 1), Inclusivity(true, false)) @test string(interval) == "[2013-02-13 HE00:01:00)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "HourEnding{DateTime}(2013-02-13T00:01:00, -1 hour, Inclusivity(true, false))" + "HourEnding{DateTime}(2013-02-13T00:01:00, 1 hour, Inclusivity(true, false))" interval = HourBeginning(dt) @test string(interval) == "[2016-08-11 HB02)" @@ -184,99 +189,99 @@ using Intervals: Beginning, Ending, canonicalize @test string(interval) == "(2016-08-11 HE02-05:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "HourEnding{$ZonedDateTime}(2016-08-11T02:00:00-05:00, -1 hour, ", + "HourEnding{$ZonedDateTime}(2016-08-11T02:00:00-05:00, 1 hour, ", "Inclusivity(false, true))", ) - interval = AnchoredInterval(Date(dt), Year(-1)) + interval = IntervalEnding(Date(dt), Year(1)) @test string(interval) == "(YE 2016-08-11]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{Date, $Year, Ending}(2016-08-11, -1 year, ", + "AnchoredInterval{Date, $Year, Ending}(2016-08-11, 1 year, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval(ceil(Date(dt), Year), Year(-1)) + interval = IntervalEnding(ceil(Date(dt), Year), Year(1)) @test string(interval) == "(YE 2017-01-01]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{Date, $Year, Ending}(2017-01-01, -1 year, ", + "AnchoredInterval{Date, $Year, Ending}(2017-01-01, 1 year, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval(dt, Month(-1)) + interval = IntervalEnding(dt, Month(1)) @test string(interval) == "(MoE 2016-08-11 02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, $Month, Ending}(2016-08-11T02:00:00, -1 month, ", + "AnchoredInterval{DateTime, $Month, Ending}(2016-08-11T02:00:00, 1 month, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval(ceil(dt, Month), Month(-1)) + interval = IntervalEnding(ceil(dt, Month), Month(1)) @test string(interval) == "(MoE 2016-09-01]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, $Month, Ending}(2016-09-01T00:00:00, -1 month, ", + "AnchoredInterval{DateTime, $Month, Ending}(2016-09-01T00:00:00, 1 month, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval(DateTime(dt), Day(-1)) + interval = IntervalEnding(DateTime(dt), Day(1)) @test string(interval) == "(DE 2016-08-11 02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, $Day, Ending}(2016-08-11T02:00:00, -1 day, ", + "AnchoredInterval{DateTime, $Day, Ending}(2016-08-11T02:00:00, 1 day, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval(ceil(DateTime(dt), Day), Day(-1)) + interval = IntervalEnding(ceil(DateTime(dt), Day), Day(1)) @test string(interval) == "(DE 2016-08-12]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, $Day, Ending}(2016-08-12T00:00:00, -1 day, ", + "AnchoredInterval{DateTime, $Day, Ending}(2016-08-12T00:00:00, 1 day, ", "Inclusivity(false, true))" ) # Date(dt) will truncate the DateTime to the nearest day - interval = AnchoredInterval(Date(dt), Day(-1)) + interval = IntervalEnding(Date(dt), Day(1)) @test string(interval) == "(DE 2016-08-11]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{Date, $Day, Ending}(2016-08-11, -1 day, ", + "AnchoredInterval{Date, $Day, Ending}(2016-08-11, 1 day, ", "Inclusivity(false, true))" ) - interval = AnchoredInterval(dt, Minute(-5)) + interval = IntervalEnding(dt, Minute(5)) @test string(interval) == "(2016-08-11 5ME02:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( - "AnchoredInterval{DateTime, $Minute, Ending}(2016-08-11T02:00:00, -5 minutes, ", + "AnchoredInterval{DateTime, $Minute, Ending}(2016-08-11T02:00:00, 5 minutes, ", "Inclusivity(false, true))", ) - interval = AnchoredInterval(dt, Second(-30)) + interval = IntervalEnding(dt, Second(30)) @test string(interval) == "(2016-08-11 30SE02:00:00]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( "AnchoredInterval{DateTime, $Second, Ending}(2016-08-11T02:00:00, ", - "-30 seconds, Inclusivity(false, true))", + "30 seconds, Inclusivity(false, true))", ) - interval = AnchoredInterval(dt, Millisecond(-10)) + interval = IntervalEnding(dt, Millisecond(10)) @test string(interval) == "(2016-08-11 10msE02:00:00.000]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == string( "AnchoredInterval{DateTime, $Millisecond, Ending}(2016-08-11T02:00:00, ", - "-10 milliseconds, Inclusivity(false, true))", + "10 milliseconds, Inclusivity(false, true))", ) # Non-period AnchoredIntervals - interval = AnchoredInterval(10, -10) + interval = IntervalEnding(10, 10) @test string(interval) == "(0..10]" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == - "AnchoredInterval{$Int, $Int, Ending}(10, -10, Inclusivity(false, true))" + "AnchoredInterval{$Int, $Int, Ending}(10, 10, Inclusivity(false, true))" - interval = AnchoredInterval('a', 25) + interval = IntervalBeginning('a', 25) @test string(interval) == "[a..z)" @test sprint(showcompact, interval) == string(interval) @test sprint(show, interval) == @@ -286,7 +291,7 @@ using Intervals: Beginning, Ending, canonicalize @testset "equality" begin he = HourEnding(dt) hb = HourBeginning(dt) - me = AnchoredInterval(dt, Minute(-1)) + me = IntervalEnding(dt, Minute(1)) cpe = copy(he) cpb = copy(hb) @@ -370,9 +375,9 @@ using Intervals: Beginning, Ending, canonicalize @test he + Minute(30) == HourEnding(dt + Minute(30)) @test he - Minute(30) == HourEnding(dt - Minute(30)) - ai = AnchoredInterval(dt, Minute(-60)) - @test ai + Minute(30) == AnchoredInterval(dt + Minute(30), Minute(-60)) - @test ai - Minute(30) == AnchoredInterval(dt - Minute(30), Minute(-60)) + ai = IntervalEnding(dt, Minute(60)) + @test ai + Minute(30) == IntervalEnding(dt + Minute(30), Minute(60)) + @test ai - Minute(30) == IntervalEnding(dt - Minute(30), Minute(60)) # Subtracting AnchoredInterval{P, T} from T doesn't make sense if T is a TimeType @test_throws MethodError Hour(1) - he @@ -380,31 +385,31 @@ using Intervals: Beginning, Ending, canonicalize # DST transition endpoint = ZonedDateTime(2018, 3, 11, 3, tz"America/Winnipeg") - interval = AnchoredInterval(endpoint, Hour(-2)) + interval = IntervalEnding(endpoint, Hour(2)) @test span(interval) == Hour(2) endpoint = ZonedDateTime(2018, 11, 4, 2, tz"America/Winnipeg") - interval = AnchoredInterval(endpoint, Hour(-2)) + interval = IntervalEnding(endpoint, Hour(2)) @test span(interval) == Hour(2) # Non-period AnchoredIntervals - @test AnchoredInterval('a', 10) + 2 == AnchoredInterval('c', 10) - @test AnchoredInterval('d', 10) - 2 == AnchoredInterval('b', 10) + @test IntervalBeginning('a', 10) + 2 == IntervalBeginning('c', 10) + @test IntervalBeginning('d', 10) - 2 == IntervalBeginning('b', 10) - @test AnchoredInterval(20, -1) + 2 == AnchoredInterval(22, -1) - @test AnchoredInterval(20, -1) - 2 == AnchoredInterval(18, -1) + @test IntervalEnding(20, 1) + 2 == IntervalEnding(22, 1) + @test IntervalEnding(20, 1) - 2 == IntervalEnding(18, 1) - @test -AnchoredInterval(10, 2) == AnchoredInterval(-10, -2) # -[10,12)==(-12,-10] - @test -AnchoredInterval(10, -2) == AnchoredInterval(-10, 2) # -(8,10]==[-10,-8) + @test -IntervalBeginning(10, 2) == IntervalEnding(-10, 2) # -[10,12)==(-12,-10] + @test -IntervalEnding(10, 2) == IntervalBeginning(-10, 2) # -(8,10]==[-10,-8) - @test 15 - AnchoredInterval(10, -2) == AnchoredInterval(5, 2) # 15-(8,10]==[5,8) - @test 15 - AnchoredInterval(10, 2) == AnchoredInterval(5, -2) # 15-[10,12)==(3,5] + @test 15 - IntervalEnding(10, 2) == IntervalBeginning(5, 2) # 15-(8,10]==[5,8) + @test 15 - IntervalBeginning(10, 2) == IntervalEnding(5, 2) # 15-[10,12)==(3,5] - @test_throws MethodError -AnchoredInterval('a', 10) - @test_throws MethodError -AnchoredInterval('z', -10) + @test_throws MethodError -IntervalBeginning('a', 10) + @test_throws MethodError -IntervalEnding('z', 10) - @test_throws MethodError 10 - AnchoredInterval('a', 10) - @test_throws MethodError 10 - AnchoredInterval('z', -10) + @test_throws MethodError 10 - IntervalBeginning('a', 10) + @test_throws MethodError 10 - IntervalEnding('z', 10) end @testset "range" begin @@ -435,21 +440,21 @@ using Intervals: Beginning, Ending, canonicalize end @testset "isempty" begin - for P in [Year, Month, Day, Hour, Minute, Second], sign in [+, -] - span = sign(oneunit(P)) + for P in [Year, Month, Day, Hour, Minute, Second] + span = oneunit(P) - @test !isempty(AnchoredInterval(dt, span, Inclusivity(false, false))) - @test !isempty(AnchoredInterval(dt, span, Inclusivity(false, true))) - @test !isempty(AnchoredInterval(dt, span, Inclusivity(true, false))) - @test !isempty(AnchoredInterval(dt, span, Inclusivity(true, true))) + for T in [IntervalEnding, IntervalBeginning] + @test !isempty(T(dt, span, Inclusivity(false, false))) + @test !isempty(T(dt, span, Inclusivity(false, true))) + @test !isempty(T(dt, span, Inclusivity(true, false))) + @test !isempty(T(dt, span, Inclusivity(true, true))) + end end for P in [Year, Month, Day, Hour, Minute, Second] span = zero(P) - @test_throws ArgumentError AnchoredInterval(dt, span) - for E in [Ending, Beginning] - T = AnchoredInterval{DateTime, P, E} + for T in [IntervalEnding, IntervalBeginning] @test isempty(T(dt, span, Inclusivity(false, false))) @test isempty(T(dt, span, Inclusivity(false, true))) @test isempty(T(dt, span, Inclusivity(true, false))) @@ -497,8 +502,8 @@ using Intervals: Beginning, Ending, canonicalize # Hour overlap he = HourEnding(dt) - @test intersect(he, AnchoredInterval(dt, Hour(-2))) == he - @test intersect(AnchoredInterval(dt + Hour(1), Hour(-3)), he) == he + @test intersect(he, IntervalEnding(dt, Hour(2))) == he + @test intersect(IntervalEnding(dt + Hour(1), Hour(3)), he) == he @test intersect(HourBeginning(dt - Hour(1)), he) == Interval(dt - Hour(1), dt, Inclusivity(false, false)) @@ -527,8 +532,7 @@ using Intervals: Beginning, Ending, canonicalize @test intersect(HourEnding(dt), HourBeginning(dt)) == Interval(dt, dt) # Non-period AnchoredIntervals - @test intersect(AnchoredInterval(3, -2), AnchoredInterval(4, -2)) == - AnchoredInterval(3, -1) + @test intersect(IntervalEnding(3, 2), IntervalEnding(4, 2)) == IntervalEnding(3, 1) end @testset "canonicalize" begin