Pre-RFC: Adding minutes & hours API to time::Duration

Summary

This RFC would add the following methods to time::Duration;

impl Duration {
    fn from_mins(mins: u64) -> Self;
    fn from_hours(hours: u64) -> Self;
    fn as_mins(&self) -> u64;
    fn as_hours(&self) -> u64;
}

Motivation

When using time::Duration for longer running tasks, such as background processes, or for user facing output having constructors and accessors for minutes and hours would reduce a small amount of boilerplate that can occur frequently in these kinds of programs. It adds more semantic way to create and use time::Duration.

Current

// minutes
duration.as_secs() / 60

// hours
duration.as_secs() / 3600
// or to be more semantically clear.
duration.as_secs() / 60 / 60

Proposed

// minutes
duration.as_mins()
// hours
duration.as_hours()

Drawbacks

We could not add these methods as it could be seen as too niche, preferring to defer this functionality to third-party crates.

Unresolved questions

  • Should it be hours or hrs? hrs would match the shortened name scheme, however it might not be as clear as hours, and hours is only one character longer than mins.
3 Likes

We have tried adding these before, could you address the comments raised in the PR?

2 Likes

Thank you for the link, I can't believe I missed this!

So to address the feedback, which I believe is that these methods, namely hours could be misused in a way that would outweigh their usefulness due to how infrequently this pattern appears.

The two use cases I saw considered in that RFC was simulation software and long running backend processes. I would say that this API would be more frequently used in user facing applications, system measurement tools, and particularly embedded applications where external timers like cron might not be available.

I would agree with not adding days or anything beyond, however I don't think it's unreasonable to have tasks that need to be run in an hour, or for the next few hours.

1 Like
// hours
duration.as_secs() * 120

You mean 3600, right?

2 Likes

Whoopsie! This right here is exhibit A as to why these methods should exist!

5 Likes

I think have methods for minutes and hours would be great.

I guess I'll go ahead and start a bikeshed: what about using f64 instead of u64? I know u64 is consistent with the existing Duration::from_*() methods, but I'm much more likely to have a fractional minute or hour than I am a fractional milli/micro/nano second.

My personal ideal would be supporting both f64 and u64 somehow (I'm not really a fan of having a bunch of *_f32() and *_f64() suffixed methods)... maybe with generics? I'm not sure what the right trait bound would be though.

This made me aware of another mistake in the proposal:

// minutes
duration.as_secs() * 60

should be

duration.as_secs() / 60

As I understand it, the main concern in this thread was that it would encourage using the API in the wrong way.

For example, someone could write duration.as_hours() / 24. to get the number of days, which might produce the wrong result because of daylight saving. To do this properly, you need a library like chrono.

According to this comment, the main use case of std::time::Duration is benchmarking, where converting to/from hours or days is not needed.

That seems like a fairly large assumption as Rust gets used for higher-level things. To avoid this:

The API could be:

duration.as_approximate_hours();
duration.as_approximate_minutes();
duration.of_approximate_minutes( .. );

etc.

It would meet the use-cases while clearly signaling that there is something inexact about the duration which should then prompt the user to read the docs which could explain those issues.

This would make the API useful to those needing higher-level (beyond benchmarking) kinds of durations without setting up the wrong expectations.

1 Like

Rather than supporting f64 or similar, it might be better to have:

Duration::from_approximate_days_hours_minutes_seconds( ... )
Duration::from_approximate_hours_minutes( ... )
etc.

For higher-level things, you should probably use something like chrono instead, which also supports days and larger time spans.

1 Like

I think approximate is too confusing.

Wikipedia mentions "civil time" as the one which may be affected by daylight savings and leap seconds. There are other time standards mentioned and I think the closest one to what we normally want is "atomic" although that minutes/hours/days do not seem to be defined by them. Other terms which may be confusing are "standard" (the same as civil time) and "proper" (affected by relativistic movement).

How about

duration.as_nominal_hours();
duration.as_nominal_minutes();
duration.of_nominal_minutes( .. );

to account for the daylight savings time shifts and leap seconds that are common in civil time. GMT and UTC also have leap seconds, because they are based on astronomic time, so nominal_minutes would apply to them as well.

TAI (Temps Atomique International, International Atomic TIme) does not have leap seconds, which is why industrial process control standards, which use finite-difference integrals and derivatives that are incompatible with the discontinuity induced by a leap second, prefer it.

2 Likes

I think the talk here is about the durations expressed in other than seconds units. As a duration is not attached to any moment in time neither to a timezone, it cannot be converted to any "standard/civil” time unit (minute/hour/day). But it can be converted to "atomic" version of them which ignore anything Earth-related, such as leap seconds and daylight shifts.

1 Like

We also have https://github.com/rust-lang/rust/issues/57391, which should allow one to write 3 * DAY + HOUR

Wouldn't naming them DAY & HOUR also have similar problems in terms that it doesn't describe it's accuracy so it could be confused as more accurate than it is?

I think the talk here is about the durations expressed in other than seconds units. As a duration is not attached to any moment in time neither to a timezone, it cannot be converted to any "standard/civil” time unit (minute/hour/day). But it can be converted to "atomic" version of them which ignore anything Earth-related, such as leap seconds and daylight shifts.

What if the API was instead to_atomic_? The reasoning for to over as is that as to me implies zero computation to get that representation, where as I've seen to used in APIs to describe when an operation requires some amount of computation to perform.

impl Duration {
    fn from_atomic_mins(mins: u64) -> Self;
    fn from_from_hours(hours: u64) -> Self;
    fn to_atomic_mins(&self) -> u64;
    fn to_atomic_hours(&self) -> u64;
}

When you have a clock showing 01:00 summer time, wait one hour, then adjust the clock to winter time, does this mean that no time has passed?

This paragraph betrays a confusion between two concepts: civil time and physical time. It's only really meaningful in the latter to speak of time durations; civil time defines a coordinate space for time instants, but because this coordinate space is not perfectly regular and liable to be redefined in the future, specifying what a 'duration' of civil time should mean is somewhat problematic. When regarded as units of physical time, a minute is simply 60 SI seconds exactly, and an hour is 60 minutes exactly. Adjustments like DST or leap seconds don't enter into it at all. In civil time though, hours, minutes and seconds are not even units, they are just slots for coordinates.

Apparently, Duration can be used both to express distance between 'physical' time Instants (ignoring things like clock drift and virtual machines) and between values of (civil) SystemTime, with no indication in the documentation that those are distinct things. This strikes me as quite bad design; but it still seems to me that Duration is mostly meant for the former anyway.

And if so, it should be fine to ignore DST and leap seconds entirely. Just document that Duration values may diverge from what is obtained by naïve subtraction of civil time coordinates (maybe even link to some snotty list of 'falsehoods programmers believe about time' for good measure), then fix a unit of 86400 SI seconds and call it a day.

8 Likes

Could not agree more. Seconds, Minutes, Hours, Days, and Weeks should all be relatively uncontroversial. Months and Years is a different matter though.

2 Likes