How can I convert a CompoundPeriod type object to Period type in Julia?

Using Dates module in Julia I am able to transform a Period type object (Hour, Minute, Second, Day etc.) to another Period type as below:

using Dates

julia> convert(Day, Hour(96))
4 days

julia> convert(Second, Hour(20))
72000 seconds

But if I try to convert any CompoundPeriod type object (Hour(2) + Minute(20), Week(3) + Day(5) etc.), it throws an error as below:

julia> convert(Second, Hour(3) + Minute(12))
ERROR: MethodError: Cannot `convert` an object of type Dates.CompoundPeriod to an object of type Second

Here, 3 hours + 12 minutes = 11520 seconds
Is there a Julia function to find the number of seconds (11520 seconds) in this case?
How can I convert a CompoundPeriod type object to Period type in Julia?

I don’t think there is a nice way to do this right now (but it would be good to add it). You can use Dates.tons or Dates.toms to convert a CompoundPeriod to a number in nanoseconds or milliseconds, but it will be a floating-point number, so you might lose some precision. For example, you could do

julia> c = Hour(3) + Minute(12)
3 hours, 12 minutes

julia> convert(Second, Millisecond(Dates.toms(c)))
11520 seconds
2 Likes

Another way, which does not use floating-point numbers, is the following:

julia> c = Hour(3) + Minute(12)
3 hours, 12 minutes

julia> sum(Second, c.periods)
11520 seconds
3 Likes

I have made a PR to enable conversion of CompoundPeriods to Periods.

3 Likes

Don’t know, if this is a good idea. From the docs:

help?> Dates.CompoundPeriod
  CompoundPeriod

  A CompoundPeriod is useful for expressing time periods that are not a fixed multiple of smaller periods. For
  example, "a year and a day" is not a fixed number of days, but can be expressed using a CompoundPeriod.

From this definition it seems clear, why there isn’t such a thing.
Of course, one can argue, that it should be available and do the conversion for those CompoundPeriods, where it is uniquely possible, like for hours+minutes to seconds.
Should it raise an error/exception for year+month e.g. into days?

I didn’t look into your PR, so perhaps you have already thought of all this issues.

1 Like

The conversion only works for CompoundPeriods for which all contained Periods can be converted. Otherwise, it throws a MethodError.

1 Like

nice

Thank you @sostock . This is the best way so far :slight_smile:

Fyi:

using Dates, CompoundPeriods
t = Hour(3) + Minute(12)
Seconds(t)    # 11520 seconds
1 Like

Should this be Second not Seconds?

As a more general question, I don’t understand what the intention behind CompoundPeriod is.

  • Could someone explain how / what it is supposed to be used for?

I understand that a CompoundPeriod is a collection of Periods, and that it can be constructed from a Vector{Period}.

However, what can be done with it after this? There is a canonicalize function which converts (assuming I understand correctly) a CompoundPeriod into another CompoundPeriod. It redistributes the quantities assigned to each Period component of a CompoundPeriod in the canonical way.

Beyond that - is there anything else you can do with a CompoundPeriod?

Sorry I’m somewhat confused because I don’t appear to be able to do any of the following things:

  • convert it to a Period
  • print it (format it as a String)
  • convert it to a number of Seconds, either as an integer or floating point number of “seconds”
  • convert it to a Time or TimePeriod or some other “duration” type

I would have guessed that I should be able to do one or more of the above operations?

I’ve discovered you can convert a CompoundPeriod to a Time type, if the duration is less than 24 hours.

Dates.Time(compound_period.periods...)

If it exceeds 24 hours, I think you have a problem

I think we should use CompoundPeriods.jl Seconds():

using Dates, CompoundPeriods
t = Hour(3) + Minute(12)
Seconds(t)   # 11520 seconds
Second(t)    # 0 seconds

I think I’m getting confused or otherwise doing something incorrectly.

Do you happen to know why this isn’t working?

julia> using Dates

julia> t=Hour(3)+Second(12)
3 hours, 12 seconds

julia> Dates.CompoundPeriods.Seconds(t)
ERROR: UndefVarError: CompoundPeriods not defined
Stacktrace:
 [1] getproperty(x::Module, f::Symbol)
   @ Base ./Base.jl:31
 [2] top-level scope
   @ REPL[3]:1

I also tried

julia> CompoundPeriods(t)
ERROR: UndefVarError: CompoundPeriods not defined
Stacktrace:
 [1] top-level scope
   @ REPL[4]:1

You missed loading the package:

using CompoundPeriods

Ok thanks. I’ve realized I am asking the wrong questions - DateTimes in Julia has had me going around in circles for an hour or so

The CompoundPeriods.jl package is a third-party package. I would recommend sticking with the Dates standard library for now. (Note that the composite period type is Dates.CompoundPeriod, without an s at the end.)

Regarding your other questions, it’s been a while since I’ve used the Dates standard library (I use Python at work these days), but I can attempt to answer them.

I believe that the Period and CompoundPeriod types should be mostly interchangeable, so you shouldn’t need to do a manual conversion from one to the other.

Not sure if this is what you are looking for:

julia> cp = Month(1) + Day(1)
1 month, 1 day

julia> string(cp)
"1 month, 1 day"

There is the Dates.format function for formatting times as strings, but note that it only works for subtypes of Dates.TimeType (i.e. Date, Time, and DateTime). Periods, durations, and compound periods are not subtypes of TimeType.

A period or compound period represents a calendar period, which does not have a well-defined duration. So, for instance, Month(1) + Day(1) is a compound period that represents “a month and a day”. But the number of seconds in Month(1) + Day(1) depends on the month that you are talking about. So, the second extractor doesn’t work on a period:

julia> second(Month(1) + Day(1))
ERROR: MethodError: no method matching second(::Dates.CompoundPeriod)

Closest candidates are:
  second(::DateTime)
   @ Dates C:\Users\U216145\AppData\Local\Programs\Julia-1.9.0\share\julia\stdlib\v1.9\Dates\src\accessors.jl:62
  second(::Time)
   @ Dates C:\Users\U216145\AppData\Local\Programs\Julia-1.9.0\share\julia\stdlib\v1.9\Dates\src\accessors.jl:66

Typically what you do with a period is add it to a TimeType to get a new TimeType, like this:

julia> t = now()
2024-10-23T09:26:41.859

julia> p = Month(1) + Day(1)
1 month, 1 day

julia> t + p
2024-11-24T09:26:41.859

Sorry, to clarify I meant with some customizable format. I was assuming the format function call might work with something like a String to specify the format. eg HH:MM:SS, etc

Ah yes, ok then it’s reasonable such a thing is not implemented. I should have realized this.

Let me re-think what I want to ask here.

1 Like

Just want to give my 5 cents to this topic, as it came up to me every now and then during work on different projects.
If the duration does not comprise Month or Year you can always convert back to a period. Just make sure you use the lowest occurring unit.
Hence,

convert(Nanosecond, cp)

is a safe bet.
If you are interested in converting to the lowest occurring unit you can define

function toperiod(cp::Dates.CompoundPeriod)::Period
    p = Dates.periods(ei)
    isempty(p) ? Millisecond(0) : convert(typeof(last(p)), cp)
end

Where I deliberately chose to convert zero compund periods to Milliseconds, as that is (currently) the lowest unit of now().

Nice to see reply in this thread after 3 years :slight_smile:

What I feel is that a large time duration becomes easier to see when it is viewed as CompoundPeriod type.

As example here is a Period (more precisely a TimePeriod) object t

julia> using Dates

julia> t = Second(115920)
115920 seconds

Lots of seconds right!? Looking at this time duration t it is difficult to imagine how long it is! This time duration exceeds a whole day or half day!? The best way to view this duration is by transforming it with larger time units (e.g. Day, Hour etc.). We can do that using canonicalize() function as

julia> canonicalize(t)
1 day, 8 hours, 12 minutes

canonicalize() function transforms the time duration into a visually nice CompoundPeriod object which helps us to understand the length of the time duration better.

Now, while doing calculations with typical time durations, it is not always convenient to work with CompoundPeriod objects, because it can take long time while typing out and we need to think about memory allocations also. That’s why, when necessary and possible, we need to transform the CompoundPeriod objects with smaller atomic units (e.g. Minute, Second, Millisecond etc). In Julia v1.8.0 afterwards we can do that using convert() function. As we see for the CompoundPeriod object cp here

julia> cp = Day(1) + Hour(8) + Minute(12)
1 day, 8 hours, 12 minutes

julia> convert(Millisecond, cp)
115920000 milliseconds

julia> convert(Second, cp)
115920 seconds

julia> convert(Minute, cp)
1932 minutes