Skip to content

proposal: time/v2: make Duration safer to use #20757

@bcmills

Description

@bcmills

time.Duration is currently very prone to accidental misuse, especially when interoperating with libraries and/or wire formats external to Go (c.f. http://golang.org/issue/20678).

Some common errors include:

  • Unchecked conversions from floating-point seconds.
  • Unchecked overflow when multiplying time.Duration values.
  • Conversion from floating-point to time.Duration before scaling instead of after.
  • Accidentally scaling an already-scaled value during conversion.

https://play.golang.org/p/BwwVO5DxTj illustrates some of these issues. The bugs are unsurprising once detected, but subtle to casual readers of the code — and all of the errors currently produce unexpected values silently. (Some but not all of them would be caught by #19624.)

For Go 2, I believe we should revisit the Duration part of the time API.


A quick survey of other languages shows that Go's Duration type is unusually unsafe. Among modern languages, only Swift appears to be prone to the same bugs as Go.

Out-of-range conversion and overflow

Exceptions:

  • The Rust constructor panics on out-of-range conversions and provides checked variants of arithmetic operations.
  • C# raises OverflowException or ArgumentException for out-of-range arguments (to FromMilliseconds and friends).
  • Java doesn't appear to have an explicit conversion operator, but raises ArithmeticException on overflow to its arithmetic methods.
  • Python's timedelta raises OverflowError.
  • Standard ML raises the Time exception if the argument is out of range.

Floating-point or arbitrary-precision representations:

  • C++11 allows the use of floating-point representations; I would expect that overflow with integer representations is undefined.
  • The only OCaml time package I could find uses a floating-point representation.
  • The Haskell constructor takes an arbitrary-precision Integer argument, but it does not appear to check ranges on floating-point to integer conversions.

Double-scaling

  • Rust, Java, Python, C++11, and OCaml all have asymmetric scaling or multiplication operations: you can scale a duration by a float or an integer, but you cannot scale a duration by a duration.
  • The Haskell type system will reject attempts at double-conversion: converting an Integer to a TimeInterval is an explicit function call, not just a reinterpretation of the type. However, it won't stop you from erroneously multiplying a TimeInterval by a TimeInterval.
  • C# and Standard ML do not support multiplying time intervals at all, requiring a round-trip conversion through a primitive number type in user code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Proposalv2An incompatible library change

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions