From bed9d292da01ac5c4cf82f48c01fe0ce55a71a7d Mon Sep 17 00:00:00 2001 From: Gord Stephen Date: Sat, 11 Jul 2015 09:54:07 -0400 Subject: [PATCH] Add Period/CompoundPeriod/TimeType array addition, subtraction, and broadcasting (where not previously implemented) --- base/dates/arithmetic.jl | 42 ++++++++++++++++++++++++++--- base/dates/periods.jl | 43 ++++++++++++++++++++++------- test/dates/arithmetic.jl | 58 ++++++++++++++++++++++++++++++++++++++++ test/dates/periods.jl | 58 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 13 deletions(-) diff --git a/base/dates/arithmetic.jl b/base/dates/arithmetic.jl index 84f62b13519ac..f81780ac6340c 100644 --- a/base/dates/arithmetic.jl +++ b/base/dates/arithmetic.jl @@ -66,7 +66,41 @@ end (+)(y::Period,x::TimeType) = x + y (-)(y::Period,x::TimeType) = x - y -(.+){T<:TimeType}(x::AbstractArray{T}, y::Period) = reshape(T[i + y for i in x], size(x)) -(.-){T<:TimeType}(x::AbstractArray{T}, y::Period) = reshape(T[i - y for i in x], size(x)) -(.+){T<:TimeType}(y::Period, x::AbstractArray{T}) = x .+ y -(.-){T<:TimeType}(y::Period, x::AbstractArray{T}) = x .- y +for op in (:.+, :.-) + op_ = symbol(string(op)[2:end]) + @eval begin + # GeneralPeriod, AbstractArray{TimeType} + ($op){T<:TimeType}(x::AbstractArray{T}, y::GeneralPeriod) = + reshape(T[($op_)(i,y) for i in x], size(x)) + ($op){T<:TimeType}(y::GeneralPeriod, x::AbstractArray{T}) = ($op)(x,y) + ($op_){T<:TimeType}(x::AbstractArray{T}, y::GeneralPeriod) = ($op)(x,y) + ($op_){T<:TimeType}(y::GeneralPeriod, x::AbstractArray{T}) = ($op)(x,y) + + # TimeType, StridedArray{GeneralPeriod} + ($op){T<:TimeType,P<:GeneralPeriod}(x::StridedArray{P}, y::T) = + reshape(T[($op_)(i,y) for i in x], size(x)) + ($op){P<:GeneralPeriod}(y::TimeType, x::StridedArray{P}) = ($op)(x,y) + ($op_){T<:TimeType,P<:GeneralPeriod}(x::StridedArray{P}, y::T) = ($op)(x,y) + ($op_){P<:GeneralPeriod}(y::TimeType, x::StridedArray{P}) = ($op)(x,y) + + # AbstractArray{TimeType}, StridedArray{GeneralPeriod} + ($op_){T<:TimeType,P<:GeneralPeriod}(x::Range{T}, y::StridedArray{P}) = ($op_)(collect(x),y) + ($op_){T<:TimeType,P<:GeneralPeriod}(x::AbstractArray{T}, y::StridedArray{P}) = + reshape(TimeType[($op_)(x[i],y[i]) for i in eachindex(x, y)], promote_shape(size(x),size(y))) + ($op_){T<:TimeType,P<:GeneralPeriod}(y::StridedArray{P}, x::AbstractArray{T}) = ($op_)(x,y) + end +end + +# TimeType, AbstractArray{TimeType} +(.-){T<:TimeType}(x::AbstractArray{T}, y::T) = reshape(Period[i - y for i in x], size(x)) +(.-){T<:TimeType}(y::T, x::AbstractArray{T}) = -(x .- y) +(-){T<:TimeType}(x::AbstractArray{T}, y::T) = x .- y +(-){T<:TimeType}(y::T, x::AbstractArray{T}) = -(x .- y) + +# AbstractArray{TimeType}, AbstractArray{TimeType} +(-){T<:TimeType}(x::OrdinalRange{T}, y::OrdinalRange{T}) = collect(x) - collect(y) +(-){T<:TimeType}(x::Range{T}, y::Range{T}) = collect(x) - collect(y) +(-){T<:TimeType}(x::AbstractArray{T}, y::Range{T}) = y - collect(x) +(-){T<:TimeType}(x::Range{T}, y::AbstractArray{T}) = collect(x) - y +(-){T<:TimeType}(x::AbstractArray{T}, y::AbstractArray{T}) = + reshape(Period[x[i] - y[i] for i in eachindex(x, y)], promote_shape(size(x),size(y))) diff --git a/base/dates/periods.jl b/base/dates/periods.jl index 0e3b9112dac00..9f0c7bb9e58a5 100644 --- a/base/dates/periods.jl +++ b/base/dates/periods.jl @@ -42,14 +42,7 @@ import Base: div, mod, rem, gcd, lcm, +, -, *, /, %, .+, .-, .*, .% for op in (:+,:-,:lcm,:gcd) @eval ($op){P<:Period}(x::P,y::P) = P(($op)(value(x),value(y))) end -for op in (:.+, :.-) - op_ = symbol(string(op)[2:end]) - @eval begin - ($op){P<:Period}(x::P,Y::StridedArray{P}) = ($op)(Y,x) - ($op_){P<:Period}(x::P,Y::StridedArray{P}) = ($op)(Y,x) - ($op_){P<:Period}(Y::StridedArray{P},x::P) = ($op)(Y,x) - end -end + for op in (:/,:%,:div,:mod) @eval begin ($op){P<:Period}(x::P,y::P) = ($op)(value(x),value(y)) @@ -61,7 +54,7 @@ end *{P<:Period}(x::P,y::Real) = P(value(x) * Int64(y)) *(y::Real,x::Period) = x * y .*{P<:Period}(y::Real, X::StridedArray{P}) = X .* y -for (op,Ty,Tz) in ((:.+,:P,:P),(:.-,:P,:P), (:.*,Real,:P), +for (op,Ty,Tz) in ((:.*,Real,:P), (:./,:P,Float64), (:./,Real,:P), (:.%,:P,Int64), (:.%,Integer,:P), (:div,:P,Int64), (:div,Integer,:P), @@ -192,6 +185,7 @@ function Base.string(x::CompoundPeriod) end end Base.show(io::IO,x::CompoundPeriod) = print(io,string(x)) + # E.g. Year(1) + Day(1) (+)(x::Period,y::Period) = CompoundPeriod(Period[x,y]) (+)(x::CompoundPeriod,y::Period) = CompoundPeriod(vcat(x.periods,y)) @@ -202,6 +196,29 @@ Base.show(io::IO,x::CompoundPeriod) = print(io,string(x)) (-)(x::CompoundPeriod,y::Period) = CompoundPeriod(vcat(x.periods,-y)) (-)(x::CompoundPeriod) = CompoundPeriod(-x.periods) (-)(y::Union{Period,CompoundPeriod},x::CompoundPeriod) = (-x) + y + +GeneralPeriod = Union{Period,CompoundPeriod} +(+)(x::GeneralPeriod) = x +(+){P<:GeneralPeriod}(x::StridedArray{P}) = x + +for op in (:.+, :.-) + op_ = symbol(string(op)[2:end]) + @eval begin + function ($op){P<:GeneralPeriod}(X::StridedArray{P},y::GeneralPeriod) + Z = similar(X, CompoundPeriod) + for i = 1:length(X) + @inbounds Z[i] = ($op_)(X[i],y) + end + return Z + end + ($op){P<:GeneralPeriod}(x::GeneralPeriod,Y::StridedArray{P}) = ($op)(Y,x) |> ($op_) + ($op_){P<:GeneralPeriod}(x::GeneralPeriod,Y::StridedArray{P}) = ($op)(Y,x) |> ($op_) + ($op_){P<:GeneralPeriod}(Y::StridedArray{P},x::GeneralPeriod) = ($op)(Y,x) + ($op_){P<:GeneralPeriod, Q<:GeneralPeriod}(X::StridedArray{P}, Y::StridedArray{Q}) = + reshape(CompoundPeriod[($op_)(X[i],Y[i]) for i in eachindex(X, Y)], promote_shape(size(X),size(Y))) + end +end + (==)(x::CompoundPeriod, y::Period) = x == CompoundPeriod(y) (==)(y::Period, x::CompoundPeriod) = x == y (==)(x::CompoundPeriod, y::CompoundPeriod) = x.periods == y.periods @@ -220,6 +237,14 @@ function (+)(x::TimeType,y::CompoundPeriod) end (+)(x::CompoundPeriod,y::TimeType) = y + x +function (-)(x::TimeType,y::CompoundPeriod) + for p in y.periods + x -= p + end + return x +end +(-)(x::CompoundPeriod,y::TimeType) = y - x + # Fixed-value Periods (periods corresponding to a well-defined time interval, # as opposed to variable calendar intervals like Year). typealias FixedPeriod Union{Week,Day,Hour,Minute,Second,Millisecond} diff --git a/test/dates/arithmetic.jl b/test/dates/arithmetic.jl index 8a4bb794cb6e1..aef98392e08b4 100644 --- a/test/dates/arithmetic.jl +++ b/test/dates/arithmetic.jl @@ -330,3 +330,61 @@ dt2 = dt + Dates.Year(1) @test Dates.minute(dt2) == 30 @test Dates.second(dt2) == 45 @test Dates.millisecond(dt2) == 500 + +t1 = [Date(2009,1,1) Date(2009,1,2) Date(2009,1,3); Date(2009,2,1) Date(2009,2,2) Date(2009,2,3)] +t2 = [Date(2009,1,2) Date(2009,2,2) Date(2010,1,3); Date(2010,2,1) Date(2009,3,2) Date(2009,2,4)] +t3 = [DateTime(2009,1,1), DateTime(2009,1,2), DateTime(2009,1,3)] +t4 = [DateTime(2009,1,1,0,0,1), DateTime(2009,1,2,0,1), DateTime(2009,1,3,1)] + +# TimeType, Array{TimeType} +@test Date(2010,1,1) .- t1 == [Day(365) Day(364) Day(363); Day(334) Day(333) Day(332)] +@test t1 .- Date(2010,1,1) == [Day(-365) Day(-364) Day(-363); Day(-334) Day(-333) Day(-332)] +@test DateTime(2009,1,1) .- t3 == [Millisecond(0), Millisecond(-86400000), Millisecond(-172800000)] +@test t3 .- DateTime(2009,1,1) == [Millisecond(0), Millisecond(86400000), Millisecond(172800000)] +@test Date(2010,1,1) - t1 == [Day(365) Day(364) Day(363); Day(334) Day(333) Day(332)] +@test t1 - Date(2010,1,1) == [Day(-365) Day(-364) Day(-363); Day(-334) Day(-333) Day(-332)] +@test DateTime(2009,1,1) - t3 == [Millisecond(0), Millisecond(-86400000), Millisecond(-172800000)] +@test t3 - DateTime(2009,1,1) == [Millisecond(0), Millisecond(86400000), Millisecond(172800000)] + +# GeneralPeriod, Array{TimeType} +@test Day(1) .+ t1 == [Date(2009,1,2) Date(2009,1,3) Date(2009,1,4); Date(2009,2,2) Date(2009,2,3) Date(2009,2,4)] +@test Hour(1) .+ t3 == [DateTime(2009,1,1,1), DateTime(2009,1,2,1), DateTime(2009,1,3,1)] +@test t1 .+ Day(1) == [Date(2009,1,2) Date(2009,1,3) Date(2009,1,4); Date(2009,2,2) Date(2009,2,3) Date(2009,2,4)] +@test t3 .+ Hour(1) == [DateTime(2009,1,1,1), DateTime(2009,1,2,1), DateTime(2009,1,3,1)] +@test Day(1) + t1 == [Date(2009,1,2) Date(2009,1,3) Date(2009,1,4); Date(2009,2,2) Date(2009,2,3) Date(2009,2,4)] +@test Hour(1) + t3 == [DateTime(2009,1,1,1), DateTime(2009,1,2,1), DateTime(2009,1,3,1)] +@test t1 + Day(1) == [Date(2009,1,2) Date(2009,1,3) Date(2009,1,4); Date(2009,2,2) Date(2009,2,3) Date(2009,2,4)] +@test t3 + Hour(1) == [DateTime(2009,1,1,1), DateTime(2009,1,2,1), DateTime(2009,1,3,1)] + +@test (Month(1) + Day(1)) .+ t1 == [Date(2009,2,2) Date(2009,2,3) Date(2009,2,4); Date(2009,3,2) Date(2009,3,3) Date(2009,3,4)] +@test (Hour(1) + Minute(1)) .+ t3 == [DateTime(2009,1,1,1,1), DateTime(2009,1,2,1,1), DateTime(2009,1,3,1,1)] +@test t1 .+ (Month(1) + Day(1)) == [Date(2009,2,2) Date(2009,2,3) Date(2009,2,4); Date(2009,3,2) Date(2009,3,3) Date(2009,3,4)] +@test t3 .+ (Hour(1) + Minute(1)) == [DateTime(2009,1,1,1,1), DateTime(2009,1,2,1,1), DateTime(2009,1,3,1,1)] +@test (Month(1) + Day(1)) + t1 == [Date(2009,2,2) Date(2009,2,3) Date(2009,2,4); Date(2009,3,2) Date(2009,3,3) Date(2009,3,4)] +@test (Hour(1) + Minute(1)) + t3 == [DateTime(2009,1,1,1,1), DateTime(2009,1,2,1,1), DateTime(2009,1,3,1,1)] +@test t1 + (Month(1) + Day(1)) == [Date(2009,2,2) Date(2009,2,3) Date(2009,2,4); Date(2009,3,2) Date(2009,3,3) Date(2009,3,4)] +@test t3 + (Hour(1) + Minute(1)) == [DateTime(2009,1,1,1,1), DateTime(2009,1,2,1,1), DateTime(2009,1,3,1,1)] + +@test Day(1) .- t1 == [Date(2008,12,31) Date(2009,1,1) Date(2009,1,2); Date(2009,1,31) Date(2009,2,1) Date(2009,2,2)] +@test Hour(1) .- t3 == [DateTime(2008,12,31,23), DateTime(2009,1,1,23), DateTime(2009,1,2,23)] +@test t1 .- Day(1) == [Date(2008,12,31) Date(2009,1,1) Date(2009,1,2); Date(2009,1,31) Date(2009,2,1) Date(2009,2,2)] +@test t3 .- Hour(1) == [DateTime(2008,12,31,23), DateTime(2009,1,1,23), DateTime(2009,1,2,23)] +@test Day(1) - t1 == [Date(2008,12,31) Date(2009,1,1) Date(2009,1,2); Date(2009,1,31) Date(2009,2,1) Date(2009,2,2)] +@test Hour(1) - t3 == [DateTime(2008,12,31,23), DateTime(2009,1,1,23), DateTime(2009,1,2,23)] +@test t1 - Day(1) == [Date(2008,12,31) Date(2009,1,1) Date(2009,1,2); Date(2009,1,31) Date(2009,2,1) Date(2009,2,2)] +@test t3 - Hour(1) == [DateTime(2008,12,31,23), DateTime(2009,1,1,23), DateTime(2009,1,2,23)] + +@test (Month(1) + Day(1)) .- t1 == [Date(2008,11,30) Date(2008,12,1) Date(2008,12,2); Date(2008,12,31) Date(2009,1,1) Date(2009,1,2)] +@test (Hour(1) + Minute(1)) .- t3 == [DateTime(2008,12,31,22,59), DateTime(2009,1,1,22,59), DateTime(2009,1,2,22,59)] +@test t1 .- (Month(1) + Day(1)) == [Date(2008,11,30) Date(2008,12,1) Date(2008,12,2); Date(2008,12,31) Date(2009,1,1) Date(2009,1,2)] +@test t3 .- (Hour(1) + Minute(1)) == [DateTime(2008,12,31,22,59), DateTime(2009,1,1,22,59), DateTime(2009,1,2,22,59)] +@test (Month(1) + Day(1)) - t1 == [Date(2008,11,30) Date(2008,12,1) Date(2008,12,2); Date(2008,12,31) Date(2009,1,1) Date(2009,1,2)] +@test (Hour(1) + Minute(1)) - t3 == [DateTime(2008,12,31,22,59), DateTime(2009,1,1,22,59), DateTime(2009,1,2,22,59)] +@test t1 - (Month(1) + Day(1)) == [Date(2008,11,30) Date(2008,12,1) Date(2008,12,2); Date(2008,12,31) Date(2009,1,1) Date(2009,1,2)] +@test t3 - (Hour(1) + Minute(1)) == [DateTime(2008,12,31,22,59), DateTime(2009,1,1,22,59), DateTime(2009,1,2,22,59)] + +# Array{TimeType}, Array{TimeType} +@test t2 - t1 == [Day(1) Day(31) Day(365); Day(365) Day(28) Day(1)] +@test t4 - t3 == [Millisecond(1000), Millisecond(60000), Millisecond(3600000)] +@test (Date(2009,1,1):Week(1):Date(2009,1,21)) - (Date(2009,1,1):Day(1):Date(2009,1,3)) == [0d, 6d, 12d] +@test (DateTime(2009,1,1,1,1,1):Second(1):DateTime(2009,1,1,1,1,3)) - (DateTime(2009,1,1,1,1):Second(1):DateTime(2009,1,1,1,1,2)) == [1s, 1s, 1s] diff --git a/test/dates/periods.jl b/test/dates/periods.jl index 6f31697722afe..989d50eec9898 100644 --- a/test/dates/periods.jl +++ b/test/dates/periods.jl @@ -288,3 +288,61 @@ emptyperiod = ((y + d) - d) - y @test 8d - s == 1w + 23h + 59mi + 59s @test h + 3mi == 63mi @test y - m == 11m + +@test Date(2009,2,1) - (Month(1) + Day(1)) == Date(2008,12,31) +@test (Month(1) + Day(1)) - Date(2009,2,1) == Date(2008,12,31) + +pa = [1y 1m 1w 1d; 1h 1mi 1s 1ms] +cpa = [1y+1s 1m+1s 1w+1s 1d+1s; 1h+1s 1mi+1s 2m+1s 1s+1ms] + +@test 1y .+ pa == [2y 1y+1m 1y+1w 1y+1d; 1y+1h 1y+1mi 1y+1s 1y+1ms] +@test (1y+1m) .+ pa == [2y+1m 1y+2m 1y+1m+1w 1y+1m+1d; 1y+1m+1h 1y+1m+1mi 1y+1m+1s 1y+1m+1ms] +@test pa .+ 1y == [2y 1y+1m 1y+1w 1y+1d; 1y+1h 1y+1mi 1y+1s 1y+1ms] +@test pa .+ (1y+1m) == [2y+1m 1y+2m 1y+1m+1w 1y+1m+1d; 1y+1m+1h 1y+1m+1mi 1y+1m+1s 1y+1m+1ms] + +@test 1y .+ cpa == [2y+1s 1y+1m+1s 1y+1w+1s 1y+1d+1s; 1y+1h+1s 1y+1mi+1s 1y+2m+1s 1y+1ms+1s] +@test (1y+1m) .+ cpa == [2y+1m+1s 1y+2m+1s 1y+1m+1w+1s 1y+1m+1d+1s; 1y+1m+1h+1s 1y+1m+1mi+1s 1y+3m+1s 1y+1m+1s+1ms] +@test cpa .+ 1y == [2y+1s 1y+1m+1s 1y+1w+1s 1y+1d+1s; 1y+1h+1s 1y+1mi+1s 1y+2m+1s 1y+1ms+1s] +@test cpa .+ (1y+1m) == [2y+1m+1s 1y+2m+1s 1y+1m+1w+1s 1y+1m+1d+1s; 1y+1m+1h+1s 1y+1m+1mi+1s 1y+3m+1s 1y+1m+1s+1ms] + +@test 1y + pa == [2y 1y+1m 1y+1w 1y+1d; 1y+1h 1y+1mi 1y+1s 1y+1ms] +@test (1y+1m) + pa == [2y+1m 1y+2m 1y+1m+1w 1y+1m+1d; 1y+1m+1h 1y+1m+1mi 1y+1m+1s 1y+1m+1ms] +@test pa + 1y == [2y 1y+1m 1y+1w 1y+1d; 1y+1h 1y+1mi 1y+1s 1y+1ms] +@test pa + (1y+1m) == [2y+1m 1y+2m 1y+1m+1w 1y+1m+1d; 1y+1m+1h 1y+1m+1mi 1y+1m+1s 1y+1m+1ms] + +@test 1y + cpa == [2y+1s 1y+1m+1s 1y+1w+1s 1y+1d+1s; 1y+1h+1s 1y+1mi+1s 1y+2m+1s 1y+1ms+1s] +@test (1y+1m) + cpa == [2y+1m+1s 1y+2m+1s 1y+1m+1w+1s 1y+1m+1d+1s; 1y+1m+1h+1s 1y+1m+1mi+1s 1y+3m+1s 1y+1m+1s+1ms] +@test cpa + 1y == [2y+1s 1y+1m+1s 1y+1w+1s 1y+1d+1s; 1y+1h+1s 1y+1mi+1s 1y+2m+1s 1y+1ms+1s] +@test cpa + (1y+1m) == [2y+1m+1s 1y+2m+1s 1y+1m+1w+1s 1y+1m+1d+1s; 1y+1m+1h+1s 1y+1m+1mi+1s 1y+3m+1s 1y+1m+1s+1ms] + +@test 1y .- pa == [0y 1y-1m 1y-1w 1y-1d; 1y-1h 1y-1mi 1y-1s 1y-1ms] +@test (1y+1m) .- pa == [1m 1y 1y+1m-1w 1y+1m-1d; 1y+1m-1h 1y+1m-1mi 1y+1m-1s 1y+1m-1ms] +@test pa .- (1y+1m) == [-1m -1y -1y-1m+1w -1y-1m+1d; -1y-1m+1h -1y-1m+1mi -1y-1m+1s -1y-1m+1ms] +@test pa .- 1y == [0y 1m-1y -1y+1w -1y+1d; -1y+1h -1y+1mi -1y+1s -1y+1ms] + +@test 1y .- cpa == [-1s 1y-1m-1s 1y-1w-1s 1y-1d-1s; 1y-1h-1s 1y-1mi-1s 1y-2m-1s 1y-1ms-1s] +@test (1y+1m) .- cpa == [1m-1s 1y-1s 1y+1m-1w-1s 1y+1m-1d-1s; 1y+1m-1h-1s 1y+1m-1mi-1s 1y-1m-1s 1y+1m-1s-1ms] +@test cpa .- 1y == [1s -1y+1m+1s -1y+1w+1s -1y+1d+1s; -1y+1h+1s -1y+1mi+1s -1y+2m+1s -1y+1ms+1s] +@test cpa .- (1y+1m) == [-1m+1s -1y+1s -1y-1m+1w+1s -1y-1m+1d+1s; -1y-1m+1h+1s -1y-1m+1mi+1s -1y+1m+1s -1y+-1m+1s+1ms] + +@test 1y - pa == [0y 1y-1m 1y-1w 1y-1d; 1y-1h 1y-1mi 1y-1s 1y-1ms] +@test (1y+1m) - pa == [1m 1y 1y+1m-1w 1y+1m-1d; 1y+1m-1h 1y+1m-1mi 1y+1m-1s 1y+1m-1ms] +@test pa - (1y+1m) == [-1m -1y -1y-1m+1w -1y-1m+1d; -1y-1m+1h -1y-1m+1mi -1y-1m+1s -1y-1m+1ms] +@test pa - 1y == [0y 1m-1y -1y+1w -1y+1d; -1y+1h -1y+1mi -1y+1s -1y+1ms] + +@test 1y - cpa == [-1s 1y-1m-1s 1y-1w-1s 1y-1d-1s; 1y-1h-1s 1y-1mi-1s 1y-2m-1s 1y-1ms-1s] +@test (1y+1m) - cpa == [1m-1s 1y-1s 1y+1m-1w-1s 1y+1m-1d-1s; 1y+1m-1h-1s 1y+1m-1mi-1s 1y-1m-1s 1y+1m-1s-1ms] +@test cpa - 1y == [1s -1y+1m+1s -1y+1w+1s -1y+1d+1s; -1y+1h+1s -1y+1mi+1s -1y+2m+1s -1y+1ms+1s] +@test cpa - (1y+1m) == [-1m+1s -1y+1s -1y-1m+1w+1s -1y-1m+1d+1s; -1y-1m+1h+1s -1y-1m+1mi+1s -1y+1m+1s -1y+-1m+1s+1ms] + +@test [1y 1m; 1w 1d] + [1h 1mi; 1s 1ms] == [1y+1h 1m+1mi; 1w+1s 1d+1ms] +@test [1y 1m; 1w 1d] - [1h 1mi; 1s 1ms] == [1y-1h 1m-1mi; 1w-1s 1d-1ms] + +@test [1y+1s 1m+1s; 1w+1s 1d+1s] + [1h 1mi; 1s 1ms] == [1y+1h+1s 1m+1mi+1s; 1w+2s 1d+1s+1ms] +@test [1y+1s 1m+1s; 1w+1s 1d+1s] - [1h 1mi; 1s 1ms] == [1y-1h+1s 1m-1mi+1s; 1w 1d+1s-1ms] + +@test [1y 1m; 1w 1d] + [1h+1s 1mi+1s; 1m+1s 1s+1ms] == [1y+1h+1s 1m+1mi+1s; 1w+1m+1s 1d+1s+1ms] +@test [1y 1m; 1w 1d] - [1h+1s 1mi+1s; 1m+1s 1s+1ms] == [1y-1h-1s 1m-1mi-1s; 1w-1m-1s 1d-1s-1ms] + +@test [1y+1s 1m+1s; 1w+1s 1d+1s] + [1y+1h 1y+1mi; 1y+1s 1y+1ms] == [2y+1h+1s 1y+1m+1mi+1s; 1y+1w+2s 1y+1d+1s+1ms] +@test [1y+1s 1m+1s; 1w+1s 1d+1s] - [1y+1h 1y+1mi; 1y+1s 1y+1ms] == [1s-1h 1m+1s-1y-1mi; 1w-1y 1d+1s-1y-1ms]