Skip to content

A duration is a count and a unit

Level: 201 · working knowledge

One line: std::chrono::milliseconds{1500} is the number 1500 plus a unit the compiler tracks for you — conversions that could lose something have to be asked for by name, and forgetting that a duration is not a number is how the talk once measured a clock at 0ns.

#include <chrono>
using namespace std::chrono_literals;

auto timeout = 1500ms;                                                    // 1500, in milliseconds
auto whole = std::chrono::duration_cast<std::chrono::seconds>(timeout);  // 1s — you asked to lose 500ms

Verified output of a_duration_is_a_count_and_a_unit.cpp — regenerated by tools/run_examples.py and held to the same answer key on libstdc++ and libc++ in CI, never hand-typed.

ms.count()        = 1500
ms                = 1500ms
1s + 250ms        = 1250ms
1s - 2s           = -1s
2s as ms          = 2000ms
1500ms as double  = 1.5s
ms -> s implicitly: false

                 1500ms   2500ms  -1500ms
duration_cast        1s       2s      -1s
floor                1s       2s      -2s
ceil                 2s       3s      -1s
round                2s       2s      -2s

numeric_limits<nanoseconds>::is_specialized = false
numeric_limits<nanoseconds>::max() = 0ns
nanoseconds::max()                  = 9223372036854775807ns
...in whole years                   = 292

A count and a unit

std::chrono::milliseconds is duration< a signed integer of at least 45 bits , std::milli> — a rep that holds the count and a period that says what one count is worth, the same pair every clock defines (What a clock is). Arithmetic between two units happens in the finer one, so 1s + 250ms is 1250ms; and the count is signed, so 1s - 2s is -1s. .count() hands back the bare number, which is where the compiler stops being able to help.

Converting

A conversion that cannot lose anything happens by itself: 2s into milliseconds, or an integer count into a floating-point one, which keeps the fraction — 1500ms becomes 1.5s. A conversion that could lose something does not compile until you say how to lose it — is_convertible_v<milliseconds, seconds> is false — and the four ways of saying it are the rows of the table in the output:

  • duration_cast truncates toward zero, so 1500ms is 1s and -1500ms is -1s.
  • floor rounds down, so -1500ms is -2s.
  • ceil rounds up.
  • round goes to the nearest, and a tie goes to the even number: 1500ms is 2s, and so is 2500ms.

duration_cast is the one most code reaches for, and for negative durations it is usually not the one it means.

The talk's Min = 0ns

The talk's How long does it take? slide times back-to-back calls to now() and keeps the smallest gap. Its first version starts the running minimum like this:

auto minDelta = std::numeric_limits<clock::duration>::max();

and prints Min = 0ns. The next slide swaps in 1'000'000'000ns and gets "20-30ns!". The slides leave the reason to the talk; the output above shows it. std::numeric_limits is specialised for arithmetic types, not for duration, and for the unspecialised template the standard says "all member functions return a value-initialized object" (numeric.limits.general ↗) — a duration of zero. std::min(0ns, gap) is 0ns for every non-negative gap, so the minimum never moves. The maximum the slide wanted is a member of duration itself:

auto minDelta = clock::duration::max();   // 9223372036854775807ns: 292 years

If you are coming from another language

  • Rust. One type, std::time::Duration, for every unit — seconds plus nanoseconds, unsigned — so 1s - 2s has nowhere to go: checked_sub returns None, and plain - panics. C++ puts the unit in the type and lets the count go negative: A Duration cannot be negative ↗.
  • Python. datetime.timedelta is the typed duration: one type for every unit, negative allowed, and normalised rather than truncated — timedelta(seconds=1) - timedelta(seconds=2) prints -1 day, 23:59:59. The clock functions skip it and return float seconds, or int nanoseconds from the _ns variants such as time.perf_counter_ns().
  • ABAP. (Not machine-checked.) There is no duration type: a length of time is an integer or a packed number with its unit in the variable's name, such as lv_runtime_us — the convention duration exists to replace.

See also