pub const TIME: &str = "//! Clocks, durations, sleeping and time limits.\n//!\n//! `Instant` reads a monotonic clock, for measuring how long something takes,\n//! and `SystemTime` reads the wall clock, for timestamps. A `Duration` is a\n//! span of time, which `sleep` takes to suspend a task and `timeout` to limit\n//! how long a future may take. Every module sees these names without an\n//! import, and a module\'s own definition of one of them shadows the one here.\n//!\n//! A `Duration` counts whole nanoseconds in an `Int`. `Duration::from_secs`,\n//! `from_millis` and `from_micros` build one, and saturate rather than\n//! overflow. A duration can be negative, and `sleep` and `timeout` treat a\n//! negative one as zero.\n//!\n//! A `Duration` built and read back, a `sleep` timed with `Instant`, and a\n//! wall-clock timestamp:\n//!\n//! ```\n//! let d = Duration::from_millis(1500)\n//! assert_eq(d.as_secs(), 1) // rounded toward zero\n//! assert_eq(d.as_millis(), 1500)\n//!\n//! // Instant measures how long something takes.\n//! let start = Instant::now()\n//! block_on(sleep(Duration::from_millis(10)))\n//! assert(start.elapsed().as_millis() >= 10, \"sleep waits at least as long as asked\")\n//!\n//! // SystemTime reads the wall clock, for timestamps.\n//! let stamp = SystemTime::now().to_iso8601()\n//! assert_eq(stamp.len(), 24)\n//! assert(stamp.ends_with(\"Z\"), \"always in UTC\")\n//! ```\n\n// A point on this process\'s monotonic clock.\n//\n// `nanos` is not privacy-enforced -- no record field in this language is --\n// so a program can build `Instant { nanos: -1 }` naming no real clock\n// reading. `now()` is the intended and only supported way to obtain one,\n// and two are meant to be compared only through `duration_since` or\n// `elapsed`. Held as nanoseconds since the runtime\'s single process epoch,\n// so a real reading is never negative and two real `Instant`s are ordered\n// by subtraction.\n/// A reading of a monotonic clock, from `Instant::now`, for measuring how\n/// much time passes.\n///\n/// The clock never goes backwards, unlike the wall clock `SystemTime` reads,\n/// and its readings mean nothing outside the program that took them. Measure\n/// between two readings with `duration_since`, or from one reading to now\n/// with `elapsed`.\npub record Instant { nanos: Int }\n\n// A span of time, held in nanoseconds because all three constructors below\n// are then exactly representable, and because `elapsed` is a measurement\n// whose precision a coarser unit would discard.\n//\n// `nanos` is not privacy-enforced either, so `Duration { nanos: -1 }`\n// compiles and walks straight past every saturating constructor below.\n// `from_secs`/`from_millis`/`from_micros` are the intended and only\n// supported way to build one. That matters concretely: a negative or\n// absurd `nanos` reaching `sleep` is treated as an immediate wake -- the\n// same silent no-op the saturating constructors exist to prevent.\n/// A span of time, counted in whole nanoseconds; it can be negative.\n///\n/// Build one with `from_secs`, `from_millis` or `from_micros`, and read it\n/// back with `as_secs` or `as_millis`. The constructors saturate: a count too\n/// large for an `Int` of nanoseconds gives the longest `Duration`,\n/// 9,223,372,036,854,775,807 nanoseconds (just over 292 years), and one too\n/// far below zero gives the most negative, -9,223,372,036,854,775,808\n/// nanoseconds.\npub record Duration { nanos: Int }\n\nconst NANOS_PER_SEC: Int = 1_000_000_000\n\nconst NANOS_PER_MILLI: Int = 1_000_000\n\nconst NANOS_PER_MICRO: Int = 1_000\n\n// The largest argument each constructor can scale without overflowing an\n// `Int` of nanoseconds, and the smallest without underflowing one -- the\n// clamped result on either side is whichever extreme of `Int` that\n// direction saturates to.\n//\n// These exist because **Nova\'s `Int` wraps** -- measured:\n// `9223372036854775807 + 1` is `-9223372036854775808`. A wrapped duration can\n// land on either sign. Too large wraps negative, and `sleep` treats a\n// non-positive duration as an immediate wake, so an unclamped `from_secs`\n// would turn \"sleep for ages\" into \"do not sleep at all\", silently. Too\n// negative wraps positive -- measured: `Duration::from_secs(-9_223_372_037)\n// .as_secs()` and `Duration::from_secs(9_223_372_037).as_secs()` both return\n// `9223372036`, byte-identical -- so an unclamped low end would turn\n// \"wake immediately\" into \"sleep for roughly 292 years\", the exact inversion\n// clamping exists to prevent, just on the other side of zero. Clamping both\n// directions yields a saturated answer instead of a wrong one -- the\n// no-panic analogue of Rust\'s `Duration::from_secs`, which panics on the\n// same overflow (Rust\'s version takes an unsigned argument and so has no low\n// side to clamp).\nconst MAX_SECS: Int = 9_223_372_036\n\nconst MAX_MILLIS: Int = 9_223_372_036_854\n\nconst MAX_MICROS: Int = 9_223_372_036_854_775\n\nconst MAX_NANOS: Int = 9_223_372_036_854_775_807\n\nconst MIN_SECS: Int = -MAX_SECS\n\nconst MIN_MILLIS: Int = -MAX_MILLIS\n\nconst MIN_MICROS: Int = -MAX_MICROS\n\n// One further from zero than `-MAX_NANOS`: `Int` is two\'s-complement, so its\n// negative range holds one more value than its positive range, and this is\n// that value -- the most-negative `Int` representable, computed rather than\n// spelled out because the positive literal one larger than `MAX_NANOS` would\n// itself overflow.\nconst MIN_NANOS: Int = -MAX_NANOS - 1\n\nimpl Instant {\n // The current reading of the monotonic clock.\n /// Returns the current reading of the monotonic clock.\n pub fn now() -> Instant { Instant { nanos: time_now_nanos() } }\n\n // How long since `self`. Zero if `self` is somehow in the future.\n /// Returns the time that has passed since `self` was read.\n ///\n /// It is never negative.\n pub fn elapsed(self) -> Duration { Instant::now().duration_since(self) }\n\n // The span from `earlier` to `self`, **saturating at zero** so an elapsed\n // time can never come back negative when the arguments are the wrong way\n // round.\n /// Returns the time from `earlier` to `self`.\n ///\n /// Saturates at zero: if `earlier` is in fact the later reading, the\n /// result is a zero duration, never a negative one.\n pub fn duration_since(self, earlier: Instant) -> Duration {\n if self.nanos <= earlier.nanos {\n Duration { nanos: 0 }\n } else {\n Duration { nanos: self.nanos - earlier.nanos }\n }\n }\n}\n\nimpl Duration {\n // Whole seconds. Clamps above `MAX_SECS` and below `MIN_SECS`; see the\n // constants above.\n /// Returns a duration of `s` seconds.\n ///\n /// Saturates: an `s` above 9,223,372,036 gives the longest `Duration`, and\n /// one below -9,223,372,036 the most negative.\n pub fn from_secs(s: Int) -> Duration {\n if s > MAX_SECS {\n Duration { nanos: MAX_NANOS }\n } else if s < MIN_SECS {\n Duration { nanos: MIN_NANOS }\n } else {\n Duration { nanos: s * NANOS_PER_SEC }\n }\n }\n\n // Whole milliseconds. Clamps above `MAX_MILLIS` and below `MIN_MILLIS`.\n /// Returns a duration of `ms` milliseconds.\n ///\n /// Saturates: an `ms` above 9,223,372,036,854 gives the longest\n /// `Duration`, and one below -9,223,372,036,854 the most negative.\n pub fn from_millis(ms: Int) -> Duration {\n if ms > MAX_MILLIS {\n Duration { nanos: MAX_NANOS }\n } else if ms < MIN_MILLIS {\n Duration { nanos: MIN_NANOS }\n } else {\n Duration { nanos: ms * NANOS_PER_MILLI }\n }\n }\n\n // Whole microseconds. Clamps above `MAX_MICROS` and below `MIN_MICROS`.\n /// Returns a duration of `us` microseconds.\n ///\n /// Saturates: a `us` above 9,223,372,036,854,775 gives the longest\n /// `Duration`, and one below -9,223,372,036,854,775 the most negative.\n pub fn from_micros(us: Int) -> Duration {\n if us > MAX_MICROS {\n Duration { nanos: MAX_NANOS }\n } else if us < MIN_MICROS {\n Duration { nanos: MIN_NANOS }\n } else {\n Duration { nanos: us * NANOS_PER_MICRO }\n }\n }\n\n // Whole seconds, truncating. `from_millis(1500).as_secs()` is `1`.\n /// Returns the duration in whole seconds, rounded toward zero: 1.5 seconds\n /// gives 1, and -1.5 seconds gives -1.\n pub fn as_secs(self) -> Int { self.nanos / NANOS_PER_SEC }\n\n // Whole milliseconds, truncating. `from_micros(1).as_millis()` is `0`,\n // which `tests/runtime/time_conversions.nova` pins deliberately.\n /// Returns the duration in whole milliseconds, rounded toward zero: 1,999\n /// microseconds give 1, and -1,999 microseconds give -1.\n pub fn as_millis(self) -> Int { self.nanos / NANOS_PER_MILLI }\n}\n\n// Suspend the current task for at least `d`.\n//\n// Parks rather than spins: this registers a deadline with the executor and is\n// not polled again until that deadline passes, so a sleeping task costs no\n// polls at all. Contrast `std/task`\'s `yield_now`, which is re-queued\n// immediately and deliberately.\n//\n// \"At least\" is exact, and platform-dependent in its granularity only. The\n// executor waits with microsecond granularity on Unix and whole milliseconds\n// on Windows, truncating a remaining duration the ordinary way and lifting\n// only a result that truncated all the way to zero back up to one unit --\n// never down to zero, not always up. So a sub-millisecond sleep on Windows\n// takes about a millisecond, but a 1.5ms remainder there becomes 1ms, not\n// 2ms. What makes \"at least\" hold regardless is the executor\'s own wake\n// check: a task parked on a deadline is only woken once that deadline is\n// `<=` the clock reading it is checked against, so a platform wait that\n// returns early just re-parks on the same deadline instead of waking the\n// sleeper ahead of schedule. A non-positive `d` wakes immediately rather\n// than being rejected.\n/// Suspends the calling task for at least `d`.\n///\n/// An `async fn`: other tasks run meanwhile, and a sleeping task uses no\n/// processor time. It can wake a little after `d` has passed, never before. A\n/// zero or negative `d` returns at once without suspending, so no other task\n/// gets a turn; `yield_now` gives them one.\npub async fn sleep(d: Duration) { task_sleep_future_nanos(d.nanos).await }\n\n// What `task_timeout_future` reports when the inner future finished first.\nconst TIMEOUT_COMPLETED: Int = 0\n\n// The error `timeout` reports when `d` elapsed before the inner future\n// completed.\n//\n// Carries nothing, and needs to: the caller passed `d` in, so it already has\n// the only fact there is. It is the one record in this library with no field\n// to disclose as un-enforced, because it has no field.\n/// The error `timeout` returns when its time runs out before the future\n/// finishes. It holds nothing.\npub record TimeoutError {}\n\n// Run `fut`, giving up after `d`.\n//\n// Returns `Ok` with the future\'s value if it completed within `d`, and\n// `Err(TimeoutError {})` if `d` elapsed first. A non-positive `d` still polls\n// `fut` once, so an already-complete future succeeds rather than reporting a\n// timeout it never had a chance to beat.\n//\n// **On timeout the inner future is ABANDONED, not cancelled.** Nothing tells\n// it that it was dropped. That is free for a `sleep` (its state is collected),\n// for a `join` (the joined task runs on independently), and for a `read` or\n// `write` (the caller still holds the `TcpStream` and can close it). It is\n// **not** free for `std/net`\'s `connect`: a timed-out connect leaves a socket\n// registered that nothing can reach or close, and it leaks until the process\n// exits -- which is what `docs/adr/0012-file-descriptor-lifecycle.md` already\n// says of any descriptor nobody closes.\n/// Runs `fut` in the calling task, giving up once `d` has passed.\n///\n/// An `async fn`: it suspends the calling task while `fut` waits, and other\n/// tasks run meanwhile. Returns `Ok` with `fut`\'s value if it finishes in\n/// time, or `Err(TimeoutError {})` if `d` passes first. `fut` runs before the\n/// time is checked, so even with a zero or negative `d`, a future that\n/// finishes without waiting gives `Ok`. The time is checked only when `fut`\n/// waits: a future that computes past `d` without awaiting anything still\n/// gives `Ok`.\n///\n/// On a timeout, `fut` is abandoned, not cancelled: it is never resumed, and\n/// nothing tells it so. A task it was joining runs on, and a stream it was\n/// reading or writing stays open, but a `connect` it was making leaves its\n/// socket open until the program exits. Today, a future that waits by\n/// yielding and trying again, as `std/sync`\'s `lock`, `send` and `recv` and\n/// `yield_now` do, is tried again only once `d` has passed, so under\n/// `timeout` it takes all of `d` even when it could have finished sooner.\n///\n/// One future in time, one too slow:\n///\n/// ```\n/// async fn answer_after(ms: Int) -> Int {\n/// sleep(Duration::from_millis(ms)).await\n/// 42\n/// }\n///\n/// async fn main() {\n/// // In time: `Ok` with the future\'s value.\n/// let fast = timeout(Duration::from_secs(5), answer_after(1)).await\n/// assert_eq(fast.unwrap_or(0), 42)\n///\n/// // Too slow: `Err(TimeoutError {})`. The future is abandoned, not\n/// // cancelled, and nothing waits for it to finish.\n/// let late = timeout(Duration::from_millis(10), answer_after(5000)).await\n/// assert(late.is_err(), \"10 ms passed before the answer came\")\n/// }\n/// ```\npub async fn timeout<T>(d: Duration, fut: Future<T>) -> Result<T, TimeoutError> {\n if task_timeout_future(d.nanos, fut).await == TIMEOUT_COMPLETED {\n Ok(task_output(fut))\n } else {\n Err(TimeoutError {})\n }\n}\n\n// Days since 1970-01-01 to [year, month, day], by Howard Hinnant\'s\n// civil_from_days. Shifting the era to start in March makes leap-day\n// handling fall out of integer arithmetic with no conditionals and no\n// table: February becomes the last month of the previous shifted year, so\n// its variable length never lands mid-sequence.\nfn civil_from_days(days: Int) -> [Int] {\n let z = days + 719_468\n let era = z / 146_097\n let doe = z - era * 146_097\n let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365\n let y = yoe + era * 400\n let doy = doe - (365 * yoe + yoe / 4 - yoe / 100)\n let mp = (5 * doy + 2) / 153\n let d = doy - (153 * mp + 2) / 5 + 1\n let m = if mp < 10 { mp + 3 } else { mp - 9 }\n let year = if m <= 2 { y + 1 } else { y }\n // Bound to `ymd` and returned on its own line, not written as a bare\n // `[year, m, d]` right after the `let year = ...` above: Nova has no\n // statement separator at all, so a postfix operator continues onto\n // whatever precedes it regardless of that statement\'s terminal token --\n // a bare integer literal, a bare identifier and a call ending in `)` all\n // fail the same way a `}`-closed expression does. Only a `let`/`return`\n // prefix breaks the continuation. Measured here as\n // `error[P0001]: expected \']\' (in index expression), found \',\'`.\n let ymd = [year, m, d]\n ymd\n}\n\n// A wall-clock reading: nanoseconds since the Unix epoch.\n//\n// Deliberately a separate type from `Instant`, not a method on it.\n// `Instant`\'s whole contract is that it is monotonic and comparable by\n// subtraction within one process; a wall clock is neither, and can jump\n// backwards when NTP corrects it. Two types that cannot be mistaken for\n// one another is the point.\n//\n// `nanos` is not privacy-enforced either, so `SystemTime { nanos: -1 }`\n// compiles and walks straight past the clamp at zero that `now()`\'s\n// intrinsic applies (`crates/nova-runtime/src/time.rs`). `now()` is the\n// intended and only supported way to obtain one. A negative `nanos` does\n// not mean the wrong instant so much as a nonsensical one: Nova\'s `/`\n// truncates toward zero and `%` follows the dividend\'s sign (measured:\n// `-7 / 2 = -3`, `-7 % 2 = -1`), so a negative `nanos` leaves `rem` in\n// `to_iso8601` negative and every field derived from it with it. A small\n// negative `nanos` (e.g. `-1`) still renders a well-formed 24-character\n// string, `1970-01-01T00:00:00.000Z` -- byte-identical to `nanos: 0`, the\n// sign silently lost. Only once a field\'s magnitude is large enough that\n// `Int::pad`\'s sign digit eats the padding budget does the width itself\n// break: `nanos: -43_200_000_000_000` (a half day) renders\n// `1970-01-01T-12:00:00.000Z`, 25 characters, not 24.\n/// A reading of the wall clock, from `SystemTime::now`: the time since\n/// 1970-01-01 00:00:00 UTC, in nanoseconds.\n///\n/// The wall clock can jump forwards or backwards when the system\'s time is\n/// set, so measure how much time passes with `Instant` instead. `to_iso8601`\n/// renders a reading as text.\npub record SystemTime { nanos: Int }\n\nimpl SystemTime {\n // ISO-8601 UTC to milliseconds: `2026-08-19T02:40:13.123Z`. Fixed\n // width, so log lines align with no padding logic in the caller.\n //\n // UTC only, permanently: `00-MASTER-SPEC.md` \u{a7}6\'s crate list is FINAL\n // and has no date/time crate, so there is no timezone database to\n // consult and a local-time offset would be a guess that is wrong twice\n // a year in every DST zone.\n /// Returns the time as ISO 8601 text in UTC, to the millisecond, such as\n /// `2026-08-19T02:40:13.123Z`.\n ///\n /// The text is 24 characters long for any reading from `now`. Fractions\n /// of a millisecond are dropped, not rounded, and there is no local time\n /// zone.\n pub fn to_iso8601(self) -> String {\n let nanos_per_day = 86_400_000_000_000\n let days = self.nanos / nanos_per_day\n let rem = self.nanos % nanos_per_day\n let ymd = civil_from_days(days)\n let hour = rem / 3_600_000_000_000\n let minute = rem / 60_000_000_000 % 60\n let second = rem / 1_000_000_000 % 60\n let milli = rem / 1_000_000 % 1_000\n \"${ymd[0]}-${ymd[1].pad(2)}-${ymd[2].pad(2)}T${hour.pad(2)}:${minute.pad(2)}:${second.pad(2)}.${milli.pad(3)}Z\"\n }\n\n // The live wall clock: nanoseconds since the Unix epoch, read from the\n // OS at call time.\n /// Returns the current wall-clock time.\n ///\n /// A system clock set before 1970 reads as 1970-01-01T00:00:00.000Z, and\n /// one set past April 2262, beyond what an `Int` of nanoseconds holds,\n /// reads as that limit.\n pub fn now() -> SystemTime { SystemTime { nanos: time_now_epoch_nanos() } }\n}\n";Expand description
std/time/lib.nova.