Interoperability with the Dates standard library

Julia's Dates standard library 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 Quantitys.

Support for Dates.FixedPeriods

The Dates.FixedPeriod union type includes all Dates.Periods 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 Quantitys 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:

Unitful.QuantityMethod
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

julia> using Dates: Second

julia> Quantity(Second(5))
5 s
source

In most respects, FixedPeriods 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:

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:

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.CompoundPeriods

The Dates standard library provides the Dates.CompoundPeriod type to represent sums of periods of different types:

julia> using Dates: Day, Second
julia> Day(5) + Second(1)5 days, 1 second
julia> typeof(ans)Dates.CompoundPeriod

Unitful provides facilities to work with CompoundPeriods as long as they consist only of FixedPeriods. Such CompoundPeriods can be converted to Quantitys using convert, uconvert, or round:

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 FixedPeriods support CompoundPeriods as well. The reason for that is that a CompoundPeriod does not correspond to a specific unit:

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 CompoundPeriods. 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.