Repository: go
Issue: proposal: time/v2: make Duration safer to use
Labels: v2,Proposal

`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](https://developer.apple.com/documentation/foundation/timeinterval) appears to be prone to the same bugs as Go.

**Out-of-range conversion and overflow**

Exceptions:
* The Rust constructor [panics](https://doc.rust-lang.org/std/time/struct.Duration.html#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](https://msdn.microsoft.com/en-us/library/system.timespan.frommilliseconds(v=vs.110).aspx) and friends).
* Java doesn't appear to have an explicit conversion operator, but raises `ArithmeticException` on overflow to its [arithmetic methods](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#plusNanos-long-).
* Python's [`timedelta`](https://docs.python.org/2/library/datetime.html#timedelta-objects) raises `OverflowError`.
* Standard ML raises the `Time` exception if the argument is out of range.

Floating-point or arbitrary-precision representations:
* [C++11](http://en.cppreference.com/w/cpp/chrono/duration) allows the use of floating-point representations; I would expect that overflow with integer representations is undefined.
* The only [OCaml](https://ocaml.janestreet.com/ocaml-core/109.31.00/doc/core/Time.Span.html) time package I could find uses a floating-point representation.
* The Haskell [constructor](https://hackage.haskell.org/package/time-units-1.0.0/docs/Data-Time-Units.html#t:TimeUnit) takes an arbitrary-precision `Integer` argument, but it [does not appear to check](https://stackoverflow.com/questions/24338673/why-does-flooring-infinity-not-throw-some-error) ranges on floating-point to integer conversions.

**Double-scaling**

* [Rust](https://doc.rust-lang.org/std/time/struct.Duration.html#method.mul), [Java](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#multipliedBy-long-), [Python](https://docs.python.org/2/library/datetime.html#timedelta-objects), [C++11](http://en.cppreference.com/w/cpp/chrono/duration/operator_arith4), and [OCaml](https://ocaml.janestreet.com/ocaml-core/109.31.00/doc/core/Time.Span.html) 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#](https://msdn.microsoft.com/en-us/library/system.timespan(v=vs.110).aspx) and [Standard ML](http://sml-family.org/Basis/time.html) do not support multiplying time intervals at all, requiring a round-trip conversion through a primitive number type in user code.