ADR-150: A Duration Is std::time::Duration, and It Is Written 5.seconds()

Status: Accepted Date: September 19, 2026 (D5: October 4, 2026, #377) Related: ADR-141 D2 (a construct a page writes before its record), ADR-313 D2 (sleep pauses, so it is time::sleep), ADR-285 (no suffix on a number literal, and why), ADR-285 (a literal takes its type from its use), ADR-288 D11 (an entry is written and reviewed, not copied from Rust), Part II 12.4, Part III 13.3 (cleanup-deadline, which is already a duration in a manifest)

1. Context

Part II 12.4’s sleep(5.seconds()) wrote a value of a type nothing declared. The type is not in dispute — a duration is a duration — so what is left is where it lives and how it is spelled.

The manifest already has one: cleanup-deadline is a duration a person writes in a .toml. What was missing is the value a program holds.

2. Decisions

D1 — The type is std::time::Duration.

A std type, not a language one. It carries a span of time and nothing else, it is a value that copies, and it is what time::sleep, a timeout and a deadline take. It is Rust’s own type re-exported, not wrapped — a wrapper would be a second type with the same meaning — and it crosses a thread (crosses = true).

D2 — It is written 5.seconds(), as a method on an integer.

Changed by ADR-313 D2: sleep pauses, so it is not in the prelude; a program writes time::sleep(5.seconds()) after use std::time.

use std::time

time::sleep(5.seconds())
time::sleep(250.millis())
let deadline = 2.minutes()

An extension std::time provides — seconds, millis, micros, minutes, hours on both integer types of Part I 2.2. A computed count is written the same way, retry_after.seconds(), so there is no second spelling: no Duration::seconds(5), which would be a type’s associated constructor beside the method (ADR-140 D2). A count below zero is no time at all rather than an abort, and a count too large to multiply saturates at the longest span there is rather than wrapping into a short wait. time::sleep is a suspension point: its entry carries no sync.

It is what the pages already write, which is not a small argument: the spelling was chosen by whoever wrote the example, and it reads the way the sentence does.

D3 — There is no suffix literal, and that is ADR-285 kept.

5s and 250ms would be a literal form that record declined for numbers, on the ground that the digits a number is written in are a spelling and the use gives the type. Reopening it for one type would make the rule a literal has no suffix, except for durations — and a rule with an exception in it is a rule a reader has to remember rather than one they can apply.

D4 — It is std’s, so the language learns nothing.

No keyword, no literal, no type in the grammar. What this costs the language is nothing at all; what it costs std is a type and a trait implementation.

D5 — It is read back as timeout.in_seconds(), in the five units it is made in.

Added October 4, 2026 (#377).

let timeout = 90.seconds()
timeout.in_seconds()    // 90
timeout.in_minutes()    // 1

in_hours, in_minutes, in_seconds, in_millis and in_micros, as methods on the span: the five constructors of D2 mirrored, so a unit has one name in each direction. Whole units, cut off, as an integer division is (Java’s toSeconds, Go’s Milliseconds and Rust’s as_secs cut off too). The answer is an i64, a count’s type (ADR-285 D1), so the type says it is whole and the name need not.

Past i64::MAX it stays at i64::MAX. A span holds more microseconds than an i64 counts only past 292,000 years, so only a span that was itself saturated (i64::MAX.hours()) gets there; for ever read back as the largest number is D2’s saturation mirrored, and what a timeout computation wants rather than an abort.

Why not the other spellings. whole_seconds (Kotlin’s inWholeSeconds) reads oddly; d.seconds() would be one name that makes a span on a number and reads a number on a span; to_seconds is a conversion verb this language writes as as or truncating_; d / 1.seconds() is an operator on a std type nobody asked for. Rust’s own as_secs, as_millis and kin, which answer u64 and u128, are not in the ledger: one spelling per unit. A fractional reading (1.5 seconds) is not decided here.

3. Consequences

4. What this does not decide

This site is open source. Improve this page.