From 3dc610ba77803e2d4e50e2bbfbb96ac6d3a51058 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 14 May 2020 18:26:51 +0200 Subject: [PATCH 01/13] Add support for Dates.FixedPeriod --- Project.toml | 1 + src/Unitful.jl | 2 + src/dates.jl | 97 ++++++++++++++ test/dates.jl | 326 +++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 7 + 5 files changed, 433 insertions(+) create mode 100644 src/dates.jl create mode 100644 test/dates.jl diff --git a/Project.toml b/Project.toml index 8ac63855..8b408fa7 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ version = "1.2.1" [deps] ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/src/Unitful.jl b/src/Unitful.jl index bd671615..7bd297b5 100644 --- a/src/Unitful.jl +++ b/src/Unitful.jl @@ -17,6 +17,7 @@ import Base: getindex, eltype, step, last, first, frexp import Base: Integer, Rational, typemin, typemax import Base: steprange_last, unsigned +import Dates import LinearAlgebra: Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal import LinearAlgebra: istril, istriu, norm import Random @@ -64,5 +65,6 @@ include("fastmath.jl") include("logarithm.jl") include("complex.jl") include("pkgdefaults.jl") +include("dates.jl") end diff --git a/src/dates.jl b/src/dates.jl new file mode 100644 index 00000000..ba2bb812 --- /dev/null +++ b/src/dates.jl @@ -0,0 +1,97 @@ +# Conversion from and to types from the `Dates` stdlib + +import Dates: Week, Day, Hour, Minute, Second, Millisecond, Microsecond, Nanosecond + +for (period, unit) = ((Nanosecond, ns), (Microsecond, μs), (Millisecond, ms), (Second, s), + (Minute, minute), (Hour, hr), (Day, d), (Week, wk)) + @eval unit(::Type{$period}) = $unit + @eval (::Type{$period})(x::AbstractQuantity) = $period(ustrip(unit($period), x)) +end + +dimension(p::Dates.FixedPeriod) = dimension(typeof(p)) +dimension(::Type{<:Dates.FixedPeriod}) = 𝐓 + +""" + unit(x::Dates.FixedPeriod) + unit(x::Type{<:Dates.FixedPeriod}) + +Return the units that correspond to a particular period. + +# Examples + +```julia +julia> unit(Second(15)) == u"s" +true + +julia> unit(Hour) == u"hr" +true +``` +""" +unit(p::Dates.FixedPeriod) = unit(typeof(p)) + +numtype(x::Dates.FixedPeriod) = numtype(typeof(x)) +numtype(::Type{T}) where {T<:Dates.FixedPeriod} = Int64 + +quantitytype(x::Dates.FixedPeriod) = quantitytype(typeof(x)) +quantitytype(::Type{T}) where {T<:Dates.FixedPeriod} = + Quantity{numtype(T),dimension(T),typeof(unit(T))} + +ustrip(p::Dates.FixedPeriod) = Dates.value(p) + +Quantity(period::Dates.FixedPeriod) = Quantity(ustrip(period), unit(period)) + +uconvert(u::Units, period::Dates.FixedPeriod) = uconvert(u, Quantity(period)) + +convert(T::Type{<:AbstractQuantity}, period::Dates.FixedPeriod) = + convert(T, Quantity(period)) +convert(T::Type{<:Dates.FixedPeriod}, x::AbstractQuantity) = T(x) + +round(T::Type{<:Dates.FixedPeriod}, x::AbstractQuantity, r::RoundingMode=RoundNearest) = + T(round(numtype(T), ustrip(unit(T), x), r)) +round(u::Units, period::Dates.FixedPeriod, r::RoundingMode=RoundNearest; kwargs...) = + round(u, Quantity(period), r; kwargs...) +round(T::Type{<:Number}, u::Units, period::Dates.FixedPeriod, r::RoundingMode=RoundNearest; + kwargs...) = round(T, u, Quantity(period), r; kwargs...) +round(T::Type{<:AbstractQuantity}, period::Dates.FixedPeriod, r::RoundingMode=RoundNearest; + kwargs...) = round(T, Quantity(period), r; kwargs...) + +for (f, r) in ((:floor,:RoundDown), (:ceil,:RoundUp), (:trunc,:RoundToZero)) + @eval $f(T::Type{<:Dates.FixedPeriod}, x::AbstractQuantity) = round(T, x, $r) + @eval $f(u::Units, period::Dates.FixedPeriod; kwargs...) = + round(u, period, $r; kwargs...) + @eval $f(T::Type{<:Number}, u::Units, period::Dates.FixedPeriod; kwargs...) = + round(T, u, period, $r; kwargs...) + @eval $f(T::Type{<:AbstractQuantity}, period::Dates.FixedPeriod; kwargs...) = + round(T, period, $r; kwargs...) +end + +for op = (:+, :-, :*, :/, ://, :div, :fld, :cld, :mod, :rem, :atan, + :(==), :isequal, :<, :isless, :≤) + @eval $op(x::Dates.FixedPeriod, y::AbstractQuantity) = $op(Quantity(x), y) + @eval $op(x::AbstractQuantity, y::Dates.FixedPeriod) = $op(x, Quantity(y)) +end +for op = (:*, :/, ://) + @eval $op(x::Dates.FixedPeriod, y::Units) = $op(Quantity(x), y) + @eval $op(x::Units, y::Dates.FixedPeriod) = $op(x, Quantity(y)) +end + +isapprox(x::Dates.FixedPeriod, y::AbstractQuantity; kwargs...) = + isapprox(Quantity(x), y; kwargs...) +isapprox(x::AbstractQuantity, y::Dates.FixedPeriod; kwargs...) = + isapprox(x, Quantity(y); kwargs...) + +function isapprox(x::AbstractArray{<:AbstractQuantity}, y::AbstractArray{T}; + kwargs...) where {T<:Dates.Period} + if isconcretetype(T) + y′ = reinterpret(quantitytype(T), y) + else + y′ = Quantity.(y) + end + isapprox(x, y′; kwargs...) +end +isapprox(x::AbstractArray{<:Dates.Period}, + y::AbstractArray{<:AbstractQuantity}; kwargs...) = + isapprox(y, x; kwargs...) + +Base.promote_rule(::Type{Quantity{T,𝐓,U}}, ::Type{S}) where {T,U,S<:Dates.FixedPeriod} = + promote_type(Quantity{T,𝐓,U}, quantitytype(S)) diff --git a/test/dates.jl b/test/dates.jl new file mode 100644 index 00000000..f9855171 --- /dev/null +++ b/test/dates.jl @@ -0,0 +1,326 @@ +@testset "Dates stdlib" begin + @testset "> dimension, numtype, unit, ustrip" begin + for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), + (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), + (Week, u"wk")) + @test dimension(T) === dimension(T(1)) === 𝐓 + @test Unitful.numtype(T) === Unitful.numtype(T(1)) === typeof(Dates.value(T(1))) + @test unit(T) === unit(T(1)) === u + @test ustrip(T(5)) === Int64(5) + end + for T = (Month, Year) + @test_throws MethodError dimension(T) + @test_throws MethodError dimension(T(1)) + @test_throws MethodError Unitful.numtype(T) + @test_throws MethodError Unitful.numtype(T(1)) + @test_throws MethodError unit(T) + @test_throws MethodError unit(T(1)) + @test_throws MethodError ustrip(T(1)) + end + end + + @testset "> Arithmetic" begin + @testset ">> Addition/subtraction" begin + @test Second(3) + 5u"ms" === Int64(3)u"s" + 5u"ms" + @test Second(3) - 5u"ms" === Int64(3)u"s" - 5u"ms" + @test 1.0u"wk" + Day(3) === 1.0u"wk" + Int64(3)u"d" + @test 1.0u"wk" - Day(3) === 1.0u"wk" - Int64(3)u"d" + @test_throws DimensionError 1u"m" + Second(1) + @test_throws DimensionError 1u"m" - Second(1) + end + + @testset ">> Multiplication" begin + # Multiplication with quantity + @test Int64(3)u"m" * Day(1) === Int64(3)u"m*d" + @test 1.0f0u"m" * Microsecond(-2) === -2.0f0u"m*μs" + @test Hour(4) * Int32(2)u"m" === Int64(8)u"hr*m" + @test Second(5) * (3//2)u"Hz" === Rational{Int64}(15//2)u"s*Hz" + @test Second(5) * 2u"1/s" === Int64(10) + @test 2.5u"1/s^2" * Second(2) === 5.0u"1/s" + @test_throws AffineError Second(1) * 1u"°C" + @test_throws AffineError 1u"°C" * Second(1) + # Multiplication with unit + Week(5) * u"Hz" === 5.0u"wk*Hz" + u"mm" * Millisecond(20) === Int64(20)u"mm*ms" + u"ms^-1" * Millisecond(20) === Int64(20) + @test_throws AffineError Second(1) * u"°C" + @test_throws AffineError u"°C" * Second(1) + # Multiple factors + @test 3.0u"s" * Second(3) * (3//1)u"s" === 27.0u"s^3" + @test 3.0u"s" * Second(3) * Minute(3) === 27.0u"s^2*minute" + @test u"s" * Second(3) * u"minute" === Int64(3)u"s^2*minute" + @test Second(3) * u"m" * u"m" === Int64(3)u"s*m^2" + end + + @testset ">> Division" begin + # / + @test Nanosecond(10) / 2u"m" === 5.0u"ns/m" + @test Nanosecond(10) / 2.0f0u"m" === 5.0f0u"ns/m" + @test 5u"m" / Hour(2) === 2.5u"m/hr" + @test 5.0f0u"m" / Hour(2) === 2.5f0u"m/hr" + # // + @test Nanosecond(10) // 2u"m" === Rational{Int64}(5//1)u"ns/m" + @test 5u"m" // Hour(2) === Rational{Int64}(5//2)u"m/hr" + # div + @test div(Second(1), 2u"ms") == div(1u"s", Millisecond(2)) == 500 + @test div(Second(11), 2u"s") == div(11u"s", Second(2)) == 5 + @test div(Second(-5), 2u"s") == div(-5u"s", Second(2)) == -2 + @test_throws DimensionError div(Second(1), 1u"m") + @test_throws DimensionError div(1u"m", Second(1)) + # fld + @test fld(Second(1), 2u"ms") == fld(1u"s", Millisecond(2)) == 500 + @test fld(Second(11), 2u"s") == fld(11u"s", Second(2)) == 5 + @test fld(Second(-5), 2u"s") == fld(-5u"s", Second(2)) == -3 + @test_throws DimensionError fld(Second(1), 1u"m") + @test_throws DimensionError fld(1u"m", Second(1)) + # cld + @test cld(Second(1), 2u"ms") == cld(1u"s", Millisecond(2)) == 500 + @test cld(Second(11), 2u"s") == cld(11u"s", Second(2)) == 6 + @test cld(Second(-5), 2u"s") == cld(-5u"s", Second(2)) == -2 + @test_throws DimensionError cld(Second(1), 1u"m") + @test_throws DimensionError cld(1u"m", Second(1)) + # mod + @test mod(Second(11), 3_000u"ms") == mod(11u"s", Millisecond(3_000)) == 2u"s" + @test mod(Second(11), -3u"s") == mod(11u"s", Second(-3)) == -1u"s" + @test_throws DimensionError mod(Second(1), 1u"m") + @test_throws DimensionError mod(1u"m", Second(1)) + # rem + @test rem(Second(11), 3_000u"ms") == rem(11u"s", Millisecond(3_000)) == 2u"s" + @test rem(Second(11), -3u"s") == rem(11u"s", Second(-3)) == 2u"s" + @test_throws DimensionError rem(Second(1), 1u"m") + @test_throws DimensionError rem(1u"m", Second(1)) + end + + @testset ">> atan" begin + @test atan(Minute(1), 30u"s") == atan(2,1) + @test atan(1u"ms", Millisecond(5)) == atan(1,5) + @test_throws DimensionError atan(Second(1), 1u"m") + @test_throws DimensionError atan(1u"m", Second(1)) + end + end + + @testset "> Conversion" begin + @test uconvert(u"s", Second(3)) === Int64(3)u"s" + @test uconvert(u"hr", Minute(90)) === Rational{Int64}(3//2)u"hr" + @test uconvert(u"ns", Millisecond(-2)) === Int64(-2_000_000)u"ns" + @test uconvert(u"wk", Hour(1)) === Rational{Int64}(1//168)u"wk" + @test_throws DimensionError uconvert(u"m", Second(1)) + + @test convert(typeof(1.0u"s"), Second(3)) === 3.0u"s" + @test convert(typeof((1//1)u"s"), Second(3)) === (3//1)u"s" + @test convert(typeof(1.0u"d"), Hour(6)) === 0.25u"d" + @test_throws DimensionError convert(typeof(1.0u"m"), Week(1)) + + @test Week(4u"wk") === Week(4) + @test Microsecond((3//2)u"ms") === Microsecond(1500) + @test Millisecond(1.0u"s") === Millisecond(1000) + @test_throws DimensionError Second(1u"m") + + @test convert(Second, 1.0u"s") === Second(1) + @test convert(Day, 3u"wk") === Day(21) + @test_throws DimensionError convert(Second, 1u"m") + @test_throws InexactError convert(Second, 1u"ms") + @test_throws InexactError convert(Second, 1.5u"s") + end + + @testset "> Rounding" begin + @test round(Second, -1.2u"s") === round(Second, -1.2u"s", RoundNearest) === Second(-1) + @test round(Second, -1.5u"s") === round(Second, -1.5u"s", RoundNearest) === Second(-2) + @test round(Second, -0.5u"s") === round(Second, -0.5u"s", RoundNearest) === Second(0) + @test round(Minute, 45u"s") === round(Minute, 45u"s", RoundNearest) === Minute(1) + @test round(Minute, 90u"s") === round(Minute, 90u"s", RoundNearest) === Minute(2) + @test round(Minute, 150u"s") === round(Minute, 150u"s", RoundNearest) === Minute(2) + @test round(Second, -1.2u"s", RoundNearestTiesAway) === Second(-1) + @test round(Second, -1.5u"s", RoundNearestTiesAway) === Second(-2) + @test round(Second, -0.5u"s", RoundNearestTiesAway) === Second(-1) + @test round(Minute, 45u"s", RoundNearestTiesAway) === Minute(1) + @test round(Minute, 90u"s", RoundNearestTiesAway) === Minute(2) + @test round(Minute, 150u"s", RoundNearestTiesAway) === Minute(3) + @test round(Second, -1.2u"s", RoundNearestTiesUp) === Second(-1) + @test round(Second, -1.5u"s", RoundNearestTiesUp) === Second(-1) + @test round(Second, -0.5u"s", RoundNearestTiesUp) === Second(0) + @test round(Minute, 45u"s", RoundNearestTiesUp) === Minute(1) + @test round(Minute, 90u"s", RoundNearestTiesUp) === Minute(2) + @test round(Minute, 150u"s", RoundNearestTiesUp) === Minute(3) + @test trunc(Second, -1.2u"s") === round(Second, -1.2u"s", RoundToZero) === Second(-1) + @test trunc(Second, -1.5u"s") === round(Second, -1.5u"s", RoundToZero) === Second(-1) + @test trunc(Second, -0.5u"s") === round(Second, -0.5u"s", RoundToZero) === Second(0) + @test ceil(Second, -1.2u"s") === round(Second, -1.2u"s", RoundUp) === Second(-1) + @test ceil(Second, -1.5u"s") === round(Second, -1.5u"s", RoundUp) === Second(-1) + @test ceil(Second, -0.5u"s") === round(Second, -0.5u"s", RoundUp) === Second(0) + @test floor(Second, -1.2u"s") === round(Second, -1.2u"s", RoundDown) === Second(-2) + @test floor(Second, -1.5u"s") === round(Second, -1.5u"s", RoundDown) === Second(-2) + @test floor(Second, -0.5u"s") === round(Second, -0.5u"s", RoundDown) === Second(-1) + @static if VERSION ≥ v"1.5.0-DEV.742" + @test trunc(Minute, 45u"s") === round(Minute, 45u"s", RoundToZero) === Minute(0) + @test trunc(Minute, 90u"s") === round(Minute, 90u"s", RoundToZero) === Minute(1) + @test trunc(Minute, 150u"s") === round(Minute, 150u"s", RoundToZero) === Minute(2) + @test ceil(Minute, 45u"s") === round(Minute, 45u"s", RoundUp) === Minute(1) + @test ceil(Minute, 90u"s") === round(Minute, 90u"s", RoundUp) === Minute(2) + @test ceil(Minute, 150u"s") === round(Minute, 150u"s", RoundUp) === Minute(3) + @test floor(Minute, 45u"s") === round(Minute, 45u"s", RoundDown) === Minute(0) + @test floor(Minute, 90u"s") === round(Minute, 90u"s", RoundDown) === Minute(1) + @test floor(Minute, 150u"s") === round(Minute, 150u"s", RoundDown) === Minute(2) + end + @test_throws DimensionError round(Second, 1u"m") + @test_throws DimensionError round(Second, 1u"m", RoundNearestTiesUp) + @test_throws DimensionError trunc(Second, 1u"m") + @test_throws DimensionError ceil(Second, 1u"m") + @test_throws DimensionError floor(Second, 1u"m") + @test round(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundNearest) === (-1//1)u"minute" + @test round(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundNearest) === (-2//1)u"minute" + @test round(u"minute", Second(150)) === round(u"minute", Second(150), RoundNearest) === (2//1)u"minute" + @test round(u"minute", Second(-50), RoundNearestTiesAway) === (-1//1)u"minute" + @test round(u"minute", Second(-90), RoundNearestTiesAway) === (-2//1)u"minute" + @test round(u"minute", Second(150), RoundNearestTiesAway) === (3//1)u"minute" + @test round(u"minute", Second(-50), RoundNearestTiesUp) === (-1//1)u"minute" + @test round(u"minute", Second(-90), RoundNearestTiesUp) === (-1//1)u"minute" + @test round(u"minute", Second(150), RoundNearestTiesUp) === (3//1)u"minute" + @static if VERSION ≥ v"1.5.0-DEV.742" + @test trunc(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundToZero) === (0//1)u"minute" + @test trunc(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundToZero) === (-1//1)u"minute" + @test trunc(u"minute", Second(150)) === round(u"minute", Second(150), RoundToZero) === (2//1)u"minute" + @test ceil(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundUp) === (0//1)u"minute" + @test ceil(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundUp) === (-1//1)u"minute" + @test ceil(u"minute", Second(150)) === round(u"minute", Second(150), RoundUp) === (3//1)u"minute" + @test floor(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundDown) === (-1//1)u"minute" + @test floor(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundDown) === (-2//1)u"minute" + @test floor(u"minute", Second(150)) === round(u"minute", Second(150), RoundDown) === (2//1)u"minute" + end + @test_throws DimensionError round(u"m", Second(1)) + @test_throws DimensionError round(u"m", Second(1), RoundNearestTiesAway) + @test_throws DimensionError trunc(u"m", Second(1)) + @test_throws DimensionError ceil(u"m", Second(1)) + @test_throws DimensionError floor(u"m", Second(1)) + @test round(u"wk", Day(10), digits=1) === 1.4u"wk" + @test round(u"wk", Day(10), sigdigits=3) === 1.43u"wk" + @test round(u"wk", Day(10), RoundUp, digits=1) === 1.5u"wk" + @test floor(u"wk", Day(10), sigdigits=3) === round(u"wk", Day(10), RoundDown, sigdigits=3) === 1.42u"wk" + @test ceil(u"wk", Day(10), digits=2, base=2) === round(u"wk", Day(10), RoundUp, digits=2, base=2) === 1.5u"wk" + @test trunc(u"wk", Day(10), digits=2, base=2) === round(u"wk", Day(10), RoundToZero, digits=2, base=2) === 1.25u"wk" + @test_throws DimensionError round(u"m", Day(10), digits=1) + @test_throws DimensionError round(u"m", Day(10), sigdigits=3, base=2) + @test_throws DimensionError round(u"m", Day(10), RoundUp, digits=1) + @test_throws DimensionError trunc(u"m", Day(10), digits=1) + @test_throws DimensionError ceil(u"m", Day(10), sigdigits=3) + @test_throws DimensionError floor(u"m", Day(10), digits=1, base=2) + @test round(Int, u"minute", Second(-50)) === -1u"minute" + @static if VERSION ≥ v"1.5.0-DEV.742" + @test floor(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundDown) === 0u"minute" + @test ceil(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundUp) === 1u"minute" + @test trunc(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundToZero) === 0u"minute" + end + @test round(Float32, u"minute", Second(-50)) === -1.0f0u"minute" + @static if VERSION ≥ v"1.5.0-DEV.742" + @test floor(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundDown) === 0.0f0u"minute" + @test ceil(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundUp) === 1.0f0u"minute" + @test trunc(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundToZero) === 0.0f0u"minute" + end + @test round(Float32, u"wk", Day(10), digits=1) === 1.4f0u"wk" + @test round(Float32, u"wk", Day(10), sigdigits=3) === 1.43f0u"wk" + @test round(Float32, u"wk", Day(10), RoundUp, digits=1) === 1.5f0u"wk" + @test floor(Float32, u"wk", Day(10), sigdigits=3) === round(Float32, u"wk", Day(10), RoundDown, sigdigits=3) === 1.42f0u"wk" + @test ceil(Float32, u"wk", Day(10), digits=2, base=2) === round(Float32, u"wk", Day(10), RoundUp, digits=2, base=2) === 1.5f0u"wk" + @test trunc(Float32, u"wk", Day(10), digits=2, base=2) === round(Float32, u"wk", Day(10), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test_throws DimensionError round(Float32, u"m", Second(-50)) + @test_throws DimensionError round(Float32, u"m", Second(-50), RoundDown) + @test_throws DimensionError round(Float32, u"m", Second(-50), digits=1) + @test_throws DimensionError round(Float32, u"m", Second(-50), RoundDown, sigdigits=3) + @test_throws DimensionError floor(Float32, u"m", Second(-50)) + @test_throws DimensionError ceil(Float32, u"m", Second(-50), digits=1) + @test_throws DimensionError trunc(Float32, u"m", Second(-50), sigdigits=3, base=2) + @test round(typeof(1.0f0u"minute"), Second(-50)) === -1.0f0u"minute" + @static if VERSION ≥ v"1.5.0-DEV.742" + @test round(typeof(1.0f0u"minute"), Second(50), RoundToZero) === 0.0f0u"minute" + end + @test round(typeof(1.0f0u"wk"), Day(10), digits=1) === 1.4f0u"wk" + @test round(typeof(1.0f0u"wk"), Day(10), sigdigits=3) === 1.43f0u"wk" + @test round(typeof(1.0f0u"wk"), Day(10), RoundUp, digits=1) === 1.5f0u"wk" + @test floor(typeof(1.0f0u"wk"), Day(10), sigdigits=3) === round(typeof(1.0f0u"wk"), Day(10), RoundDown, sigdigits=3) === 1.42f0u"wk" + @test ceil(typeof(1.0f0u"wk"), Day(10), digits=2, base=2) === round(typeof(1.0f0u"wk"), Day(10), RoundUp, digits=2, base=2) === 1.5f0u"wk" + @test trunc(typeof(1.0f0u"wk"), Day(10), digits=2, base=2) === round(typeof(1.0f0u"wk"), Day(10), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test_throws DimensionError round(typeof(1.0u"m"), Second(1)) + @test_throws DimensionError round(typeof(1.0u"m"), Second(1), RoundToZero) + @test_throws DimensionError round(typeof(1.0u"m"), Second(1), digits=1) + @test_throws DimensionError round(typeof(1.0u"m"), Second(1), RoundToZero, sigdigits=2) + @test_throws DimensionError floor(typeof(1.0u"m"), Second(1)) + @test_throws DimensionError ceil(typeof(1.0u"m"), Second(1), sigdigits=2, base=2) + @test_throws DimensionError trunc(typeof(1.0u"m"), Second(1), digits=1) + end + + @testset "> Comparison" begin + # == + @test Second(2) == 2.0u"s" + @test 72u"hr" == Day(3) + @test Millisecond(0) == -0.0u"ms" + @test !(Day(4) == 4u"hr") + @test !(4u"cm" == Day(4)) + # isequal + @test isequal(Second(2), 2.0u"s") + @test isequal(72u"hr", Day(3)) + @test !isequal(Millisecond(0), -0.0u"ms") # !isequal(0.0, -0.0) + @test !isequal((Day(4), 4u"hr")) + @test !isequal((4u"cm", Day(4))) + # < + @test Second(1) < 1001u"ms" + @test 3u"minute" < Minute(4) + @test !(Minute(3) < 3u"minute") + @test !(7u"d" < Week(1)) + @test !(-0.0u"d" < Day(0)) + @test_throws DimensionError 7u"kg" < Day(1) + @test_throws DimensionError Day(1) < 7u"kg" + # isless + @test isless(Second(1), 1001u"ms") + @test isless(3u"minute", Minute(4)) + @test !isless(Minute(3), 3u"minute") + @test !isless(7u"d", Week(1)) + @test isless(-0.0u"d", Day(0)) + @test_throws DimensionError isless(7u"kg", Day(1)) + @test_throws DimensionError isless(Day(1), 7u"kg") + # ≤ + @test Second(1) ≤ 1001u"ms" + @test 7u"d" ≤ Week(1) + @test !(Minute(4) ≤ 3u"minute") + @test_throws DimensionError 7u"kg" ≤ Day(1) + @test_throws DimensionError Day(1) ≤ 7u"kg" + # min + @test min(1u"s", Microsecond(100)) == Microsecond(100) + @test min(Day(1), 1u"hr") == 1u"hr" + @test_throws DimensionError min(1u"kg", Second(1)) + @test_throws DimensionError min(Second(1), 1u"kg") + # max + @test max(1u"s", Microsecond(100)) == 1u"s" + @test max(Day(1), 1u"hr") == Day(1) + @test_throws DimensionError max(1u"kg", Second(1)) + @test_throws DimensionError max(Second(1), 1u"kg") + end + + @testset "> isapprox" begin + # scalar arguments + @test isapprox(nextfloat(1.0)u"s", Second(1)) + @test isapprox(1.0u"s", Second(1), rtol=0) + @test isapprox(Second(2), 2500u"ms", atol=1u"s") + @test isapprox(Second(2), 2500u"ms", atol=Second(1)) + @test isapprox(2500u"ms", Second(2), rtol=0.5) + @test !isapprox(2500u"ms", Second(2), rtol=0.1) + @test !isapprox(nextfloat(1.0)u"s", Second(1), rtol=0) + @test !isapprox(Second(1), 1u"m") + @test !isapprox(1u"m", Second(1)) + @test !isapprox(Second(1), 1u"m", atol=1u"kg") + @test !isapprox(1u"m", Second(1), atol=Second(1)) + @test_throws DimensionError isapprox(Second(2), 2500u"ms", atol=0.5) + @test_throws DimensionError isapprox(Second(2), 2500u"ms", atol=0.5u"m") + # array arguments + @test isapprox([Second(-5), Second(5)], [-5.0u"s", 5.0u"s"]) + @test isapprox([Second(-5), Second(5)], [-4.99u"s", 5.01u"s"], rtol=1e-2) + @test_broken isapprox([1u"s", 60u"s"], Period[Second(1), Minute(1)], rtol=0) + @test !isapprox([1.0u"kg"], [Second(1)]) + end + + @testset "> promote" begin + @test promote(1u"d", Minute(1)) === promote(1u"d", Int64(1)u"minute") + @test promote(Second(10), 2.0u"fs") === promote(Int64(10)u"s", 2.0u"fs") + @test_throws ErrorException promote(1u"m", Second(1)) + @test_throws ErrorException promote(Day(1), 3u"T") + end +end diff --git a/test/runtests.jl b/test/runtests.jl index e0ff0b80..1f6207e5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,6 +33,11 @@ import Unitful: import Unitful: LengthUnits, AreaUnits, MassUnits, TemperatureUnits +using Dates: + Dates, + Nanosecond, Microsecond, Millisecond, Second, Minute, Hour, Day, Week, + Month, Year + const colon = Base.:(:) @testset "Construction" begin @@ -191,6 +196,8 @@ end end end +include("dates.jl") + @testset "Temperature and affine quantities" begin @testset "Affine transforms and quantities" begin @test 1°C isa RelativeScaleTemperature From c8fd2c3bd56c34490236207c2e51a62cc7e194ae Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 14 May 2020 20:44:15 +0200 Subject: [PATCH 02/13] Fix tests on Julia < 1.2 --- test/dates.jl | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/test/dates.jl b/test/dates.jl index f9855171..6f1025f6 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -167,15 +167,17 @@ @test_throws DimensionError trunc(Second, 1u"m") @test_throws DimensionError ceil(Second, 1u"m") @test_throws DimensionError floor(Second, 1u"m") - @test round(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundNearest) === (-1//1)u"minute" - @test round(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundNearest) === (-2//1)u"minute" - @test round(u"minute", Second(150)) === round(u"minute", Second(150), RoundNearest) === (2//1)u"minute" - @test round(u"minute", Second(-50), RoundNearestTiesAway) === (-1//1)u"minute" - @test round(u"minute", Second(-90), RoundNearestTiesAway) === (-2//1)u"minute" - @test round(u"minute", Second(150), RoundNearestTiesAway) === (3//1)u"minute" - @test round(u"minute", Second(-50), RoundNearestTiesUp) === (-1//1)u"minute" - @test round(u"minute", Second(-90), RoundNearestTiesUp) === (-1//1)u"minute" - @test round(u"minute", Second(150), RoundNearestTiesUp) === (3//1)u"minute" + @static if VERSION ≥ v"1.2.0" + @test round(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundNearest) === (-1//1)u"minute" + @test round(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundNearest) === (-2//1)u"minute" + @test round(u"minute", Second(150)) === round(u"minute", Second(150), RoundNearest) === (2//1)u"minute" + @test round(u"minute", Second(-50), RoundNearestTiesAway) === (-1//1)u"minute" + @test round(u"minute", Second(-90), RoundNearestTiesAway) === (-2//1)u"minute" + @test round(u"minute", Second(150), RoundNearestTiesAway) === (3//1)u"minute" + @test round(u"minute", Second(-50), RoundNearestTiesUp) === (-1//1)u"minute" + @test round(u"minute", Second(-90), RoundNearestTiesUp) === (-1//1)u"minute" + @test round(u"minute", Second(150), RoundNearestTiesUp) === (3//1)u"minute" + end @static if VERSION ≥ v"1.5.0-DEV.742" @test trunc(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundToZero) === (0//1)u"minute" @test trunc(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundToZero) === (-1//1)u"minute" @@ -204,14 +206,14 @@ @test_throws DimensionError trunc(u"m", Day(10), digits=1) @test_throws DimensionError ceil(u"m", Day(10), sigdigits=3) @test_throws DimensionError floor(u"m", Day(10), digits=1, base=2) - @test round(Int, u"minute", Second(-50)) === -1u"minute" + @static if VERSION ≥ v"1.2.0" + @test round(Int, u"minute", Second(-50)) === -1u"minute" + @test round(Float32, u"minute", Second(-50)) === -1.0f0u"minute" + end @static if VERSION ≥ v"1.5.0-DEV.742" @test floor(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundDown) === 0u"minute" @test ceil(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundUp) === 1u"minute" @test trunc(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundToZero) === 0u"minute" - end - @test round(Float32, u"minute", Second(-50)) === -1.0f0u"minute" - @static if VERSION ≥ v"1.5.0-DEV.742" @test floor(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundDown) === 0.0f0u"minute" @test ceil(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundUp) === 1.0f0u"minute" @test trunc(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundToZero) === 0.0f0u"minute" @@ -229,7 +231,9 @@ @test_throws DimensionError floor(Float32, u"m", Second(-50)) @test_throws DimensionError ceil(Float32, u"m", Second(-50), digits=1) @test_throws DimensionError trunc(Float32, u"m", Second(-50), sigdigits=3, base=2) - @test round(typeof(1.0f0u"minute"), Second(-50)) === -1.0f0u"minute" + @static if VERSION ≥ v"1.2.0" + @test round(typeof(1.0f0u"minute"), Second(-50)) === -1.0f0u"minute" + end @static if VERSION ≥ v"1.5.0-DEV.742" @test round(typeof(1.0f0u"minute"), Second(50), RoundToZero) === 0.0f0u"minute" end @@ -259,8 +263,8 @@ @test isequal(Second(2), 2.0u"s") @test isequal(72u"hr", Day(3)) @test !isequal(Millisecond(0), -0.0u"ms") # !isequal(0.0, -0.0) - @test !isequal((Day(4), 4u"hr")) - @test !isequal((4u"cm", Day(4))) + @test !isequal(Day(4), 4u"hr") + @test !isequal(4u"cm", Day(4)) # < @test Second(1) < 1001u"ms" @test 3u"minute" < Minute(4) From 18fc3e031f9baa4f1bfc054f87bc731f3cb00dde Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 14 May 2020 21:20:18 +0200 Subject: [PATCH 03/13] Fix more tests --- test/dates.jl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/dates.jl b/test/dates.jl index 6f1025f6..d0f32ee8 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -34,7 +34,7 @@ @test Int64(3)u"m" * Day(1) === Int64(3)u"m*d" @test 1.0f0u"m" * Microsecond(-2) === -2.0f0u"m*μs" @test Hour(4) * Int32(2)u"m" === Int64(8)u"hr*m" - @test Second(5) * (3//2)u"Hz" === Rational{Int64}(15//2)u"s*Hz" + @test Second(5) * (3//2)u"Hz" === Rational{Int64}(15,2)u"s*Hz" @test Second(5) * 2u"1/s" === Int64(10) @test 2.5u"1/s^2" * Second(2) === 5.0u"1/s" @test_throws AffineError Second(1) * 1u"°C" @@ -59,8 +59,8 @@ @test 5u"m" / Hour(2) === 2.5u"m/hr" @test 5.0f0u"m" / Hour(2) === 2.5f0u"m/hr" # // - @test Nanosecond(10) // 2u"m" === Rational{Int64}(5//1)u"ns/m" - @test 5u"m" // Hour(2) === Rational{Int64}(5//2)u"m/hr" + @test Nanosecond(10) // 2u"m" === Rational{Int64}(5,1)u"ns/m" + @test 5u"m" // Hour(2) === Rational{Int64}(5,2)u"m/hr" # div @test div(Second(1), 2u"ms") == div(1u"s", Millisecond(2)) == 500 @test div(Second(11), 2u"s") == div(11u"s", Second(2)) == 5 @@ -101,9 +101,9 @@ @testset "> Conversion" begin @test uconvert(u"s", Second(3)) === Int64(3)u"s" - @test uconvert(u"hr", Minute(90)) === Rational{Int64}(3//2)u"hr" + @test uconvert(u"hr", Minute(90)) === Rational{Int64}(3,2)u"hr" @test uconvert(u"ns", Millisecond(-2)) === Int64(-2_000_000)u"ns" - @test uconvert(u"wk", Hour(1)) === Rational{Int64}(1//168)u"wk" + @test uconvert(u"wk", Hour(1)) === Rational{Int64}(1,168)u"wk" @test_throws DimensionError uconvert(u"m", Second(1)) @test convert(typeof(1.0u"s"), Second(3)) === 3.0u"s" @@ -168,15 +168,15 @@ @test_throws DimensionError ceil(Second, 1u"m") @test_throws DimensionError floor(Second, 1u"m") @static if VERSION ≥ v"1.2.0" - @test round(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundNearest) === (-1//1)u"minute" - @test round(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundNearest) === (-2//1)u"minute" - @test round(u"minute", Second(150)) === round(u"minute", Second(150), RoundNearest) === (2//1)u"minute" - @test round(u"minute", Second(-50), RoundNearestTiesAway) === (-1//1)u"minute" - @test round(u"minute", Second(-90), RoundNearestTiesAway) === (-2//1)u"minute" - @test round(u"minute", Second(150), RoundNearestTiesAway) === (3//1)u"minute" - @test round(u"minute", Second(-50), RoundNearestTiesUp) === (-1//1)u"minute" - @test round(u"minute", Second(-90), RoundNearestTiesUp) === (-1//1)u"minute" - @test round(u"minute", Second(150), RoundNearestTiesUp) === (3//1)u"minute" + @test round(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundNearest) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundNearest) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", Second(150)) === round(u"minute", Second(150), RoundNearest) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", Second(-50), RoundNearestTiesAway) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", Second(-90), RoundNearestTiesAway) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", Second(150), RoundNearestTiesAway) === Rational{Int64}(3,1)u"minute" + @test round(u"minute", Second(-50), RoundNearestTiesUp) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", Second(-90), RoundNearestTiesUp) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", Second(150), RoundNearestTiesUp) === Rational{Int64}(3,1)u"minute" end @static if VERSION ≥ v"1.5.0-DEV.742" @test trunc(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundToZero) === (0//1)u"minute" From 4852218cec781fa5ce10b90028c2038ce0e56cf9 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Fri, 15 May 2020 08:40:28 +0200 Subject: [PATCH 04/13] Delete unused method --- src/dates.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dates.jl b/src/dates.jl index ba2bb812..592c5b06 100644 --- a/src/dates.jl +++ b/src/dates.jl @@ -32,7 +32,6 @@ unit(p::Dates.FixedPeriod) = unit(typeof(p)) numtype(x::Dates.FixedPeriod) = numtype(typeof(x)) numtype(::Type{T}) where {T<:Dates.FixedPeriod} = Int64 -quantitytype(x::Dates.FixedPeriod) = quantitytype(typeof(x)) quantitytype(::Type{T}) where {T<:Dates.FixedPeriod} = Quantity{numtype(T),dimension(T),typeof(unit(T))} From 3cc67bc5f5e4d264694565fe953815e003a22e48 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Fri, 15 May 2020 11:47:11 +0200 Subject: [PATCH 05/13] Add some more tests --- test/dates.jl | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/test/dates.jl b/test/dates.jl index d0f32ee8..635140a3 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -1,12 +1,11 @@ @testset "Dates stdlib" begin - @testset "> dimension, numtype, unit, ustrip" begin + @testset "> dimension, numtype, unit" begin for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), (Week, u"wk")) @test dimension(T) === dimension(T(1)) === 𝐓 @test Unitful.numtype(T) === Unitful.numtype(T(1)) === typeof(Dates.value(T(1))) @test unit(T) === unit(T(1)) === u - @test ustrip(T(5)) === Int64(5) end for T = (Month, Year) @test_throws MethodError dimension(T) @@ -15,7 +14,6 @@ @test_throws MethodError Unitful.numtype(T(1)) @test_throws MethodError unit(T) @test_throws MethodError unit(T(1)) - @test_throws MethodError ustrip(T(1)) end end @@ -100,11 +98,25 @@ end @testset "> Conversion" begin - @test uconvert(u"s", Second(3)) === Int64(3)u"s" - @test uconvert(u"hr", Minute(90)) === Rational{Int64}(3,2)u"hr" - @test uconvert(u"ns", Millisecond(-2)) === Int64(-2_000_000)u"ns" - @test uconvert(u"wk", Hour(1)) === Rational{Int64}(1,168)u"wk" + @test uconvert(u"s", Second(3)) === u"s"(Second(3)) === Int64(3)u"s" + @test uconvert(u"hr", Minute(90)) === u"hr"(Minute(90)) === Rational{Int64}(3,2)u"hr" + @test uconvert(u"ns", Millisecond(-2)) === u"ns"(Millisecond(-2)) === Int64(-2_000_000)u"ns" + @test uconvert(u"wk", Hour(1)) === u"wk"(Hour(1)) === Rational{Int64}(1,168)u"wk" @test_throws DimensionError uconvert(u"m", Second(1)) + @test_throws DimensionError u"m"(Second(1)) + + for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), + (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), + (Week, u"wk")) + @test ustrip(T(5)) === ustrip(u, T(5)) === Int64(5) + end + @test ustrip(u"ms", Second(1)) === Int64(1000) + @test ustrip(u"wk", Day(1)) === Rational{Int64}(1,7) + @test_throws DimensionError ustrip(u"m", Nanosecond(1)) + @test_throws MethodError ustrip(Month(1)) + @test_throws MethodError ustrip(Year(1)) + @test_throws MethodError ustrip(u"s", Month(1)) + @test_throws MethodError ustrip(u"yr", Year(1)) @test convert(typeof(1.0u"s"), Second(3)) === 3.0u"s" @test convert(typeof((1//1)u"s"), Second(3)) === (3//1)u"s" From aec64dbe22296ee6282daa6aa03a6bc712978c2a Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Fri, 15 May 2020 12:20:01 +0200 Subject: [PATCH 06/13] Fix tests on 32-bit master --- test/dates.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/dates.jl b/test/dates.jl index 635140a3..ddc3ea0c 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -191,15 +191,15 @@ @test round(u"minute", Second(150), RoundNearestTiesUp) === Rational{Int64}(3,1)u"minute" end @static if VERSION ≥ v"1.5.0-DEV.742" - @test trunc(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundToZero) === (0//1)u"minute" - @test trunc(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundToZero) === (-1//1)u"minute" - @test trunc(u"minute", Second(150)) === round(u"minute", Second(150), RoundToZero) === (2//1)u"minute" - @test ceil(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundUp) === (0//1)u"minute" - @test ceil(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundUp) === (-1//1)u"minute" - @test ceil(u"minute", Second(150)) === round(u"minute", Second(150), RoundUp) === (3//1)u"minute" - @test floor(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundDown) === (-1//1)u"minute" - @test floor(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundDown) === (-2//1)u"minute" - @test floor(u"minute", Second(150)) === round(u"minute", Second(150), RoundDown) === (2//1)u"minute" + @test trunc(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundToZero) === Rational{Int64}(0,1)u"minute" + @test trunc(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundToZero) === Rational{Int64}(-1,1)u"minute" + @test trunc(u"minute", Second(150)) === round(u"minute", Second(150), RoundToZero) === Rational{Int64}(2,1)u"minute" + @test ceil(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundUp) === Rational{Int64}(0,1)u"minute" + @test ceil(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundUp) === Rational{Int64}(-1,1)u"minute" + @test ceil(u"minute", Second(150)) === round(u"minute", Second(150), RoundUp) === Rational{Int64}(3,1)u"minute" + @test floor(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundDown) === Rational{Int64}(-1,1)u"minute" + @test floor(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundDown) === Rational{Int64}(-2,1)u"minute" + @test floor(u"minute", Second(150)) === round(u"minute", Second(150), RoundDown) === Rational{Int64}(2,1)u"minute" end @test_throws DimensionError round(u"m", Second(1)) @test_throws DimensionError round(u"m", Second(1), RoundNearestTiesAway) From ab0a1c392e19f5adc65102a8d29101663e223e4b Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Sat, 16 May 2020 10:51:49 +0200 Subject: [PATCH 07/13] Add three-argument div --- src/dates.jl | 10 ++-- test/dates.jl | 128 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 92 insertions(+), 46 deletions(-) diff --git a/src/dates.jl b/src/dates.jl index 592c5b06..b8128a0c 100644 --- a/src/dates.jl +++ b/src/dates.jl @@ -41,8 +41,9 @@ Quantity(period::Dates.FixedPeriod) = Quantity(ustrip(period), unit(period)) uconvert(u::Units, period::Dates.FixedPeriod) = uconvert(u, Quantity(period)) -convert(T::Type{<:AbstractQuantity}, period::Dates.FixedPeriod) = - convert(T, Quantity(period)) +(T::Type{<:AbstractQuantity})(period::Dates.FixedPeriod) = T(Quantity(period)) + +convert(T::Type{<:AbstractQuantity}, period::Dates.FixedPeriod) = T(period) convert(T::Type{<:Dates.FixedPeriod}, x::AbstractQuantity) = T(x) round(T::Type{<:Dates.FixedPeriod}, x::AbstractQuantity, r::RoundingMode=RoundNearest) = @@ -64,7 +65,7 @@ for (f, r) in ((:floor,:RoundDown), (:ceil,:RoundUp), (:trunc,:RoundToZero)) round(T, period, $r; kwargs...) end -for op = (:+, :-, :*, :/, ://, :div, :fld, :cld, :mod, :rem, :atan, +for op = (:+, :-, :*, :/, ://, :fld, :cld, :mod, :rem, :atan, :(==), :isequal, :<, :isless, :≤) @eval $op(x::Dates.FixedPeriod, y::AbstractQuantity) = $op(Quantity(x), y) @eval $op(x::AbstractQuantity, y::Dates.FixedPeriod) = $op(x, Quantity(y)) @@ -74,6 +75,9 @@ for op = (:*, :/, ://) @eval $op(x::Units, y::Dates.FixedPeriod) = $op(x, Quantity(y)) end +div(x::Dates.FixedPeriod, y::AbstractQuantity, r...) = div(Quantity(x), y, r...) +div(x::AbstractQuantity, y::Dates.FixedPeriod, r...) = div(x, Quantity(y), r...) + isapprox(x::Dates.FixedPeriod, y::AbstractQuantity; kwargs...) = isapprox(Quantity(x), y; kwargs...) isapprox(x::AbstractQuantity, y::Dates.FixedPeriod; kwargs...) = diff --git a/test/dates.jl b/test/dates.jl index ddc3ea0c..95e3c82a 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -60,33 +60,47 @@ @test Nanosecond(10) // 2u"m" === Rational{Int64}(5,1)u"ns/m" @test 5u"m" // Hour(2) === Rational{Int64}(5,2)u"m/hr" # div - @test div(Second(1), 2u"ms") == div(1u"s", Millisecond(2)) == 500 - @test div(Second(11), 2u"s") == div(11u"s", Second(2)) == 5 - @test div(Second(-5), 2u"s") == div(-5u"s", Second(2)) == -2 + @test div(Second(1), 2u"ms") == div(1u"s", Millisecond(2)) == div(1000, 2) + @test div(Second(11), 2u"s") == div(11u"s", Second(2)) == div(11, 2) + @test div(Second(-5), 2u"s") == div(-5u"s", Second(2)) == div(-5, 2) @test_throws DimensionError div(Second(1), 1u"m") @test_throws DimensionError div(1u"m", Second(1)) + @static if VERSION ≥ v"1.4.0-DEV.208" + for r = (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundToZero, RoundUp, RoundDown) + @test div(Second(11), 2u"s", r) == div(11u"s", Second(2), r) == div(11, 2, r) + @test div(Second(-5), 2u"s", r) == div(-5u"s", Second(2), r) == div(-5, 2, r) + @test_throws DimensionError div(Second(1), 1u"m", r) + @test_throws DimensionError div(1u"m", Second(1), r) + end + end # fld - @test fld(Second(1), 2u"ms") == fld(1u"s", Millisecond(2)) == 500 - @test fld(Second(11), 2u"s") == fld(11u"s", Second(2)) == 5 - @test fld(Second(-5), 2u"s") == fld(-5u"s", Second(2)) == -3 + @test fld(Second(1), 2u"ms") == fld(1u"s", Millisecond(2)) == fld(1000, 2) + @test fld(Second(11), 2u"s") == fld(11u"s", Second(2)) == fld(11, 2) + @test fld(Second(-5), 2u"s") == fld(-5u"s", Second(2)) == fld(-5, 2) @test_throws DimensionError fld(Second(1), 1u"m") @test_throws DimensionError fld(1u"m", Second(1)) # cld - @test cld(Second(1), 2u"ms") == cld(1u"s", Millisecond(2)) == 500 - @test cld(Second(11), 2u"s") == cld(11u"s", Second(2)) == 6 - @test cld(Second(-5), 2u"s") == cld(-5u"s", Second(2)) == -2 + @test cld(Second(1), 2u"ms") == cld(1u"s", Millisecond(2)) == cld(1000, 2) + @test cld(Second(11), 2u"s") == cld(11u"s", Second(2)) == cld(11, 2) + @test cld(Second(-5), 2u"s") == cld(-5u"s", Second(2)) == cld(-5, 2) @test_throws DimensionError cld(Second(1), 1u"m") @test_throws DimensionError cld(1u"m", Second(1)) # mod - @test mod(Second(11), 3_000u"ms") == mod(11u"s", Millisecond(3_000)) == 2u"s" - @test mod(Second(11), -3u"s") == mod(11u"s", Second(-3)) == -1u"s" + @test mod(Second(11), 3_000u"ms") == mod(11u"s", Millisecond(3_000)) == mod(11, 3)u"s" + @test mod(Second(11), -3u"s") == mod(11u"s", Second(-3)) == mod(11, -3)u"s" @test_throws DimensionError mod(Second(1), 1u"m") @test_throws DimensionError mod(1u"m", Second(1)) # rem - @test rem(Second(11), 3_000u"ms") == rem(11u"s", Millisecond(3_000)) == 2u"s" - @test rem(Second(11), -3u"s") == rem(11u"s", Second(-3)) == 2u"s" + @test rem(Second(11), 3_000u"ms") == rem(11u"s", Millisecond(3_000)) == rem(11, 3)u"s" + @test rem(Second(11), -3u"s") == rem(11u"s", Second(-3)) == rem(11, -3)u"s" @test_throws DimensionError rem(Second(1), 1u"m") @test_throws DimensionError rem(1u"m", Second(1)) + for r = (RoundToZero, RoundUp, RoundDown) + @test rem(Second(11), 2u"s", r) == rem(11u"s", Second(2), r) == rem(11, 2, r)u"s" + @test rem(Second(-5), 2u"s", r) == rem(-5u"s", Second(2), r) == rem(-5, 2, r)u"s" + @test_throws DimensionError rem(Second(1), 1u"m", r) + @test_throws DimensionError rem(1u"m", Second(1), r) + end end @testset ">> atan" begin @@ -98,41 +112,69 @@ end @testset "> Conversion" begin - @test uconvert(u"s", Second(3)) === u"s"(Second(3)) === Int64(3)u"s" - @test uconvert(u"hr", Minute(90)) === u"hr"(Minute(90)) === Rational{Int64}(3,2)u"hr" - @test uconvert(u"ns", Millisecond(-2)) === u"ns"(Millisecond(-2)) === Int64(-2_000_000)u"ns" - @test uconvert(u"wk", Hour(1)) === u"wk"(Hour(1)) === Rational{Int64}(1,168)u"wk" - @test_throws DimensionError uconvert(u"m", Second(1)) - @test_throws DimensionError u"m"(Second(1)) + @testset ">> uconvert" begin + @test uconvert(u"s", Second(3)) === u"s"(Second(3)) === Int64(3)u"s" + @test uconvert(u"hr", Minute(90)) === u"hr"(Minute(90)) === Rational{Int64}(3,2)u"hr" + @test uconvert(u"ns", Millisecond(-2)) === u"ns"(Millisecond(-2)) === Int64(-2_000_000)u"ns" + @test uconvert(u"wk", Hour(1)) === u"wk"(Hour(1)) === Rational{Int64}(1,168)u"wk" + @test_throws DimensionError uconvert(u"m", Second(1)) + @test_throws DimensionError u"m"(Second(1)) + end - for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), - (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), - (Week, u"wk")) - @test ustrip(T(5)) === ustrip(u, T(5)) === Int64(5) + @testset ">> ustrip" begin + for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), + (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), + (Week, u"wk")) + @test ustrip(T(5)) === ustrip(u, T(5)) === Int64(5) + end + @test ustrip(u"ms", Second(1)) === Int64(1000) + @test ustrip(u"wk", Day(1)) === Rational{Int64}(1,7) + @test_throws DimensionError ustrip(u"m", Nanosecond(1)) + @test_throws MethodError ustrip(Month(1)) + @test_throws MethodError ustrip(Year(1)) + @test_throws MethodError ustrip(u"s", Month(1)) + @test_throws MethodError ustrip(u"yr", Year(1)) end - @test ustrip(u"ms", Second(1)) === Int64(1000) - @test ustrip(u"wk", Day(1)) === Rational{Int64}(1,7) - @test_throws DimensionError ustrip(u"m", Nanosecond(1)) - @test_throws MethodError ustrip(Month(1)) - @test_throws MethodError ustrip(Year(1)) - @test_throws MethodError ustrip(u"s", Month(1)) - @test_throws MethodError ustrip(u"yr", Year(1)) - @test convert(typeof(1.0u"s"), Second(3)) === 3.0u"s" - @test convert(typeof((1//1)u"s"), Second(3)) === (3//1)u"s" - @test convert(typeof(1.0u"d"), Hour(6)) === 0.25u"d" - @test_throws DimensionError convert(typeof(1.0u"m"), Week(1)) + @testset ">> Constructors" begin + for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), + (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), + (Week, u"wk")) + @test Quantity(T(5)) === Int64(5)*u + @test Quantity{Float64,𝐓,typeof(u)}(T(5)) === 5.0u + end + @test Quantity{Float64,𝐓,typeof(u"d")}(Hour(6)) === 0.25u"d" + @test_throws InexactError Quantity{Int64,𝐓,typeof(u"d")}(Hour(6)) + @test_throws DimensionError Quantity{Float64,𝐋,typeof(u"m")}(Hour(6)) + @test_throws MethodError Quantity{Float64,𝐓,typeof(u"d")}(Month(1)) + @test_throws MethodError Quantity{Float64,𝐓,typeof(u"d")}(Year(1)) - @test Week(4u"wk") === Week(4) - @test Microsecond((3//2)u"ms") === Microsecond(1500) - @test Millisecond(1.0u"s") === Millisecond(1000) - @test_throws DimensionError Second(1u"m") + @test Week(4u"wk") === Week(4) + @test Microsecond((3//2)u"ms") === Microsecond(1500) + @test Millisecond(1.0u"s") === Millisecond(1000) + @test_throws InexactError Second(1u"ms") + @test_throws DimensionError Second(1u"m") + @test_throws DimensionError Month(1u"s") # Doesn't throw MethodError because Month(::Number) exists + @test_throws DimensionError Year(1u"s") # Doesn't throw MethodError because Year(::Number) exists + end + + @testset ">> convert" begin + @test convert(typeof(1.0u"s"), Second(3)) === 3.0u"s" + @test convert(typeof((1//1)u"s"), Second(3)) === (3//1)u"s" + @test convert(typeof(1.0u"d"), Hour(6)) === 0.25u"d" + @test_throws InexactError convert(typeof(1u"d"), Hour(6)) + @test_throws DimensionError convert(typeof(1.0u"m"), Week(1)) + @test_throws MethodError convert(typeof(1u"d"), Month(1)) + @test_throws MethodError convert(typeof(1u"d"), Year(1)) - @test convert(Second, 1.0u"s") === Second(1) - @test convert(Day, 3u"wk") === Day(21) - @test_throws DimensionError convert(Second, 1u"m") - @test_throws InexactError convert(Second, 1u"ms") - @test_throws InexactError convert(Second, 1.5u"s") + @test convert(Second, 1.0u"s") === Second(1) + @test convert(Day, 3u"wk") === Day(21) + @test_throws DimensionError convert(Second, 1u"m") + @test_throws InexactError convert(Second, 1u"ms") + @test_throws InexactError convert(Second, 1.5u"s") + @test_throws MethodError convert(Month, 1u"s") + @test_throws MethodError convert(Year, 1u"s") + end end @testset "> Rounding" begin From d462642b48485c0eb3afbb9f0525991fb020a77b Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Wed, 20 May 2020 08:43:08 +0200 Subject: [PATCH 08/13] Add support for CompoundPeriod --- src/dates.jl | 87 +++++++- test/dates.jl | 555 +++++++++++++++++++++++++++++++++++++++-------- test/runtests.jl | 3 +- 3 files changed, 547 insertions(+), 98 deletions(-) diff --git a/src/dates.jl b/src/dates.jl index b8128a0c..07fb40a8 100644 --- a/src/dates.jl +++ b/src/dates.jl @@ -1,9 +1,10 @@ # Conversion from and to types from the `Dates` stdlib -import Dates: Week, Day, Hour, Minute, Second, Millisecond, Microsecond, Nanosecond +# Dates.FixedPeriod -for (period, unit) = ((Nanosecond, ns), (Microsecond, μs), (Millisecond, ms), (Second, s), - (Minute, minute), (Hour, hr), (Day, d), (Week, wk)) +for (period, unit) = ((Dates.Week, wk), (Dates.Day, d), (Dates.Hour, hr), + (Dates.Minute, minute), (Dates.Second, s), (Dates.Millisecond, ms), + (Dates.Microsecond, μs), (Dates.Nanosecond, ns)) @eval unit(::Type{$period}) = $unit @eval (::Type{$period})(x::AbstractQuantity) = $period(ustrip(unit($period), x)) end @@ -74,7 +75,6 @@ for op = (:*, :/, ://) @eval $op(x::Dates.FixedPeriod, y::Units) = $op(Quantity(x), y) @eval $op(x::Units, y::Dates.FixedPeriod) = $op(x, Quantity(y)) end - div(x::Dates.FixedPeriod, y::AbstractQuantity, r...) = div(Quantity(x), y, r...) div(x::AbstractQuantity, y::Dates.FixedPeriod, r...) = div(x, Quantity(y), r...) @@ -92,9 +92,82 @@ function isapprox(x::AbstractArray{<:AbstractQuantity}, y::AbstractArray{T}; end isapprox(x, y′; kwargs...) end -isapprox(x::AbstractArray{<:Dates.Period}, - y::AbstractArray{<:AbstractQuantity}; kwargs...) = - isapprox(y, x; kwargs...) +isapprox(x::AbstractArray{<:Dates.FixedPeriod}, y::AbstractArray{<:AbstractQuantity}; + kwargs...) = isapprox(y, x; kwargs...) Base.promote_rule(::Type{Quantity{T,𝐓,U}}, ::Type{S}) where {T,U,S<:Dates.FixedPeriod} = promote_type(Quantity{T,𝐓,U}, quantitytype(S)) + +# Dates.CompoundPeriod + +dimension(p::Dates.CompoundPeriod) = dimension(typeof(p)) +dimension(::Type{<:Dates.CompoundPeriod}) = 𝐓 + +uconvert(u::Units, period::Dates.CompoundPeriod) = + Quantity{promote_type(Int64,typeof(convfact(u,ns))),dimension(u),typeof(u)}(period) + +try_uconvert(u::Units, period::Dates.CompoundPeriod) = nothing +function try_uconvert(u::TimeUnits, period::Dates.CompoundPeriod) + T = Quantity{promote_type(Int64,typeof(convfact(u,ns))),dimension(u),typeof(u)} + val = zero(T) + for p in period.periods + p isa Dates.FixedPeriod || return nothing + val += T(p) + end + val +end + +(T::Type{<:AbstractQuantity})(period::Dates.CompoundPeriod) = + mapreduce(T, +, period.periods, init=zero(T)) + +convert(T::Type{<:AbstractQuantity}, period::Dates.CompoundPeriod) = T(period) + +round(u::Units, period::Dates.CompoundPeriod, r::RoundingMode=RoundNearest; kwargs...) = + round(u, uconvert(u, period), r; kwargs...) +round(T::Type{<:Number}, u::Units, period::Dates.CompoundPeriod, + r::RoundingMode=RoundNearest; kwargs...) = + round(T, u, uconvert(u, period), r; kwargs...) +round(T::Type{<:AbstractQuantity}, period::Dates.CompoundPeriod, + r::RoundingMode=RoundNearest; kwargs...) = + round(T, T(period), r; kwargs...) + +for (f, r) in ((:floor,:RoundDown), (:ceil,:RoundUp), (:trunc,:RoundToZero)) + @eval $f(u::Units, period::Dates.CompoundPeriod; kwargs...) = + round(u, period, $r; kwargs...) + @eval $f(T::Type{<:Number}, u::Units, period::Dates.CompoundPeriod; kwargs...) = + round(T, u, period, $r; kwargs...) + @eval $f(T::Type{<:AbstractQuantity}, period::Dates.CompoundPeriod; kwargs...) = + round(T, period, $r; kwargs...) +end + +for op = (:fld, :cld, :atan, :<, :isless, :≤) + @eval $op(x::Dates.CompoundPeriod, y::AbstractQuantity) = $op(uconvert(unit(y),x), y) + @eval $op(x::AbstractQuantity, y::Dates.CompoundPeriod) = $op(x, uconvert(unit(x),y)) +end +div(x::Dates.CompoundPeriod, y::AbstractQuantity, r...) = div(uconvert(unit(y),x), y, r...) +div(x::AbstractQuantity, y::Dates.CompoundPeriod, r...) = div(x, uconvert(unit(x),y), r...) +mod(x::Dates.CompoundPeriod, y::AbstractQuantity) = mod(uconvert(unit(y),x), y) +rem(x::Dates.CompoundPeriod, y::AbstractQuantity) = rem(uconvert(unit(y),x), y) +for op = (:(==), :isequal) + @eval $op(x::Dates.CompoundPeriod, y::AbstractQuantity{T,𝐓,U}) where {T,U} = + $op(try_uconvert(U(), x), y) + @eval $op(x::AbstractQuantity{T,𝐓,U}, y::Dates.CompoundPeriod) where {T,U} = + $op(x, try_uconvert(U(), y)) +end + +isapprox(x::Dates.CompoundPeriod, y::AbstractQuantity; kwargs...) = + dimension(y) === 𝐓 ? isapprox(uconvert(unit(y), x), y; kwargs...) : false +isapprox(x::AbstractQuantity, y::Dates.CompoundPeriod; kwargs...) = + dimension(x) === 𝐓 ? isapprox(x, uconvert(unit(x), y); kwargs...) : false + +function isapprox(x::AbstractArray{<:AbstractQuantity}, + y::AbstractArray{Dates.CompoundPeriod}; kwargs...) + if dimension(eltype(x)) === 𝐓 + isapprox(x, uconvert.(unit(eltype(x)), y); kwargs...) + else + false + end +end + +isapprox(x::AbstractArray{Dates.CompoundPeriod}, y::AbstractArray{<:AbstractQuantity}; + kwargs...) = isapprox(y, x; kwargs...) diff --git a/test/dates.jl b/test/dates.jl index 95e3c82a..d4fec1bd 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -15,6 +15,12 @@ @test_throws MethodError unit(T) @test_throws MethodError unit(T(1)) end + + for p = (CompoundPeriod, CompoundPeriod(), CompoundPeriod(Day(1)), CompoundPeriod(Day(1), Hour(-1))) + @test dimension(p) === 𝐓 + @test_throws MethodError Unitful.numtype(p) + @test_throws MethodError unit(p) + end end @testset "> Arithmetic" begin @@ -51,55 +57,92 @@ end @testset ">> Division" begin - # / - @test Nanosecond(10) / 2u"m" === 5.0u"ns/m" - @test Nanosecond(10) / 2.0f0u"m" === 5.0f0u"ns/m" - @test 5u"m" / Hour(2) === 2.5u"m/hr" - @test 5.0f0u"m" / Hour(2) === 2.5f0u"m/hr" - # // - @test Nanosecond(10) // 2u"m" === Rational{Int64}(5,1)u"ns/m" - @test 5u"m" // Hour(2) === Rational{Int64}(5,2)u"m/hr" - # div - @test div(Second(1), 2u"ms") == div(1u"s", Millisecond(2)) == div(1000, 2) - @test div(Second(11), 2u"s") == div(11u"s", Second(2)) == div(11, 2) - @test div(Second(-5), 2u"s") == div(-5u"s", Second(2)) == div(-5, 2) - @test_throws DimensionError div(Second(1), 1u"m") - @test_throws DimensionError div(1u"m", Second(1)) - @static if VERSION ≥ v"1.4.0-DEV.208" - for r = (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundToZero, RoundUp, RoundDown) - @test div(Second(11), 2u"s", r) == div(11u"s", Second(2), r) == div(11, 2, r) - @test div(Second(-5), 2u"s", r) == div(-5u"s", Second(2), r) == div(-5, 2, r) - @test_throws DimensionError div(Second(1), 1u"m", r) - @test_throws DimensionError div(1u"m", Second(1), r) + @testset ">>> /, //" begin + @test Nanosecond(10) / 2u"m" === 5.0u"ns/m" + @test Nanosecond(10) / 2.0f0u"m" === 5.0f0u"ns/m" + @test 5u"m" / Hour(2) === 2.5u"m/hr" + @test 5.0f0u"m" / Hour(2) === 2.5f0u"m/hr" + + @test Nanosecond(10) // 2u"m" === Rational{Int64}(5,1)u"ns/m" + @test 5u"m" // Hour(2) === Rational{Int64}(5,2)u"m/hr" + end + + @testset ">>> div, fld, cld" begin + @test div(Second(1), 2u"ms") == div(1u"s", Millisecond(2)) == div(1000, 2) + @test div(Second(11), 2u"s") == div(11u"s", Second(2)) == div(11, 2) + @test div(Second(-5), 2u"s") == div(-5u"s", Second(2)) == div(-5, 2) + @test_throws DimensionError div(Second(1), 1u"m") + @test_throws DimensionError div(1u"m", Second(1)) + + @test div(4u"minute", CompoundPeriod(Minute(1), Second(30))) == div(8, 3) + @test div(CompoundPeriod(Minute(4)), 90u"s") == div(8, 3) + @test_throws DimensionError div(4u"m", CompoundPeriod(Minute(1), Second(30))) + @test_throws DimensionError div(CompoundPeriod(Minute(4)), 90u"m") + + @static if VERSION ≥ v"1.4.0-DEV.208" + for r = (RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundToZero, RoundUp, RoundDown) + @test div(Second(11), 2u"s", r) == div(11u"s", Second(2), r) == div(11, 2, r) + @test div(Second(-5), 2u"s", r) == div(-5u"s", Second(2), r) == div(-5, 2, r) + @test_throws DimensionError div(Second(1), 1u"m", r) + @test_throws DimensionError div(1u"m", Second(1), r) + + @test div(4u"minute", CompoundPeriod(Minute(1), Second(30)), r) == div(8, 3, r) + @test div(CompoundPeriod(Minute(4)), 90u"s", r) == div(8, 3, r) + @test_throws DimensionError div(4u"m", CompoundPeriod(Minute(1), Second(30)), r) + @test_throws DimensionError div(CompoundPeriod(Minute(4)), 90u"m", r) + end end + + @test fld(Second(1), 2u"ms") == fld(1u"s", Millisecond(2)) == fld(1000, 2) + @test fld(Second(11), 2u"s") == fld(11u"s", Second(2)) == fld(11, 2) + @test fld(Second(-5), 2u"s") == fld(-5u"s", Second(2)) == fld(-5, 2) + @test_throws DimensionError fld(Second(1), 1u"m") + @test_throws DimensionError fld(1u"m", Second(1)) + + @test fld(4u"minute", CompoundPeriod(Minute(1), Second(30))) == fld(8, 3) + @test fld(CompoundPeriod(Minute(4)), 90u"s") == fld(8, 3) + @test_throws DimensionError fld(4u"m", CompoundPeriod(Minute(1), Second(30))) + @test_throws DimensionError fld(CompoundPeriod(Minute(4)), 90u"m") + + @test cld(Second(1), 2u"ms") == cld(1u"s", Millisecond(2)) == cld(1000, 2) + @test cld(Second(11), 2u"s") == cld(11u"s", Second(2)) == cld(11, 2) + @test cld(Second(-5), 2u"s") == cld(-5u"s", Second(2)) == cld(-5, 2) + @test_throws DimensionError cld(Second(1), 1u"m") + @test_throws DimensionError cld(1u"m", Second(1)) + + @test cld(4u"minute", CompoundPeriod(Minute(1), Second(30))) == cld(8, 3) + @test cld(CompoundPeriod(Minute(4)), 90u"s") == cld(8, 3) + @test_throws DimensionError cld(4u"m", CompoundPeriod(Minute(1), Second(30))) + @test_throws DimensionError cld(CompoundPeriod(Minute(4)), 90u"m") end - # fld - @test fld(Second(1), 2u"ms") == fld(1u"s", Millisecond(2)) == fld(1000, 2) - @test fld(Second(11), 2u"s") == fld(11u"s", Second(2)) == fld(11, 2) - @test fld(Second(-5), 2u"s") == fld(-5u"s", Second(2)) == fld(-5, 2) - @test_throws DimensionError fld(Second(1), 1u"m") - @test_throws DimensionError fld(1u"m", Second(1)) - # cld - @test cld(Second(1), 2u"ms") == cld(1u"s", Millisecond(2)) == cld(1000, 2) - @test cld(Second(11), 2u"s") == cld(11u"s", Second(2)) == cld(11, 2) - @test cld(Second(-5), 2u"s") == cld(-5u"s", Second(2)) == cld(-5, 2) - @test_throws DimensionError cld(Second(1), 1u"m") - @test_throws DimensionError cld(1u"m", Second(1)) - # mod - @test mod(Second(11), 3_000u"ms") == mod(11u"s", Millisecond(3_000)) == mod(11, 3)u"s" - @test mod(Second(11), -3u"s") == mod(11u"s", Second(-3)) == mod(11, -3)u"s" - @test_throws DimensionError mod(Second(1), 1u"m") - @test_throws DimensionError mod(1u"m", Second(1)) - # rem - @test rem(Second(11), 3_000u"ms") == rem(11u"s", Millisecond(3_000)) == rem(11, 3)u"s" - @test rem(Second(11), -3u"s") == rem(11u"s", Second(-3)) == rem(11, -3)u"s" - @test_throws DimensionError rem(Second(1), 1u"m") - @test_throws DimensionError rem(1u"m", Second(1)) - for r = (RoundToZero, RoundUp, RoundDown) - @test rem(Second(11), 2u"s", r) == rem(11u"s", Second(2), r) == rem(11, 2, r)u"s" - @test rem(Second(-5), 2u"s", r) == rem(-5u"s", Second(2), r) == rem(-5, 2, r)u"s" - @test_throws DimensionError rem(Second(1), 1u"m", r) - @test_throws DimensionError rem(1u"m", Second(1), r) + + @testset ">>> mod, rem" begin + @test mod(Second(11), 3_000u"ms") == mod(11u"s", Millisecond(3_000)) == mod(11, 3)u"s" + @test mod(Second(11), -3u"s") == mod(11u"s", Second(-3)) == mod(11, -3)u"s" + @test_throws DimensionError mod(Second(1), 1u"m") + @test_throws DimensionError mod(1u"m", Second(1)) + + @test mod(CompoundPeriod(Minute(4)), 90u"s") == mod(240, 90)u"s" + @test_throws MethodError mod(4u"minute", CompoundPeriod(Minute(1), Second(30))) + @test_throws MethodError mod(4u"m", CompoundPeriod(Minute(1), Second(30))) + @test_throws DimensionError mod(CompoundPeriod(Minute(4)), 90u"m") + + @test rem(Second(11), 3_000u"ms") == rem(11u"s", Millisecond(3_000)) == rem(11, 3)u"s" + @test rem(Second(11), -3u"s") == rem(11u"s", Second(-3)) == rem(11, -3)u"s" + @test_throws DimensionError rem(Second(1), 1u"m") + @test_throws DimensionError rem(1u"m", Second(1)) + + @test rem(CompoundPeriod(Minute(4)), 90u"s") == rem(240, 90)u"s" + @test_throws MethodError rem(4u"minute", CompoundPeriod(Minute(1), Second(30))) + @test_throws MethodError rem(4u"m", CompoundPeriod(Minute(1), Second(30))) + @test_throws DimensionError rem(CompoundPeriod(Minute(4)), 90u"m") + + for r = (RoundToZero, RoundUp, RoundDown) + @test rem(Second(11), 2u"s", r) == rem(11u"s", Second(2), r) == rem(11, 2, r)u"s" + @test rem(Second(-5), 2u"s", r) == rem(-5u"s", Second(2), r) == rem(-5, 2, r)u"s" + @test_throws DimensionError rem(Second(1), 1u"m", r) + @test_throws DimensionError rem(1u"m", Second(1), r) + end end end @@ -108,6 +151,13 @@ @test atan(1u"ms", Millisecond(5)) == atan(1,5) @test_throws DimensionError atan(Second(1), 1u"m") @test_throws DimensionError atan(1u"m", Second(1)) + + @test atan(CompoundPeriod(Minute(1), Second(30)), 10u"s") == atan(9,1) + @test atan(1u"yr", CompoundPeriod(Day(365), Hour(6))) == atan(1,1) + @test_throws DimensionError atan(1u"m", CompoundPeriod(Day(365), Hour(6))) + @test_throws DimensionError atan(CompoundPeriod(Day(365), Hour(6)), 1u"m") + @test_throws MethodError atan(1u"s", CompoundPeriod(Year(1))) + @test_throws MethodError atan(CompoundPeriod(Month(6)), 1u"s") end end @@ -119,6 +169,21 @@ @test uconvert(u"wk", Hour(1)) === u"wk"(Hour(1)) === Rational{Int64}(1,168)u"wk" @test_throws DimensionError uconvert(u"m", Second(1)) @test_throws DimensionError u"m"(Second(1)) + + @test uconvert(u"yr", CompoundPeriod()) === u"yr"(CompoundPeriod()) === Rational{Int64}(0,1)u"yr" + @test uconvert(u"μs", CompoundPeriod()) === u"μs"(CompoundPeriod()) === Rational{Int64}(0,1)u"μs" + @test uconvert(u"ns", CompoundPeriod()) === u"ns"(CompoundPeriod()) === Int64(0)u"ns" + @test uconvert(u"ps", CompoundPeriod()) === u"ps"(CompoundPeriod()) === Int64(0)u"ps" + @test uconvert(u"yr", CompoundPeriod(Day(365),Hour(6))) === u"yr"(CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1)u"yr" + @test uconvert(u"μs", CompoundPeriod(Day(365),Hour(6))) === u"μs"(CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(31_557_600_000_000,1)u"μs" + @test uconvert(u"ns", CompoundPeriod(Day(365),Hour(6))) === u"ns"(CompoundPeriod(Day(365),Hour(6))) === Int64(31_557_600_000_000_000)u"ns" + @test uconvert(u"ps", CompoundPeriod(Week(1),Hour(-1))) === u"ps"(CompoundPeriod(Week(1),Hour(-1))) === Int64(601_200_000_000_000_000)u"ps" + @test_throws DimensionError uconvert(u"m", CompoundPeriod(Day(365),Hour(6))) + @test_throws DimensionError u"m"(CompoundPeriod(Day(365),Hour(6))) + @test_throws MethodError uconvert(u"yr", CompoundPeriod(Year(1),Day(1))) + @test_throws MethodError u"yr"(CompoundPeriod(Year(1),Day(1))) + @test_throws MethodError uconvert(u"s", CompoundPeriod(Month(1),Day(1))) + @test_throws MethodError u"s"(CompoundPeriod(Month(1),Day(1))) end @testset ">> ustrip" begin @@ -134,46 +199,72 @@ @test_throws MethodError ustrip(Year(1)) @test_throws MethodError ustrip(u"s", Month(1)) @test_throws MethodError ustrip(u"yr", Year(1)) + + @test ustrip(u"yr", CompoundPeriod()) === Rational{Int64}(0,1) + @test ustrip(u"μs", CompoundPeriod()) === Rational{Int64}(0,1) + @test ustrip(u"ns", CompoundPeriod()) === Int64(0) + @test ustrip(u"ps", CompoundPeriod()) === Int64(0) + @test ustrip(u"yr", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1) + @test ustrip(u"μs", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(31_557_600_000_000,1) + @test ustrip(u"ns", CompoundPeriod(Day(365),Hour(6))) === Int64(31_557_600_000_000_000) + @test ustrip(u"ps", CompoundPeriod(Week(1),Hour(-1))) === Int64(601_200_000_000_000_000) + @test_throws DimensionError ustrip(u"m", CompoundPeriod(Day(365),Hour(6))) + @test_throws MethodError ustrip(CompoundPeriod()) + @test_throws MethodError ustrip(CompoundPeriod(Second(1))) + @test_throws MethodError ustrip(CompoundPeriod(Week(1), Hour(-1))) + @test_throws MethodError ustrip(u"yr", CompoundPeriod(Year(1))) + @test_throws MethodError ustrip(u"yr", CompoundPeriod(Month(1))) end - @testset ">> Constructors" begin + @testset ">> Constructors/convert" begin for (T,u) = ((Nanosecond, u"ns"), (Microsecond, u"μs"), (Millisecond, u"ms"), (Second, u"s"), (Minute, u"minute"), (Hour, u"hr"), (Day, u"d"), (Week, u"wk")) - @test Quantity(T(5)) === Int64(5)*u - @test Quantity{Float64,𝐓,typeof(u)}(T(5)) === 5.0u + @test Quantity(T(1)) === convert(Quantity, T(1)) === Int64(1)*u + @test Quantity{Float64,𝐓,typeof(u)}(T(2)) === convert(Quantity{Float64,𝐓,typeof(u)}, T(2)) === 2.0u + @test Quantity{Rational{Int64},𝐓,typeof(u)}(T(3)) === convert(Quantity{Rational{Int64},𝐓,typeof(u)}, T(3)) === Rational{Int64}(3,1)u end - @test Quantity{Float64,𝐓,typeof(u"d")}(Hour(6)) === 0.25u"d" + @test Quantity{Float64,𝐓,typeof(u"d")}(Hour(6)) === convert(typeof(1.0u"d"), Hour(6)) === 0.25u"d" @test_throws InexactError Quantity{Int64,𝐓,typeof(u"d")}(Hour(6)) + @test_throws InexactError convert(typeof(1u"d"), Hour(6)) @test_throws DimensionError Quantity{Float64,𝐋,typeof(u"m")}(Hour(6)) + @test_throws DimensionError convert(typeof(1.0u"m"), Week(1)) @test_throws MethodError Quantity{Float64,𝐓,typeof(u"d")}(Month(1)) @test_throws MethodError Quantity{Float64,𝐓,typeof(u"d")}(Year(1)) + @test_throws MethodError convert(typeof(1u"d"), Month(1)) + @test_throws MethodError convert(typeof(1u"d"), Year(1)) - @test Week(4u"wk") === Week(4) - @test Microsecond((3//2)u"ms") === Microsecond(1500) - @test Millisecond(1.0u"s") === Millisecond(1000) + @test Week(4u"wk") === convert(Week, 4u"wk") === Week(4) + @test Microsecond((3//2)u"ms") === convert(Microsecond, (3//2)u"ms") === Microsecond(1500) + @test Millisecond(1.0u"s") === convert(Millisecond, 1.0u"s") === Millisecond(1000) + @test Second(1.0u"s") === convert(Second, 1.0u"s") === Second(1) + @test Day(3u"wk") === convert(Day, 3u"wk") === Day(21) + @test_throws InexactError Second(1.5u"s") + @test_throws InexactError convert(Second, 1.5u"s") @test_throws InexactError Second(1u"ms") + @test_throws InexactError convert(Second, 1u"ms") @test_throws DimensionError Second(1u"m") + @test_throws DimensionError convert(Second, 1u"m") @test_throws DimensionError Month(1u"s") # Doesn't throw MethodError because Month(::Number) exists @test_throws DimensionError Year(1u"s") # Doesn't throw MethodError because Year(::Number) exists - end - - @testset ">> convert" begin - @test convert(typeof(1.0u"s"), Second(3)) === 3.0u"s" - @test convert(typeof((1//1)u"s"), Second(3)) === (3//1)u"s" - @test convert(typeof(1.0u"d"), Hour(6)) === 0.25u"d" - @test_throws InexactError convert(typeof(1u"d"), Hour(6)) - @test_throws DimensionError convert(typeof(1.0u"m"), Week(1)) - @test_throws MethodError convert(typeof(1u"d"), Month(1)) - @test_throws MethodError convert(typeof(1u"d"), Year(1)) - - @test convert(Second, 1.0u"s") === Second(1) - @test convert(Day, 3u"wk") === Day(21) - @test_throws DimensionError convert(Second, 1u"m") - @test_throws InexactError convert(Second, 1u"ms") - @test_throws InexactError convert(Second, 1.5u"s") @test_throws MethodError convert(Month, 1u"s") @test_throws MethodError convert(Year, 1u"s") + + for T = (Quantity{Rational{Int64},𝐓,typeof(u"yr")}, + Quantity{Float64,𝐓,typeof(u"s")}, + Quantity{Int64,𝐓,typeof(u"ns")}) + @test T(CompoundPeriod()) === convert(T, CompoundPeriod()) === T(0u"s") + @test T(CompoundPeriod(Day(365), Hour(6))) === convert(T, CompoundPeriod(Day(365), Hour(6))) === T(1u"yr") + @test T(CompoundPeriod(Week(1), Hour(-1))) === convert(T, CompoundPeriod(Week(1), Hour(-1))) === T(167u"hr") + @test_throws MethodError T(CompoundPeriod(Month(1))) + @test_throws MethodError T(CompoundPeriod(Year(1))) + @test_throws MethodError convert(T, CompoundPeriod(Month(1))) + @test_throws MethodError convert(T, CompoundPeriod(Year(1))) + end + @test_throws InexactError Quantity{Int64,𝐓,typeof(u"d")}(CompoundPeriod(Day(1),Hour(6))) + @test_throws InexactError convert(typeof(1u"d"), CompoundPeriod(Day(1),Hour(1))) + @test_throws DimensionError Quantity{Float64,𝐋,typeof(u"m")}(CompoundPeriod(Day(365), Hour(6))) + @test_throws DimensionError convert(typeof(1.0u"m"), CompoundPeriod(Day(365), Hour(6))) end end @@ -221,10 +312,14 @@ @test_throws DimensionError trunc(Second, 1u"m") @test_throws DimensionError ceil(Second, 1u"m") @test_throws DimensionError floor(Second, 1u"m") + @static if VERSION ≥ v"1.2.0" - @test round(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundNearest) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundNearest) === Rational{Int64}(-2,1)u"minute" - @test round(u"minute", Second(150)) === round(u"minute", Second(150), RoundNearest) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", Second(-50)) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", Second(-90)) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", Second(150)) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", Second(-50), RoundNearest) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", Second(-90), RoundNearest) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", Second(150), RoundNearest) === Rational{Int64}(2,1)u"minute" @test round(u"minute", Second(-50), RoundNearestTiesAway) === Rational{Int64}(-1,1)u"minute" @test round(u"minute", Second(-90), RoundNearestTiesAway) === Rational{Int64}(-2,1)u"minute" @test round(u"minute", Second(150), RoundNearestTiesAway) === Rational{Int64}(3,1)u"minute" @@ -236,48 +331,139 @@ @test trunc(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundToZero) === Rational{Int64}(0,1)u"minute" @test trunc(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundToZero) === Rational{Int64}(-1,1)u"minute" @test trunc(u"minute", Second(150)) === round(u"minute", Second(150), RoundToZero) === Rational{Int64}(2,1)u"minute" + @test ceil(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundUp) === Rational{Int64}(0,1)u"minute" @test ceil(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundUp) === Rational{Int64}(-1,1)u"minute" @test ceil(u"minute", Second(150)) === round(u"minute", Second(150), RoundUp) === Rational{Int64}(3,1)u"minute" + @test floor(u"minute", Second(-50)) === round(u"minute", Second(-50), RoundDown) === Rational{Int64}(-1,1)u"minute" @test floor(u"minute", Second(-90)) === round(u"minute", Second(-90), RoundDown) === Rational{Int64}(-2,1)u"minute" @test floor(u"minute", Second(150)) === round(u"minute", Second(150), RoundDown) === Rational{Int64}(2,1)u"minute" + end @test_throws DimensionError round(u"m", Second(1)) @test_throws DimensionError round(u"m", Second(1), RoundNearestTiesAway) @test_throws DimensionError trunc(u"m", Second(1)) @test_throws DimensionError ceil(u"m", Second(1)) @test_throws DimensionError floor(u"m", Second(1)) + + @static if VERSION ≥ v"1.2.0" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearest) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearest) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearest) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearestTiesAway) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearestTiesAway) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearestTiesAway) === Rational{Int64}(3,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearestTiesUp) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearestTiesUp) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearestTiesUp) === Rational{Int64}(3,1)u"minute" + end + @static if VERSION ≥ v"1.5.0-DEV.742" + @test trunc(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(0,1)u"minute" + @test trunc(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-1,1)u"minute" + @test trunc(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundToZero) === Rational{Int64}(0,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundToZero) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundToZero) === Rational{Int64}(2,1)u"minute" + @test ceil(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(0,1)u"minute" + @test ceil(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-1,1)u"minute" + @test ceil(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(3,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundUp) === Rational{Int64}(0,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundUp) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundUp) === Rational{Int64}(3,1)u"minute" + @test floor(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(-1,1)u"minute" + @test floor(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-2,1)u"minute" + @test floor(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundDown) === Rational{Int64}(-1,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundDown) === Rational{Int64}(-2,1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundDown) === Rational{Int64}(2,1)u"minute" + end + @test_throws MethodError round(u"s", CompoundPeriod(Year(1))) + @test_throws MethodError round(u"s", CompoundPeriod(Year(1)), RoundNearestTiesAway) + @test_throws MethodError trunc(u"s", CompoundPeriod(Year(1))) + @test_throws MethodError ceil(u"s", CompoundPeriod(Year(1))) + @test_throws MethodError floor(u"s", CompoundPeriod(Year(1))) + @test_throws MethodError round(u"s", CompoundPeriod(Month(1))) + @test_throws MethodError round(u"s", CompoundPeriod(Month(1)), RoundNearestTiesAway) + @test_throws MethodError trunc(u"s", CompoundPeriod(Month(1))) + @test_throws MethodError ceil(u"s", CompoundPeriod(Month(1))) + @test_throws MethodError floor(u"s", CompoundPeriod(Month(1))) + @test_throws DimensionError round(u"m", CompoundPeriod(Second(1))) + @test_throws DimensionError round(u"m", CompoundPeriod(Second(1)), RoundNearestTiesAway) + @test_throws DimensionError trunc(u"m", CompoundPeriod(Second(1))) + @test_throws DimensionError ceil(u"m", CompoundPeriod(Second(1))) + @test_throws DimensionError floor(u"m", CompoundPeriod(Second(1))) + @test round(u"wk", Day(10), digits=1) === 1.4u"wk" @test round(u"wk", Day(10), sigdigits=3) === 1.43u"wk" @test round(u"wk", Day(10), RoundUp, digits=1) === 1.5u"wk" - @test floor(u"wk", Day(10), sigdigits=3) === round(u"wk", Day(10), RoundDown, sigdigits=3) === 1.42u"wk" - @test ceil(u"wk", Day(10), digits=2, base=2) === round(u"wk", Day(10), RoundUp, digits=2, base=2) === 1.5u"wk" - @test trunc(u"wk", Day(10), digits=2, base=2) === round(u"wk", Day(10), RoundToZero, digits=2, base=2) === 1.25u"wk" + @test round(u"wk", Day(10), RoundDown, sigdigits=3) === 1.42u"wk" + @test round(u"wk", Day(10), RoundUp, digits=2, base=2) === 1.5u"wk" + @test round(u"wk", Day(10), RoundToZero, digits=2, base=2) === 1.25u"wk" + @test floor(u"wk", Day(10), sigdigits=3) === 1.42u"wk" + @test ceil(u"wk", Day(10), digits=2, base=2) === 1.5u"wk" + @test trunc(u"wk", Day(10), digits=2, base=2) === 1.25u"wk" @test_throws DimensionError round(u"m", Day(10), digits=1) @test_throws DimensionError round(u"m", Day(10), sigdigits=3, base=2) @test_throws DimensionError round(u"m", Day(10), RoundUp, digits=1) @test_throws DimensionError trunc(u"m", Day(10), digits=1) @test_throws DimensionError ceil(u"m", Day(10), sigdigits=3) @test_throws DimensionError floor(u"m", Day(10), digits=1, base=2) + + @test round(u"wk", CompoundPeriod(Week(1), Day(3)), digits=1) === 1.4u"wk" + @test round(u"wk", CompoundPeriod(Week(1), Day(3)), sigdigits=3) === 1.43u"wk" + @test round(u"wk", CompoundPeriod(Week(1), Day(3)), RoundUp, digits=1) === 1.5u"wk" + @test round(u"wk", CompoundPeriod(Week(1), Day(3)), RoundDown, sigdigits=3) === 1.42u"wk" + @test round(u"wk", CompoundPeriod(Week(1), Day(3)), RoundUp, digits=2, base=2) === 1.5u"wk" + @test round(u"wk", CompoundPeriod(Week(1), Day(3)), RoundToZero, digits=2, base=2) === 1.25u"wk" + @test floor(u"wk", CompoundPeriod(Week(1), Day(3)), sigdigits=3) === 1.42u"wk" + @test ceil(u"wk", CompoundPeriod(Week(1), Day(3)), digits=2, base=2) === 1.5u"wk" + @test trunc(u"wk", CompoundPeriod(Week(1), Day(3)), digits=2, base=2) === 1.25u"wk" + @test_throws MethodError round(u"wk", CompoundPeriod(Year(1)), digits=1) + @test_throws MethodError floor(u"wk", CompoundPeriod(Year(1)), sigdigits=3) + @test_throws MethodError ceil(u"wk", CompoundPeriod(Year(1)), digits=2, base=2) + @test_throws MethodError trunc(u"wk", CompoundPeriod(Year(1)), digits=2, base=2) + @test_throws MethodError round(u"wk", CompoundPeriod(Month(1)), digits=1) + @test_throws MethodError floor(u"wk", CompoundPeriod(Month(1)), sigdigits=3) + @test_throws MethodError ceil(u"wk", CompoundPeriod(Month(1)), digits=2, base=2) + @test_throws MethodError trunc(u"wk", CompoundPeriod(Month(1)), digits=2, base=2) + @test_throws DimensionError round(u"m", CompoundPeriod(Week(1), Day(3)), digits=1) + @test_throws DimensionError round(u"m", CompoundPeriod(Week(1), Day(3)), sigdigits=3, base=2) + @test_throws DimensionError round(u"m", CompoundPeriod(Week(1), Day(3)), RoundUp, digits=1) + @test_throws DimensionError trunc(u"m", CompoundPeriod(Week(1), Day(3)), digits=1) + @test_throws DimensionError ceil(u"m", CompoundPeriod(Week(1), Day(3)), sigdigits=3) + @test_throws DimensionError floor(u"m", CompoundPeriod(Week(1), Day(3)), digits=1, base=2) + @static if VERSION ≥ v"1.2.0" @test round(Int, u"minute", Second(-50)) === -1u"minute" @test round(Float32, u"minute", Second(-50)) === -1.0f0u"minute" end @static if VERSION ≥ v"1.5.0-DEV.742" - @test floor(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundDown) === 0u"minute" - @test ceil(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundUp) === 1u"minute" - @test trunc(Int, u"minute", Second(50)) === round(Int, u"minute", Second(50), RoundToZero) === 0u"minute" - @test floor(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundDown) === 0.0f0u"minute" - @test ceil(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundUp) === 1.0f0u"minute" - @test trunc(Float32, u"minute", Second(50)) === round(Float32, u"minute", Second(50), RoundToZero) === 0.0f0u"minute" + @test round(Int, u"minute", Second(50), RoundDown) === 0u"minute" + @test round(Int, u"minute", Second(50), RoundUp) === 1u"minute" + @test round(Int, u"minute", Second(50), RoundToZero) === 0u"minute" + @test floor(Int, u"minute", Second(50)) === 0u"minute" + @test ceil(Int, u"minute", Second(50)) === 1u"minute" + @test trunc(Int, u"minute", Second(50)) === 0u"minute" + @test round(Float32, u"minute", Second(50), RoundDown) === 0.0f0u"minute" + @test round(Float32, u"minute", Second(50), RoundUp) === 1.0f0u"minute" + @test round(Float32, u"minute", Second(50), RoundToZero) === 0.0f0u"minute" + @test floor(Float32, u"minute", Second(50)) === 0.0f0u"minute" + @test ceil(Float32, u"minute", Second(50)) === 1.0f0u"minute" + @test trunc(Float32, u"minute", Second(50)) === 0.0f0u"minute" end @test round(Float32, u"wk", Day(10), digits=1) === 1.4f0u"wk" @test round(Float32, u"wk", Day(10), sigdigits=3) === 1.43f0u"wk" @test round(Float32, u"wk", Day(10), RoundUp, digits=1) === 1.5f0u"wk" - @test floor(Float32, u"wk", Day(10), sigdigits=3) === round(Float32, u"wk", Day(10), RoundDown, sigdigits=3) === 1.42f0u"wk" - @test ceil(Float32, u"wk", Day(10), digits=2, base=2) === round(Float32, u"wk", Day(10), RoundUp, digits=2, base=2) === 1.5f0u"wk" - @test trunc(Float32, u"wk", Day(10), digits=2, base=2) === round(Float32, u"wk", Day(10), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test round(Float32, u"wk", Day(10), RoundDown, sigdigits=3) === 1.42f0u"wk" + @test round(Float32, u"wk", Day(10), RoundUp, digits=2, base=2) === 1.5f0u"wk" + @test round(Float32, u"wk", Day(10), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test floor(Float32, u"wk", Day(10), sigdigits=3) === 1.42f0u"wk" + @test ceil(Float32, u"wk", Day(10), digits=2, base=2) === 1.5f0u"wk" + @test trunc(Float32, u"wk", Day(10), digits=2, base=2) === 1.25f0u"wk" @test_throws DimensionError round(Float32, u"m", Second(-50)) @test_throws DimensionError round(Float32, u"m", Second(-50), RoundDown) @test_throws DimensionError round(Float32, u"m", Second(-50), digits=1) @@ -285,6 +471,56 @@ @test_throws DimensionError floor(Float32, u"m", Second(-50)) @test_throws DimensionError ceil(Float32, u"m", Second(-50), digits=1) @test_throws DimensionError trunc(Float32, u"m", Second(-50), sigdigits=3, base=2) + + @static if VERSION ≥ v"1.2.0" + @test round(Int, u"minute", CompoundPeriod(Minute(-1), Second(10))) === -1u"minute" + @test round(Float32, u"minute", CompoundPeriod(Minute(-1), Second(10))) === -1.0f0u"minute" + end + @static if VERSION ≥ v"1.5.0-DEV.742" + @test round(Int, u"minute", CompoundPeriod(Minute(1), Second(-10)), RoundDown) === 0u"minute" + @test round(Int, u"minute", CompoundPeriod(Minute(1), Second(-10)), RoundUp) === 1u"minute" + @test round(Int, u"minute", CompoundPeriod(Minute(1), Second(-10)), RoundToZero) === 0u"minute" + @test floor(Int, u"minute", CompoundPeriod(Minute(1), Second(-10))) === 0u"minute" + @test ceil(Int, u"minute", CompoundPeriod(Minute(1), Second(-10))) === 1u"minute" + @test trunc(Int, u"minute", CompoundPeriod(Minute(1), Second(-10))) === 0u"minute" + @test round(Float32, u"minute", CompoundPeriod(Minute(1), Second(-10)), RoundDown) === 0.0f0u"minute" + @test round(Float32, u"minute", CompoundPeriod(Minute(1), Second(-10)), RoundUp) === 1.0f0u"minute" + @test round(Float32, u"minute", CompoundPeriod(Minute(1), Second(-10)), RoundToZero) === 0.0f0u"minute" + @test floor(Float32, u"minute", CompoundPeriod(Minute(1), Second(-10))) === 0.0f0u"minute" + @test ceil(Float32, u"minute", CompoundPeriod(Minute(1), Second(-10))) === 1.0f0u"minute" + @test trunc(Float32, u"minute", CompoundPeriod(Minute(1), Second(-10))) === 0.0f0u"minute" + end + @test round(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), digits=1) === 1.4f0u"wk" + @test round(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), sigdigits=3) === 1.43f0u"wk" + @test round(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), RoundUp, digits=1) === 1.5f0u"wk" + @test round(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), RoundDown, sigdigits=3) === 1.42f0u"wk" + @test round(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), RoundUp, digits=2, base=2) === 1.5f0u"wk" + @test round(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test floor(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), sigdigits=3) === 1.42f0u"wk" + @test ceil(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), digits=2, base=2) === 1.5f0u"wk" + @test trunc(Float32, u"wk", CompoundPeriod(Week(1), Day(3)), digits=2, base=2) === 1.25f0u"wk" + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Year(1))) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Year(1)), RoundDown) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Year(1)), digits=1) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Year(1)), RoundDown, sigdigits=3) + @test_throws MethodError floor(Float32, u"yr", CompoundPeriod(Year(1))) + @test_throws MethodError ceil(Float32, u"yr", CompoundPeriod(Year(1)), digits=1) + @test_throws MethodError trunc(Float32, u"yr", CompoundPeriod(Year(1)), sigdigits=3, base=2) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Month(1))) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Month(1)), RoundDown) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Month(1)), digits=1) + @test_throws MethodError round(Float32, u"yr", CompoundPeriod(Month(1)), RoundDown, sigdigits=3) + @test_throws MethodError floor(Float32, u"yr", CompoundPeriod(Month(1))) + @test_throws MethodError ceil(Float32, u"yr", CompoundPeriod(Month(1)), digits=1) + @test_throws MethodError trunc(Float32, u"yr", CompoundPeriod(Month(1)), sigdigits=3, base=2) + @test_throws DimensionError round(Float32, u"m", CompoundPeriod(Minute(-1), Second(10))) + @test_throws DimensionError round(Float32, u"m", CompoundPeriod(Minute(-1), Second(10)), RoundDown) + @test_throws DimensionError round(Float32, u"m", CompoundPeriod(Minute(-1), Second(10)), digits=1) + @test_throws DimensionError round(Float32, u"m", CompoundPeriod(Minute(-1), Second(10)), RoundDown, sigdigits=3) + @test_throws DimensionError floor(Float32, u"m", CompoundPeriod(Minute(-1), Second(10))) + @test_throws DimensionError ceil(Float32, u"m", CompoundPeriod(Minute(-1), Second(10)), digits=1) + @test_throws DimensionError trunc(Float32, u"m", CompoundPeriod(Minute(-1), Second(10)), sigdigits=3, base=2) + @static if VERSION ≥ v"1.2.0" @test round(typeof(1.0f0u"minute"), Second(-50)) === -1.0f0u"minute" end @@ -294,9 +530,12 @@ @test round(typeof(1.0f0u"wk"), Day(10), digits=1) === 1.4f0u"wk" @test round(typeof(1.0f0u"wk"), Day(10), sigdigits=3) === 1.43f0u"wk" @test round(typeof(1.0f0u"wk"), Day(10), RoundUp, digits=1) === 1.5f0u"wk" - @test floor(typeof(1.0f0u"wk"), Day(10), sigdigits=3) === round(typeof(1.0f0u"wk"), Day(10), RoundDown, sigdigits=3) === 1.42f0u"wk" - @test ceil(typeof(1.0f0u"wk"), Day(10), digits=2, base=2) === round(typeof(1.0f0u"wk"), Day(10), RoundUp, digits=2, base=2) === 1.5f0u"wk" - @test trunc(typeof(1.0f0u"wk"), Day(10), digits=2, base=2) === round(typeof(1.0f0u"wk"), Day(10), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test round(typeof(1.0f0u"wk"), Day(10), RoundDown, sigdigits=3) === 1.42f0u"wk" + @test round(typeof(1.0f0u"wk"), Day(10), RoundUp, digits=2, base=2) === 1.5f0u"wk" + @test round(typeof(1.0f0u"wk"), Day(10), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test floor(typeof(1.0f0u"wk"), Day(10), sigdigits=3) === 1.42f0u"wk" + @test ceil(typeof(1.0f0u"wk"), Day(10), digits=2, base=2) === 1.5f0u"wk" + @test trunc(typeof(1.0f0u"wk"), Day(10), digits=2, base=2) === 1.25f0u"wk" @test_throws DimensionError round(typeof(1.0u"m"), Second(1)) @test_throws DimensionError round(typeof(1.0u"m"), Second(1), RoundToZero) @test_throws DimensionError round(typeof(1.0u"m"), Second(1), digits=1) @@ -304,6 +543,43 @@ @test_throws DimensionError floor(typeof(1.0u"m"), Second(1)) @test_throws DimensionError ceil(typeof(1.0u"m"), Second(1), sigdigits=2, base=2) @test_throws DimensionError trunc(typeof(1.0u"m"), Second(1), digits=1) + + @static if VERSION ≥ v"1.2.0" + @test round(typeof(1.0f0u"minute"), CompoundPeriod(Minute(-1), Second(10))) === -1.0f0u"minute" + end + @static if VERSION ≥ v"1.5.0-DEV.742" + @test round(typeof(1.0f0u"minute"), CompoundPeriod(Minute(1), Second(-10)), RoundToZero) === 0.0f0u"minute" + end + @test round(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), digits=1) === 1.4f0u"wk" + @test round(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), sigdigits=3) === 1.43f0u"wk" + @test round(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), RoundUp, digits=1) === 1.5f0u"wk" + @test round(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), RoundDown, sigdigits=3) === 1.42f0u"wk" + @test round(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), RoundUp, digits=2, base=2) === 1.5f0u"wk" + @test round(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), RoundToZero, digits=2, base=2) === 1.25f0u"wk" + @test floor(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), sigdigits=3) === 1.42f0u"wk" + @test ceil(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), digits=2, base=2) === 1.5f0u"wk" + @test trunc(typeof(1.0f0u"wk"), CompoundPeriod(Week(1), Day(3)), digits=2, base=2) === 1.25f0u"wk" + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Year(1))) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Year(1)), RoundToZero) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Year(1)), digits=1) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Year(1)), RoundToZero, sigdigits=2) + @test_throws MethodError floor(typeof(1.0u"s"), CompoundPeriod(Year(1))) + @test_throws MethodError ceil(typeof(1.0u"s"), CompoundPeriod(Year(1)), sigdigits=2, base=2) + @test_throws MethodError trunc(typeof(1.0u"s"), CompoundPeriod(Year(1)), digits=1) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Month(1))) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Month(1)), RoundToZero) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Month(1)), digits=1) + @test_throws MethodError round(typeof(1.0u"s"), CompoundPeriod(Month(1)), RoundToZero, sigdigits=2) + @test_throws MethodError floor(typeof(1.0u"s"), CompoundPeriod(Month(1))) + @test_throws MethodError ceil(typeof(1.0u"s"), CompoundPeriod(Month(1)), sigdigits=2, base=2) + @test_throws MethodError trunc(typeof(1.0u"s"), CompoundPeriod(Month(1)), digits=1) + @test_throws DimensionError round(typeof(1.0u"m"), CompoundPeriod(Second(1))) + @test_throws DimensionError round(typeof(1.0u"m"), CompoundPeriod(Second(1)), RoundToZero) + @test_throws DimensionError round(typeof(1.0u"m"), CompoundPeriod(Second(1)), digits=1) + @test_throws DimensionError round(typeof(1.0u"m"), CompoundPeriod(Second(1)), RoundToZero, sigdigits=2) + @test_throws DimensionError floor(typeof(1.0u"m"), CompoundPeriod(Second(1))) + @test_throws DimensionError ceil(typeof(1.0u"m"), CompoundPeriod(Second(1)), sigdigits=2, base=2) + @test_throws DimensionError trunc(typeof(1.0u"m"), CompoundPeriod(Second(1)), digits=1) end @testset "> Comparison" begin @@ -313,12 +589,34 @@ @test Millisecond(0) == -0.0u"ms" @test !(Day(4) == 4u"hr") @test !(4u"cm" == Day(4)) + + @test CompoundPeriod(Day(365), Hour(6)) == 1u"yr" + @test 1u"yr" == CompoundPeriod(Day(365), Hour(6)) + @test CompoundPeriod() == -0.0u"s" + @test !(1u"m" == CompoundPeriod(Day(1))) + @test !(CompoundPeriod(Day(1)) == 1u"m") + @test !(1u"yr" == CompoundPeriod(Year(1))) + @test !(1u"yr" == CompoundPeriod(Month(12))) + @test !(CompoundPeriod(Year(1)) == 1u"yr") + @test !(CompoundPeriod(Month(12)) == 1u"yr") + # isequal @test isequal(Second(2), 2.0u"s") @test isequal(72u"hr", Day(3)) @test !isequal(Millisecond(0), -0.0u"ms") # !isequal(0.0, -0.0) @test !isequal(Day(4), 4u"hr") @test !isequal(4u"cm", Day(4)) + + @test isequal(CompoundPeriod(Day(365), Hour(6)), 1u"yr") + @test isequal(1u"yr", CompoundPeriod(Day(365), Hour(6))) + @test !isequal(CompoundPeriod(), -0.0u"s") # !isequal(0.0, -0.0) + @test !isequal(1u"m", CompoundPeriod(Day(1))) + @test !isequal(CompoundPeriod(Day(1)), 1u"m") + @test !isequal(1u"yr", CompoundPeriod(Year(1))) + @test !isequal(1u"yr", CompoundPeriod(Month(12))) + @test !isequal(CompoundPeriod(Year(1)), 1u"yr") + @test !isequal(CompoundPeriod(Month(12)), 1u"yr") + # < @test Second(1) < 1001u"ms" @test 3u"minute" < Minute(4) @@ -327,6 +625,19 @@ @test !(-0.0u"d" < Day(0)) @test_throws DimensionError 7u"kg" < Day(1) @test_throws DimensionError Day(1) < 7u"kg" + + @test CompoundPeriod(Day(365)) < 1u"yr" + @test 1u"s" < CompoundPeriod(Second(1), Nanosecond(1)) + @test !(CompoundPeriod(Day(365), Hour(6)) < 1u"yr") + @test !(1u"s" < CompoundPeriod(Second(1))) + @test !(-0.0u"s" < CompoundPeriod()) + @test_throws DimensionError 7u"kg" < CompoundPeriod(Day(1)) + @test_throws DimensionError CompoundPeriod() < 1u"m" + @test_throws MethodError 1u"s" < CompoundPeriod(Year(1)) + @test_throws MethodError 1u"s" < CompoundPeriod(Month(1)) + @test_throws MethodError CompoundPeriod(Year(1)) < 2u"yr" + @test_throws MethodError CompoundPeriod(Month(1)) < 1u"yr" + # isless @test isless(Second(1), 1001u"ms") @test isless(3u"minute", Minute(4)) @@ -335,22 +646,58 @@ @test isless(-0.0u"d", Day(0)) @test_throws DimensionError isless(7u"kg", Day(1)) @test_throws DimensionError isless(Day(1), 7u"kg") + + @test isless(CompoundPeriod(Day(365)), 1u"yr") + @test isless(1u"s", CompoundPeriod(Second(1), Nanosecond(1))) + @test !isless(CompoundPeriod(Day(365), Hour(6)), 1u"yr") + @test !isless(1u"s", CompoundPeriod(Second(1))) + @test isless(-0.0u"s", CompoundPeriod()) + @test_throws DimensionError isless(7u"kg", CompoundPeriod(Day(1))) + @test_throws DimensionError isless(CompoundPeriod(), 1u"m") + @test_throws MethodError isless(1u"s", CompoundPeriod(Year(1))) + @test_throws MethodError isless(1u"s", CompoundPeriod(Month(1))) + @test_throws MethodError isless(CompoundPeriod(Year(1)), 2u"yr") + @test_throws MethodError isless(CompoundPeriod(Month(1)), 1u"yr") + # ≤ @test Second(1) ≤ 1001u"ms" @test 7u"d" ≤ Week(1) @test !(Minute(4) ≤ 3u"minute") @test_throws DimensionError 7u"kg" ≤ Day(1) @test_throws DimensionError Day(1) ≤ 7u"kg" - # min + + @test CompoundPeriod(Day(365), Hour(6)) ≤ 1u"yr" + @test 1u"s" ≤ CompoundPeriod(Second(1), Nanosecond(1)) + @test !(1u"s" ≤ CompoundPeriod(Millisecond(999))) + @test_throws DimensionError 7u"kg" ≤ CompoundPeriod(Day(1)) + @test_throws DimensionError CompoundPeriod() ≤ 1u"m" + @test_throws MethodError 1u"s" ≤ CompoundPeriod(Year(1)) + @test_throws MethodError 1u"s" ≤ CompoundPeriod(Month(1)) + @test_throws MethodError CompoundPeriod(Year(1)) ≤ 2u"yr" + @test_throws MethodError CompoundPeriod(Month(1)) ≤ 1u"yr" + + # min, max @test min(1u"s", Microsecond(100)) == Microsecond(100) @test min(Day(1), 1u"hr") == 1u"hr" @test_throws DimensionError min(1u"kg", Second(1)) @test_throws DimensionError min(Second(1), 1u"kg") - # max @test max(1u"s", Microsecond(100)) == 1u"s" @test max(Day(1), 1u"hr") == Day(1) @test_throws DimensionError max(1u"kg", Second(1)) @test_throws DimensionError max(Second(1), 1u"kg") + + @test min(1u"s", CompoundPeriod()) == CompoundPeriod() + @test min(1u"yr", CompoundPeriod(Day(365), Hour(7))) == 1u"yr" + @test_throws DimensionError min(CompoundPeriod(), 1u"m") + @test_throws DimensionError min(1u"m", CompoundPeriod()) + @test_throws MethodError min(1u"yr", CompoundPeriod(Year(1))) + @test_throws MethodError min(CompoundPeriod(Month(1)), 1u"yr") + @test max(1u"s", CompoundPeriod()) == 1u"s" + @test max(1u"yr", CompoundPeriod(Day(365), Hour(7))) == CompoundPeriod(Day(365), Hour(7)) + @test_throws DimensionError max(CompoundPeriod(), 1u"m") + @test_throws DimensionError max(1u"m", CompoundPeriod()) + @test_throws MethodError max(1u"yr", CompoundPeriod(Year(1))) + @test_throws MethodError max(CompoundPeriod(Month(1)), 1u"yr") end @testset "> isapprox" begin @@ -368,11 +715,39 @@ @test !isapprox(1u"m", Second(1), atol=Second(1)) @test_throws DimensionError isapprox(Second(2), 2500u"ms", atol=0.5) @test_throws DimensionError isapprox(Second(2), 2500u"ms", atol=0.5u"m") + + @test isapprox(nextfloat(1.0)u"yr", CompoundPeriod(Day(365), Hour(6))) + @test isapprox(1.0u"yr", CompoundPeriod(Day(365), Hour(6)), rtol=0) + @test isapprox(2u"s", CompoundPeriod(Second(2), Millisecond(500)), atol=1u"s") + @test isapprox(CompoundPeriod(Second(2), Millisecond(500)), 2u"s", atol=CompoundPeriod(Second(1))) + @test isapprox(CompoundPeriod(Second(2), Millisecond(500)), 2u"s", rtol=0.5) + @test !isapprox(CompoundPeriod(Second(2), Millisecond(500)), 2u"s", rtol=0.1) + @test !isapprox(CompoundPeriod(Week(1), Nanosecond(1)), 1.0u"wk", rtol=0) + @test !isapprox(CompoundPeriod(Second(1)), 1u"m") + @test !isapprox(1u"m", CompoundPeriod(Second(1))) + @test !isapprox(CompoundPeriod(Second(1)), 1u"m", atol=1u"kg") + @test !isapprox(1u"m", CompoundPeriod(Second(1)), atol=CompoundPeriod(Second(1))) + @test_throws MethodError isapprox(CompoundPeriod(Year(1)), 1u"s") + @test_throws MethodError isapprox(1u"s", CompoundPeriod(Year(1)), rtol=1) + @test_throws MethodError isapprox(1u"s", 1u"s", atol=CompoundPeriod(Year(1))) + @test_throws MethodError isapprox(CompoundPeriod(Month(1)), 1u"s") + @test_throws MethodError isapprox(1u"s", CompoundPeriod(Month(1)), rtol=1) + @test_throws MethodError isapprox(1u"s", 1u"s", atol=CompoundPeriod(Month(1))) + @test_throws DimensionError isapprox(CompoundPeriod(Day(1)), 1u"d", atol=0.1) + @test_throws DimensionError isapprox(1u"d", CompoundPeriod(Day(1)), atol=0.1u"m") + # array arguments @test isapprox([Second(-5), Second(5)], [-5.0u"s", 5.0u"s"]) @test isapprox([Second(-5), Second(5)], [-4.99u"s", 5.01u"s"], rtol=1e-2) @test_broken isapprox([1u"s", 60u"s"], Period[Second(1), Minute(1)], rtol=0) @test !isapprox([1.0u"kg"], [Second(1)]) + + @test isapprox([CompoundPeriod(Day(2), Hour(12)), CompoundPeriod(Day(-3), Hour(12))], [2.5u"d", -2.5u"d"]) + @test isapprox([2.51u"d", -2.49u"d"], [CompoundPeriod(Day(2), Hour(12)), CompoundPeriod(Day(-3), Hour(12))], rtol=1e-2) + @test isapprox([1u"s", 60u"s"], CompoundPeriod[Second(1), Minute(1)], rtol=0) + @test !isapprox([CompoundPeriod(Day(1))], [1.0u"kg"]) + @test_throws MethodError isapprox([CompoundPeriod(Year(1))], [1.0u"yr"]) + @test_throws MethodError isapprox([1.0u"yr"], [CompoundPeriod(Month(12))], rtol=1) end @testset "> promote" begin diff --git a/test/runtests.jl b/test/runtests.jl index 1f6207e5..e093ca8e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -36,7 +36,8 @@ import Unitful: LengthUnits, AreaUnits, MassUnits, TemperatureUnits using Dates: Dates, Nanosecond, Microsecond, Millisecond, Second, Minute, Hour, Day, Week, - Month, Year + Month, Year, + CompoundPeriod const colon = Base.:(:) From 7a9b3a1a17ca8cc9e4fe05d97498ca0aa4f6be7e Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Wed, 20 May 2020 12:44:53 +0200 Subject: [PATCH 09/13] Fix tests on 32bit --- test/dates.jl | 103 +++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 38 deletions(-) diff --git a/test/dates.jl b/test/dates.jl index d4fec1bd..d7ea2c28 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -86,7 +86,11 @@ @test_throws DimensionError div(Second(1), 1u"m", r) @test_throws DimensionError div(1u"m", Second(1), r) - @test div(4u"minute", CompoundPeriod(Minute(1), Second(30)), r) == div(8, 3, r) + if Sys.WORD_SIZE == 32 && r in (RoundNearestTiesAway, RoundNearestTiesUp) + @test_broken div(4u"minute", CompoundPeriod(Minute(1), Second(30)), r) == div(8, 3, r) + else + @test div(4u"minute", CompoundPeriod(Minute(1), Second(30)), r) == div(8, 3, r) + end @test div(CompoundPeriod(Minute(4)), 90u"s", r) == div(8, 3, r) @test_throws DimensionError div(4u"m", CompoundPeriod(Minute(1), Second(30)), r) @test_throws DimensionError div(CompoundPeriod(Minute(4)), 90u"m", r) @@ -170,14 +174,27 @@ @test_throws DimensionError uconvert(u"m", Second(1)) @test_throws DimensionError u"m"(Second(1)) - @test uconvert(u"yr", CompoundPeriod()) === u"yr"(CompoundPeriod()) === Rational{Int64}(0,1)u"yr" + @static if Sys.WORD_SIZE == 32 + @test uconvert(u"yr", CompoundPeriod()) === u"yr"(CompoundPeriod()) === 0.0u"yr" + else + @test uconvert(u"yr", CompoundPeriod()) === u"yr"(CompoundPeriod()) === Rational{Int64}(0,1)u"yr" + end @test uconvert(u"μs", CompoundPeriod()) === u"μs"(CompoundPeriod()) === Rational{Int64}(0,1)u"μs" @test uconvert(u"ns", CompoundPeriod()) === u"ns"(CompoundPeriod()) === Int64(0)u"ns" @test uconvert(u"ps", CompoundPeriod()) === u"ps"(CompoundPeriod()) === Int64(0)u"ps" - @test uconvert(u"yr", CompoundPeriod(Day(365),Hour(6))) === u"yr"(CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1)u"yr" - @test uconvert(u"μs", CompoundPeriod(Day(365),Hour(6))) === u"μs"(CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(31_557_600_000_000,1)u"μs" - @test uconvert(u"ns", CompoundPeriod(Day(365),Hour(6))) === u"ns"(CompoundPeriod(Day(365),Hour(6))) === Int64(31_557_600_000_000_000)u"ns" - @test uconvert(u"ps", CompoundPeriod(Week(1),Hour(-1))) === u"ps"(CompoundPeriod(Week(1),Hour(-1))) === Int64(601_200_000_000_000_000)u"ps" + @static if Sys.WORD_SIZE == 32 + @test uconvert(u"yr", CompoundPeriod(Day(365),Hour(6))) === 1.0u"yr" + @test u"yr"(CompoundPeriod(Day(365),Hour(6))) === 1.0u"yr" + else + @test uconvert(u"yr", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1)u"yr" + @test u"yr"(CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1)u"yr" + end + @test uconvert(u"μs", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(31_557_600_000_000,1)u"μs" + @test u"μs"(CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(31_557_600_000_000,1)u"μs" + @test uconvert(u"ns", CompoundPeriod(Day(365),Hour(6))) === Int64(31_557_600_000_000_000)u"ns" + @test u"ns"(CompoundPeriod(Day(365),Hour(6))) === Int64(31_557_600_000_000_000)u"ns" + @test uconvert(u"ps", CompoundPeriod(Week(1),Hour(-1))) === Int64(601_200_000_000_000_000)u"ps" + @test u"ps"(CompoundPeriod(Week(1),Hour(-1))) === Int64(601_200_000_000_000_000)u"ps" @test_throws DimensionError uconvert(u"m", CompoundPeriod(Day(365),Hour(6))) @test_throws DimensionError u"m"(CompoundPeriod(Day(365),Hour(6))) @test_throws MethodError uconvert(u"yr", CompoundPeriod(Year(1),Day(1))) @@ -200,11 +217,19 @@ @test_throws MethodError ustrip(u"s", Month(1)) @test_throws MethodError ustrip(u"yr", Year(1)) - @test ustrip(u"yr", CompoundPeriod()) === Rational{Int64}(0,1) + @static if Sys.WORD_SIZE == 32 + @test ustrip(u"yr", CompoundPeriod()) === 0.0 + else + @test ustrip(u"yr", CompoundPeriod()) === Rational{Int64}(0,1) + end @test ustrip(u"μs", CompoundPeriod()) === Rational{Int64}(0,1) @test ustrip(u"ns", CompoundPeriod()) === Int64(0) @test ustrip(u"ps", CompoundPeriod()) === Int64(0) - @test ustrip(u"yr", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1) + @static if Sys.WORD_SIZE == 32 + @test ustrip(u"yr", CompoundPeriod(Day(365),Hour(6))) === 1.0 + else + @test ustrip(u"yr", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(1,1) + end @test ustrip(u"μs", CompoundPeriod(Day(365),Hour(6))) === Rational{Int64}(31_557_600_000_000,1) @test ustrip(u"ns", CompoundPeriod(Day(365),Hour(6))) === Int64(31_557_600_000_000_000) @test ustrip(u"ps", CompoundPeriod(Week(1),Hour(-1))) === Int64(601_200_000_000_000_000) @@ -348,38 +373,40 @@ @test_throws DimensionError floor(u"m", Second(1)) @static if VERSION ≥ v"1.2.0" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearest) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearest) === Rational{Int64}(-2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearest) === Rational{Int64}(2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearestTiesAway) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearestTiesAway) === Rational{Int64}(-2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearestTiesAway) === Rational{Int64}(3,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearestTiesUp) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearestTiesUp) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearestTiesUp) === Rational{Int64}(3,1)u"minute" + T = @static Sys.WORD_SIZE == 32 ? Float64 : Rational{Int64} + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10))) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30))) === T(-2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30))) === T(2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearest) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearest) === T(-2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearest) === T(2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearestTiesAway) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearestTiesAway) === T(-2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearestTiesAway) === T(3)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundNearestTiesUp) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundNearestTiesUp) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundNearestTiesUp) === T(3)u"minute" end @static if VERSION ≥ v"1.5.0-DEV.742" - @test trunc(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(0,1)u"minute" - @test trunc(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-1,1)u"minute" - @test trunc(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundToZero) === Rational{Int64}(0,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundToZero) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundToZero) === Rational{Int64}(2,1)u"minute" - @test ceil(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(0,1)u"minute" - @test ceil(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-1,1)u"minute" - @test ceil(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(3,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundUp) === Rational{Int64}(0,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundUp) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundUp) === Rational{Int64}(3,1)u"minute" - @test floor(u"minute", CompoundPeriod(Minute(-1), Second(10))) === Rational{Int64}(-1,1)u"minute" - @test floor(u"minute", CompoundPeriod(Minute(-2), Second(30))) === Rational{Int64}(-2,1)u"minute" - @test floor(u"minute", CompoundPeriod(Minute(3), Second(-30))) === Rational{Int64}(2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundDown) === Rational{Int64}(-1,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundDown) === Rational{Int64}(-2,1)u"minute" - @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundDown) === Rational{Int64}(2,1)u"minute" + T = @static Sys.WORD_SIZE == 32 ? Float64 : Rational{Int64} + @test trunc(u"minute", CompoundPeriod(Minute(-1), Second(10))) === -T(0)u"minute" + @test trunc(u"minute", CompoundPeriod(Minute(-2), Second(30))) === T(-1)u"minute" + @test trunc(u"minute", CompoundPeriod(Minute(3), Second(-30))) === T(2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundToZero) === -T(0)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundToZero) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundToZero) === T(2)u"minute" + @test ceil(u"minute", CompoundPeriod(Minute(-1), Second(10))) === -T(0)u"minute" + @test ceil(u"minute", CompoundPeriod(Minute(-2), Second(30))) === T(-1)u"minute" + @test ceil(u"minute", CompoundPeriod(Minute(3), Second(-30))) === T(3)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundUp) === -T(0)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundUp) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundUp) === T(3)u"minute" + @test floor(u"minute", CompoundPeriod(Minute(-1), Second(10))) === T(-1)u"minute" + @test floor(u"minute", CompoundPeriod(Minute(-2), Second(30))) === T(-2)u"minute" + @test floor(u"minute", CompoundPeriod(Minute(3), Second(-30))) === T(2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-1), Second(10)), RoundDown) === T(-1)u"minute" + @test round(u"minute", CompoundPeriod(Minute(-2), Second(30)), RoundDown) === T(-2)u"minute" + @test round(u"minute", CompoundPeriod(Minute(3), Second(-30)), RoundDown) === T(2)u"minute" end @test_throws MethodError round(u"s", CompoundPeriod(Year(1))) @test_throws MethodError round(u"s", CompoundPeriod(Year(1)), RoundNearestTiesAway) From c00045a10de7cd3a00fd05d0a54f5fc5fe051ff1 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 18 Jun 2020 17:44:26 +0200 Subject: [PATCH 10/13] Add Quantity(::FixedPeriod) docstring --- src/dates.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/dates.jl b/src/dates.jl index 07fb40a8..c1738098 100644 --- a/src/dates.jl +++ b/src/dates.jl @@ -38,6 +38,21 @@ quantitytype(::Type{T}) where {T<:Dates.FixedPeriod} = ustrip(p::Dates.FixedPeriod) = Dates.value(p) +""" + Quantity(period::Dates.FixedPeriod) + +Create a `Quantity` that corresponds to the given `period`. The numerical value of the +resulting `Quantity` is of type `Int64`. + +# Example + +```jldoctest +julia> using Dates: Second + +julia> Quantity(Second(5)) +5 s +``` +""" Quantity(period::Dates.FixedPeriod) = Quantity(ustrip(period), unit(period)) uconvert(u::Units, period::Dates.FixedPeriod) = uconvert(u, Quantity(period)) From fb90227b10c93e672a557e1fb533f3c6d96c42f5 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 18 Jun 2020 20:44:30 +0200 Subject: [PATCH 11/13] Fix some tests --- test/dates.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/dates.jl b/test/dates.jl index d7ea2c28..91bcb681 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -44,9 +44,9 @@ @test_throws AffineError Second(1) * 1u"°C" @test_throws AffineError 1u"°C" * Second(1) # Multiplication with unit - Week(5) * u"Hz" === 5.0u"wk*Hz" - u"mm" * Millisecond(20) === Int64(20)u"mm*ms" - u"ms^-1" * Millisecond(20) === Int64(20) + @test Week(5) * u"Hz" === 5.0u"wk*Hz" + @test u"mm" * Millisecond(20) === Int64(20)u"mm*ms" + @test u"ms^-1" * Millisecond(20) === Int64(20) @test_throws AffineError Second(1) * u"°C" @test_throws AffineError u"°C" * Second(1) # Multiple factors From 4ddde0e18b0abb5fa689e10df46906e61dcde429 Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Thu, 18 Jun 2020 20:57:56 +0200 Subject: [PATCH 12/13] Fix a test --- test/dates.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dates.jl b/test/dates.jl index 91bcb681..fd4be784 100644 --- a/test/dates.jl +++ b/test/dates.jl @@ -44,7 +44,7 @@ @test_throws AffineError Second(1) * 1u"°C" @test_throws AffineError 1u"°C" * Second(1) # Multiplication with unit - @test Week(5) * u"Hz" === 5.0u"wk*Hz" + @test Week(5) * u"Hz" === Int64(5)u"wk*Hz" @test u"mm" * Millisecond(20) === Int64(20)u"mm*ms" @test u"ms^-1" * Millisecond(20) === Int64(20) @test_throws AffineError Second(1) * u"°C" From 2bd60160adaff922d1e89314b1ff207e28e3371e Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Fri, 3 Jul 2020 20:39:13 +0200 Subject: [PATCH 13/13] Add documentation page --- docs/Project.toml | 1 + docs/make.jl | 3 +- docs/src/dates.md | 133 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 docs/src/dates.md diff --git a/docs/Project.toml b/docs/Project.toml index 7eb4fd3c..6128171d 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" diff --git a/docs/make.jl b/docs/make.jl index e6a477ec..9bba2d55 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,4 +1,4 @@ -using Documenter, Unitful +using Documenter, Unitful, Dates DocMeta.setdocmeta!(Unitful, :DocTestSetup, :(using Unitful)) @@ -17,6 +17,7 @@ makedocs( "How units are displayed" => "display.md" "Logarithmic scales" => "logarithm.md" "Temperature scales" => "temperature.md" + "Interoperability with `Dates`" => "dates.md" "Extending Unitful" => "extending.md" "Troubleshooting" => "trouble.md" "License" => "LICENSE.md" diff --git a/docs/src/dates.md b/docs/src/dates.md new file mode 100644 index 00000000..08da083e --- /dev/null +++ b/docs/src/dates.md @@ -0,0 +1,133 @@ +```@meta +DocTestSetup = quote + using Unitful +end +``` +# Interoperability with the `Dates` standard library + +[Julia's `Dates` standard library](https://docs.julialang.org/en/v1/stdlib/Dates/) provides data types for representing specific points in time `Date`/`DateTime` and differences between them, i.e., periods. Unitful provides methods for using period types from the `Dates` standard library together with `Quantity`s. + +## Support for `Dates.FixedPeriod`s + +The `Dates.FixedPeriod` union type includes all `Dates.Period`s that represent a fixed period of time, i.e., `Dates.Week`, `Dates.Day`, `Dates.Hour`, `Dates.Minute`, `Dates.Second`, `Dates.Millisecond`, `Dates.Microsecond`, and `Dates.Nanosecond`. These types can be converted to `Quantity`s or used in place of them. + +!!! note + `Dates.Year` does not represent a fixed period and cannot be converted to a `Quantity`. While Unitful's `yr` unit is exactly equal to 365.25 days, a `Dates.Year` may contain 365 or 366 days. + +Each `FixedPeriod` is considered equivalent to a `Quantity`. For example, `Dates.Millisecond(5)` corresponds to the quantity `Int64(5)*u"ms"`. A `FixedPeriod` can be converted to the equivalent `Quantity` with a constructor: + +```@docs +Unitful.Quantity(::Dates.FixedPeriod) +``` + +In most respects, `FixedPeriod`s behave like their equivalent quantities. They can be converted to other units using `uconvert`, used in arithmetic operations with other quantities, and they have a `unit` and `dimension`: + +```jldoctest +julia> using Dates: Hour + +julia> p = Hour(3) +3 hours + +julia> uconvert(u"s", p) +10800 s + +julia> p == 180u"minute" +true + +julia> p < 1u"d" +true + +julia> 5u"s" + p +10805 s + +julia> 210u"km" / p +70.0 km hr^-1 + +julia> unit(p) === u"hr" +true + +julia> dimension(p) +𝐓 +``` + +Conversely, a `FixedPeriod` can be created from a quantity using the appropriate constructor, `convert`, or `round` methods. This will fail (i.e., throw an `InexactError`) if the resulting value cannot be represented as an `Int64`: + +```jldoctest +julia> using Dates: Day, Hour, Millisecond + +julia> Millisecond(1.5u"s") +1500 milliseconds + +julia> convert(Hour, 1u"yr") +8766 hours + +julia> Day(1u"yr") +ERROR: InexactError: Int64(1461//4) +[...] + +julia> round(Day, 1u"yr") +365 days +``` + +## Support for `Dates.CompoundPeriod`s + +The `Dates` standard library provides the `Dates.CompoundPeriod` type to represent sums of periods of different types: + +```@repl +using Dates: Day, Second +Day(5) + Second(1) +typeof(ans) +``` + +Unitful provides facilities to work with `CompoundPeriod`s as long as they consist only of `FixedPeriod`s. Such `CompoundPeriod`s can be converted to `Quantity`s using `convert`, `uconvert`, or `round`: + +```@jldoctest +julia> using Dates: Day, Second + +julia> p = Day(5) + Second(1) +5 days, 1 second + +julia> uconvert(u"s", p) +432001//1 s + +julia> convert(typeof(1.0u"yr"), p) +0.01368928562374832 yr + +julia> round(u"d", p) +5//1 d + +julia> q = Month(1) + Day(1) # Month is not a fixed period +1 month, 1 day + +julia> uconvert(u"s", q) +ERROR: MethodError: no method matching Quantity{Rational{Int64},𝐓,Unitful.FreeUnits{(s,),𝐓,nothing}}(::Month) +[...] +``` + +However, not all operations that are defined for `FixedPeriod`s support `CompoundPeriod`s as well. +The reason for that is that a `CompoundPeriod` does not correspond to a specific unit: + +```@jldoctest +julia> p = Day(365) + Hour(6) +365 days, 6 hours + +julia> unit(p) # A CompoundPeriod does not have a corresponding unit ... +ERROR: MethodError: no method matching unit(::Dates.CompoundPeriod) +[...] + +julia> dimension(p) # ... but it does have a dimension +𝐓 + +julia> Quantity(p) # As a result, there is no Quantity type associated with it ... +ERROR: MethodError: no method matching Quantity(::Int64) +[...] + +julia> T = typeof(1.0u"hr"); T(p) # ... but it can be converted to a concrete time quantity +8766.0 hr +``` + +Consequently, any operation whose result would depend on the input unit is not supported by `CompoundPeriod`s. For example: + +* `+(::Quantity, ::CompoundPeriod)` and `+(::CompoundPeriod, ::Quantity)` error, since the unit of the result depends on the units of both arguments. +* `div(::Quantity, ::CompoundPeriod)` and `div(::CompoundPeriod, ::Quantity)` work, since the result is a dimensionless number. +* `mod(::CompoundPeriod, ::Quantity)` works, but `mod(::Quantity, ::CompoundPeriod)` does not, since the second argument determines the unit of the returned quantity.