Time units in std::time


#1

The main unresolved question is: should we include minute, hour and day or not? And if yes, how they should be named, simply DAY, or something like SI_DAY, STD_DAY?


#2

IMO We should either name them DAY, HOUR and MINUTE, or just don’t include them at all.

Prior art: Name conventions from other standard libraries

Language ns µs ms s min h day week
C# - - Milliseconds Seconds Minutes Hours Days -
C++ (types)* nanoseconds microseconds milliseconds seconds minutes hours days weeks
C++ (literal suffix) ns us ms s min h - -
D* nsecs usecs msecs seconds minutes hours days weeks
Dart - Microseconds Milliseconds Seconds Minutes Hours Days -
Go Nanosecond Microsecond Millisecond Second Minute Hour - -
Java Nanos - Millis Seconds Minutes Hours Days -
Nim* - - milliseconds seconds minutes hours days -
Python - microseconds milliseconds seconds minutes hours days weeks
Rust (std) nanos micros millis secs - - - -
Rust (chrono) nanoseconds microseconds milliseconds seconds minutes hours days weeks

Note*:

  • C++20 also defines “months” (exactly 1/12 years) and “years” (exactly 365.2425 days). I don’t see how these can be useful.
  • D also defines “hnsecs” (100 nanoseconds), a unit often used in Windows APIs.
  • Nim’s TimeInterval is not calendar-independent.

#3

Why not add an si!() macro that permits specifying a constant duration, or simply an SI unit? E.g.,

let observation_interval: duration = si!(3 da 6 hr 19.05 s 273 ns);
let timeout: duration = si!(1500 us);
CONST SI_US: duration = si!(µs);

Since the underlying representation is UTF-8, both 1500 us and 1500 µs can be acceptable. A future RFC could include SI scaling units if that proved desirable (though personally I’d rather not see 1.5 kius as a way of writing 1536 µs).

Edit: Added example of an isolated unit.


#4

Ideally I would prefer to have custom literal suffixes à la C++. So we could write something like this:

use std::time::suffixes::*;

let observation_interval = 3d + 6h + 19s + 273us;
// for those who don't like `us`
let timeout = 1500µs;

Plus don’t forget that day, hour and minute are not SI units per se.


#5

True. But si_extended!() is too cumbersome a macro name. The problem with day or da is of course the occasional leap-second, which currently occurs about every 18 months but which will become more frequent over the centuries due to earth-moon tidal momentum transfer. That same leap-second is what complicates hour and minute, since the extra second is represented by 23:59:60 .


#6

Minutes, hours and days are considered “Non-SI units accepted for use with the International System of Units” (with standardized symbol and conversion factor 1min = 60s, 1h = 3600s and 1d = 86400s), which I think is fine. They are not in SI proper simply because they aren’t related to seconds by factors of 10n.

BTW, for easy tracking, we have rejected additions of Durations::from_{minutes, hours, days} previously because of confusion when a timezone change is involved,

// cargo-deps: chrono, chrono-tz
extern crate chrono;
extern crate chrono_tz;
use chrono::{Duration, TimeZone};
fn main() {
    let eastern_time = chrono_tz::US::Eastern;
    let datetime_1 = eastern_time.ymd(2018, 3, 11).and_hms(1, 59, 59);
    let datetime_2 = eastern_time.ymd(2018, 3, 11).and_hms(3, 0, 0);
    assert_eq!(datetime_2 - datetime_1, Duration::seconds(1));
}

I don’t think leap seconds is a blocking issue, chrono for instance simply treated hh:mm:60 as the same as hh:(mm+1):00.


#7

Well 23:59:60, 23:60:00, and 24:00:00 are all problematic for software that expects a value in the range (00..=23).(00..=59).(00..=59). Nor can the extra second conveniently be ignored, as it might turn up in logs, and even worse in the computation of a derivative as in speed, flow rate, etc.

Edited to show ranges in Rust syntax.


#8

That’s not a concern because we are only talking about durations here, not time points.


#9

Then I don’t understand your prior statement

I was raising the unpredictable leap-second as the likely reason why minutes, hours and days had not made it into SI units. I was not intending to imply that duration would ever involve leap-seconds directly (though a duration computed from the difference of two time stamps obviously would have to take such a possibility into account). The issue over leap-seconds is why many industrial automation standards use TAI rather than UTC, so that the leap-second irregularity does not enter into computations of duration.


#10

We’re talking Rust here, how SI categorizes a unit is out of scope.

If you read PR 47097 I’ve linked above you’ll see the reason “days” and “hours” being rejected was solely because of timezone change (e.g. 2018 Mar 11th 01:00:00 EST + 24 hours = 2018 Mar 12th 02:00:00 EDT could be unexpected to humans) and process durability (your process will die before the day ends, consider a cron job instead). Nobody from the team mentioned leap seconds or SI, which is why I say it’s not a blocker or concern.

Further, “minutes” was allowed in 47097 but simplifying a factor of mere 60 doesn’t provide much advantage, and thus the entire PR was abandoned. The discussion from 47097 affects this PR here, as they have the equivalent semantics.


#11

My initial post in this thread attempted to point out that a simple macro could provide a way to write duration in proper SI units, with us as a supported alternative to the harder-to-enter unit µs. Unfortunately my follow-up posts derailed into a discussion about SI units. :cry: apologies


#12

This. I’d love for there to be custom suffixes - I think that’s a really cool feature of C++, and works particularly nicely with time units.


#13

I canceled a reply to the same @newpavlov post about six hours ago because I concluded that my original suggestion, that of a macro to handle SI time units and non-SI time units accepted for SI, was the best approach and it had already been shared. The problem I have with the C++ approach is that it is not SI precisely because it appends alphanumeric suffixes to numeric values, whereas SI requires there to be an intervening space between the value (which could be symbolic) and the SI suffix. The si!() macro would need such space separation to parse, thus resulting in contained sequences that actually are valid SI.


#14

Side rant: I suppose that my quest for SI units is on a par with the desire that the US would use the metric system. I am so-o-o tired of reading mb in a press release when MB was intended. The fact that there is an almost ten-orders-of-magnitude difference between the two units seems to escape the innumerate authors.


#15

Since these are hard constants, I dislike the idea of including these units as simple words like “DAY”, “HOUR”, etc., as I find it lacks enough information to describe to naive developers if there is or is not extra work happening on the back-end around the fuzzy points like DST. Experienced developers are less interesting to contemplate for this naming choice as they will generally follow good practices, read documentation, and test properly to figure it out.

With that in mind, I actually think something like SI_DAY is a good idea, as it encourages the naive developer to take a moment and think “oh wait what’s the SI mean?” and nudges them to take a quick look at the docs. An experienced developer will notice that they’re all constants in measures of seconds or will know about SI and find it obvious.

Despite my clear preference, @kennytm’s post is very clear in showing that this is not the existing standard, so if we decide to follow suit with most of the other languages in the naming scheme of this fairly common ambiguity, then at bare minimum we are in parity with other languages, so that’s okay, too!


#16

More prior art: Diesel has an interval DSL that implements methods like hours, and minutes on i32, i64, and f64 and return PgInterval (our representation of the Postgres interval type): https://docs.rs/diesel/1.2.1/diesel/pg/expression/extensions/trait.IntervalDsl.html


#17

As mentioned on reddit, a parser for a subset of ISO8601 duration strings as a const fn would make a pretty sweet constructor for Duration, too. (Though currently not possible to implement because of const fn “RFC-needed” limits, and it’s also not possible to mark the from_str function in an impl of FromStr as a const fn, sadly.)