Duration as milliseconds

std::time::Duration supports durations as long as 42 times longer than the age of the universe. However, it doesn’t have as_millis() method, because it could overflow u64 if the duration was longer than about a half billion years.

As it currently stands Duration is very correct about handling millisecond precision on truly astronomical scale, but is missing ability to easily get its numeric value with precision better than a second. Users are forced to implement their own way to combine seconds + subsecond values, which is inconvenient, and a quick implementation won’t deal with handling half-billion-year durations either.

What should stdlib do?

  • Return u128
  • Return u64 and panic if the duration is longer than half billion years
  • Return u64 and clamp the the duration to half billion years (return u64::max_value() if value doesn’t fit in u64)
  • Return Option<u64> or Result<u64> and None/Err if the duration is longer than half billion years
  • Don’t add the method
  • Don’t know/don’t care

0 voters

7 Likes

Would another option be to return Result<u64>? Returning None to signal an overflow seems a little strange. Is this pattern used elsewhere in the stdlib?

I’ve updated the poll to include that as well (I’ve written Option first, because Duration::checked_mul returns Option already)

2 Likes

Keep in mind that we’re talking about millisecond precision on a scale of half billion years. I’m very skeptical that anybody who actually needs to work with time spans that large needs that much precision, and if they do, that they will be able to use a generic timestamp with a naive linear time (at that scale you’d probably want to ask “where” and “how fast were you going” about your timestamp too).

6 Likes

Note that a pull request which adds as_nanos(), as_micros() and as_millis() (all return u128) is currently under review.

5 Likes

Isn’t u128 pretty expensive and not so great for portability?

What about an option to return a BigInteger (or equivalent instead)? What if you want nanosecond precision? Or something even more precise than that (can’t think of why you would though). Have an API like:

fn as( precision : TimeUnit ) -> Result<BigInteger,UnrepresentableErrror> 

perhaps? Where, if the duration in question is unrepresentable as a BigInteger in the given precision, it returns the error. Though, I’m not sure if that is needed because a proper BigInteger implementation should, barring memory overflow, be able to represent any number no matter the size.

u128 is sufficient to represent 264 seconds down to attosecond (10-18 s) precision. There’s no need to introduce BigInteger.

5 Likes

Good point.

Would it be nice if these return new-types instead of just u128? Like:

  • struct Milliseconds( pub u128 )
  • struct Nanoseconds( pub u128 )
  • etc…

I see Duration as already being the newtype for nanoseconds, so conversion from one newtype to another newtype isn’t adding anything new.

1 Like

The portability problem of u128 has been exaggerated :slightly_smiling_face:. According to Tracking issue for 128-bit integer support (RFC 1504) · Issue #35118 · rust-lang/rust · GitHub only NVPTX, Atmel (AVR) and Emscripten are not supported at the moment.

4 Likes

Some comments:

  • u128: I voted for this alternative. std::time::Duration is not only used for waiting – it encodes the semantics of durations. See the checked_* functions for math operations.

  • panic: This makes the type unreasonably brittle. The point of supporting longer durations than 5×108 years is not that you’re likely to wait that long, it’s so that you can use these numbers for further calculations.

  • clamp: This is the worst alternative because it distorts the numbers silently. This is dangerous stuff.

  • Option<u64>: Yeah, well… This is consistent but you’ll likely use 128 bits(?) of storage anyhow because of alignment.
11 Likes

I heard, "Ba-Bong!" (Netflix Style), when I read this!

I accidentally voted for Option<u64>/Result<u64> but honestly returning u128 then using .try_into::<u64>() etc is also acceptable. We should optimize for today and the foreseeable future however, not the astronomically distant future.

  1. Should we base Duration on i128, since time machines might eventually lead to negative duration in a distant, but perhaps not astronomically distant, future? :smiley: That would also simplify the comparison of Durations and computations about relative Durations.

  2. Since Rust is an international language, is there a reason why SI units are not used for the unit abbreviations, rather than some truncated English time-unit names with an appended pluralizing "s"? In other words, method names would be based on SI time units, leading to as_ms, as_µs, as_ns, as_ps, and even as_as (for attoseconds). I've personally never seen a published benchmark duration measured in millis or micros; it's measured in ms or µs. We use SI time units all the time in technical work, even in countries like the US where traditional units are used for other quantities. So why not use SI time units in the various measures of Duration?

Edit: Corrected a typo: "were" to "where"

6 Likes

The reason is probably practical — µ is hard to type for most users (I had to copy&paste yours). milli- and micro- are usual prefixes (e.g. millimeter). If you assume the s suffix is not for plural, but a symbol for second, it kinda makes sense.

But why not use the common abbreviations: ms, us as a synonym for µs, ns, etc? What is the rationale for truncating English time-unit words (e.g., “milliseconds”) and expecting ESL (i.e., English as a second language) readers to infer how those abbreviations arose?

2 Likes

I look forward to const generics so we can just have customizable versions of these things with full names (like https://en.cppreference.com/w/cpp/chrono/duration#Helper_types), not just one choice of accuracy and range.

1 Like

My vague understanding of popular physics consensus is that "backwards" time travel is impossible. But given relativity, perhaps we should be specifying all measurements with a point of reference :wink: However let's avoid a discussion of physics.

It's more like the written prefixes that you advocated, just unabbreviated, which also appear to be the standard romanizations in other languages (at least as many as have a Wikipedia page on SI prefixes).

Rust, like most prominent programming languages, was developed in and optimized for English. Even languages like Lua (Brazil), Python (Netherlands), and Ruby (Japan) are optimized for English. I don't see the necessity to bikeshed this from a language perspective. The rest of the language has short abbreviations like Vec, but are there any you might forget the expanded form to? Contrast this with a variety C and unix names shortened obtusely to fit ancient restrictions.