Capítulo 16 20 min de leitura

Tempo

Utilitários de relógio de parede e relógio monotônico em Glide puro, sobre libc / WinAPI. O módulo expõe dois pequenos tipos de valor — Time (um instante) e Duration (um span em nanosegundos com sinal) — mais um Stopwatch monotônico, parsing RFC 3339, os enums Weekday/Month e canais de timer amigáveis a corrotinas (after, tick).

Tanto Time quanto Duration são structs pequenos copiados por valor — sem heap, sem .free(). Os componentes civis (ano, mês, dia, dia da semana, …) são calculados sob demanda, então os structs permanecem enxutos (16 / 8 bytes).

Importação

import stdlib::time::*;

Constantes

Fatores de conversão, todos i64 salvo indicação. Úteis quando você precisa de uma contagem bruta em vez de construir uma Duration.

Constante Tipo Valor
NANOS_PER_MICRO i64 1000
NANOS_PER_MILLI i64 1000000
NANOS_PER_SEC i64 1000000000
NANOS_PER_MIN i64 60000000000
NANOS_PER_HOUR i64 3600000000000
NANOS_PER_DAY i64 86400000000000
MICROS_PER_SEC i64 1000000
MILLIS_PER_SEC i64 1000
SECS_PER_MIN i64 60
SECS_PER_HOUR i64 3600
SECS_PER_DAY i64 86400
MINS_PER_HOUR i32 60
HOURS_PER_DAY i32 24
DAYS_PER_WEEK i32 7

Duration

pub struct Duration {
    !nanos: i64,
}

Uma Duration é um span exato em nanosegundos (sem aritmética de meses/anos). O intervalo é de aproximadamente ±292 anos. Os construtores from_* criam uma Duration; a família as_* a lê de volta (truncando qualquer fração mais fina); os métodos aritméticos retornam novos valores de Duration.

Construtores

Função Assinatura Descrição
zero Duration::zero() -> Duration Span zero (semente para somas).
from_nanos Duration::from_nanos(n: i64) -> Duration n nanossegundos.
from_micros Duration::from_micros(n: i64) -> Duration n microssegundos.
from_millis Duration::from_millis(n: i64) -> Duration n milissegundos.
from_secs Duration::from_secs(n: i64) -> Duration n segundos.
from_minutes Duration::from_minutes(n: i64) -> Duration n minutos.
from_hours Duration::from_hours(n: i64) -> Duration n horas.
from_days Duration::from_days(n: i64) -> Duration n dias (24×60×60 s — sem ajuste de horário de verão).

Conversões

Cada uma trunca em direção ao zero (descarta o resto sub-unitário).

Método Assinatura Descrição
as_nanos as_nanos(self: Duration) -> i64 Total de nanossegundos (o campo subjacente).
as_micros as_micros(self: Duration) -> i64 Total de microssegundos.
as_millis as_millis(self: Duration) -> i64 Total de milissegundos.
as_secs as_secs(self: Duration) -> i64 Total de segundos.
as_minutes as_minutes(self: Duration) -> i64 Total de minutos.
as_hours as_hours(self: Duration) -> i64 Total de horas.
as_days as_days(self: Duration) -> i64 Total de dias.

Aritmética

Método Assinatura Descrição
add add(self: Duration, other: Duration) -> Duration self + other.
sub sub(self: Duration, other: Duration) -> Duration self - other (pode ser negativo).
mul mul(self: Duration, factor: i64) -> Duration self * factor.
neg neg(self: Duration) -> Duration -self.

Predicados e comparações

Método Assinatura Descrição
is_zero is_zero(self: Duration) -> bool true para um span zero.
is_positive is_positive(self: Duration) -> bool true se estritamente positivo.
is_negative is_negative(self: Duration) -> bool true se estritamente negativo.
before before(self: Duration, other: Duration) -> bool self < other.
after after(self: Duration, other: Duration) -> bool self > other.
equal equal(self: Duration, other: Duration) -> bool self == other.

Renderização

pub fn to_string(self: Duration) -> string

Renderiza como <H>h <M>m <S>s (fração sub-segundo acrescentada como .<cs>). Sempre com magnitude positiva; uma duração negativa recebe um - na frente. Horas/minutos são omitidos quando zero.

Duration::from_secs(3725).to_string();   // "1h 2m 5s"
Duration::from_millis(1500).to_string(); // "1.50s"

Exemplo — construir, converter, comparar

import stdlib::time::*;

fn main() -> i32 {
    let d: Duration = Duration::from_hours(2).add(Duration::from_minutes(30));
    println!(d.to_string());            // "2h 30m 0s"
    println!(d.as_minutes());           // 150
    println!(d.as_millis());            // 9000000

    let a: Duration = Duration::from_secs(90);
    println!(a.is_positive());          // true
    println!(a.before(d));              // true
    println!(a.sub(Duration::from_secs(30)).as_secs());  // 60
    println!(Duration::zero().is_zero());

    let neg: Duration = Duration::from_secs(5).neg();
    println!(neg.is_negative());        // true
    return 0;
}

Exemplo — escalar, somar, renderizar casos extremos

mul escala um span; some uma lista dobrando add sobre Duration::zero(). As conversões truncam em direção ao zero, e um span negativo mantém sua magnitude com um - à esquerda.

import stdlib::time::*;

fn main() -> i32 {
    let half_day: Duration = Duration::from_hours(12);
    let day: Duration = half_day.mul(2);
    println!(day.as_hours());                 // 24

    // Fold a running total from the zero() seed.
    let mut total: Duration = Duration::zero();
    total = total.add(Duration::from_minutes(90));
    total = total.add(Duration::from_secs(30));
    println!(total.to_string());              // "1h 30m 30s"
    println!(total.as_secs());                // 5430

    // Conversions truncate toward zero; to_string shows centiseconds.
    let d: Duration = Duration::from_millis(1500);
    println!(d.as_secs());                    // 1  (drops .5)
    println!(d.to_string());                  // "1.50s"

    // Negative span: sign moves to the prefix.
    let back: Duration = Duration::from_secs(5).neg();
    println!(back.to_string());               // "-5s"
    println!(back.is_negative());             // true

    // Comparisons treat equal magnitudes as equal regardless of unit.
    println!(Duration::from_secs(60).equal(Duration::from_minutes(1)));  // true
    println!(Duration::from_secs(30).before(Duration::from_minutes(1))); // true
    println!(Duration::from_hours(2).after(Duration::from_minutes(90))); // true
    return 0;
}

Time

pub struct Time {
    !unix_nanos: i64,
    !tz_offset_min: i32,
}

Time é um instante: o unix_nanos subjacente está sempre em UTC, e tz_offset_min (minutos a leste de UTC) apenas desloca a forma como os acessores de componentes civis (year, hour, …) e os formatadores o renderizam. As comparações (before/after/equal) operam sobre o instante e ignoram o offset.

Construtores

Função Assinatura Descrição
epoch Time::epoch() -> Time 1970-01-01T00:00:00Z (sentinela).
now Time::now() -> Time Hora atual do relógio de parede no fuso horário local do sistema.
now_utc Time::now_utc() -> Time Hora atual do relógio de parede em UTC (tz_offset == 0).
from_unix Time::from_unix(secs: i64) -> Time Tempo UTC a partir de um timestamp em segundos Unix.
from_unix_millis Time::from_unix_millis(ms: i64) -> Time Tempo UTC a partir de milissegundos Unix.
from_unix_nanos Time::from_unix_nanos(ns: i64) -> Time Tempo UTC a partir de nanossegundos Unix (maior precisão).
from_date Time::from_date(year: i32, month: i32, day: i32) -> Time Meia-noite UTC na data civil especificada.
from_datetime Time::from_datetime(year: i32, month: i32, day: i32, hour: i32, minute: i32, second: i32) -> Time Tempo UTC a partir de componentes civis completos.

Acessores brutos

Método Assinatura Descrição
unix unix(self: Time) -> i64 Segundos desde o epoch Unix.
unix_millis unix_millis(self: Time) -> i64 Milissegundos desde o epoch.
unix_micros unix_micros(self: Time) -> i64 Microssegundos desde o epoch.
unix_nanos unix_nanos(self: Time) -> i64 Nanossegundos desde o epoch (o campo subjacente).
tz_offset tz_offset(self: Time) -> i32 Offset de fuso horário em minutos a leste de UTC (0 para qualquer tempo UTC).

Componentes civis

Calculados no fuso horário próprio do instante.

Método Assinatura Descrição
year year(self: Time) -> i32 Ano civil (negativo para AEC).
month_int month_int(self: Time) -> i32 Mês como 1..12.
month month(self: Time) -> Month Mês como enum Month tipado.
day day(self: Time) -> i32 Dia do mês 1..31.
weekday weekday(self: Time) -> Weekday Dia da semana (enum com domingo como primeiro).
hour hour(self: Time) -> i32 0..23.
minute minute(self: Time) -> i32 0..59.
second second(self: Time) -> i32 0..59 (60 somente via parse de segundo intercalar).
nanosecond nanosecond(self: Time) -> i32 Componente sub-segundo 0..999_999_999.
day_of_year day_of_year(self: Time) -> i32 1..366.
is_leap_year is_leap_year(self: Time) -> bool true em ano bissexto.

Consultas (comparações)

Método Assinatura Descrição
before before(self: Time, other: Time) -> bool self é estritamente anterior.
after after(self: Time, other: Time) -> bool self é estritamente posterior.
equal equal(self: Time, other: Time) -> bool Mesmo instante (offset ignorado).

Aritmética

Método Assinatura Descrição
add add(self: Time, d: Duration) -> Time Adiciona uma Duration; offset de fuso preservado.
sub sub(self: Time, d: Duration) -> Time Subtrai uma Duration; offset de fuso preservado.
diff diff(self: Time, other: Time) -> Duration self - other como uma Duration (pode ser negativa).
truncate truncate(self: Time, d: Duration) -> Time Arredonda para baixo ao múltiplo de d (ancorado no epoch, UTC). Duration::zero() é uma operação sem efeito.
round round(self: Time, d: Duration) -> Time Arredonda para o múltiplo de d mais próximo (empates arredondam para cima).

Transformações de fuso horário

Método Assinatura Descrição
to_utc to_utc(self: Time) -> Time Mesmo instante visto em UTC (offset descartado).
with_tz with_tz(self: Time, offset_min: i32) -> Time Mesmo instante renderizado com offset_min minutos a leste de UTC.

Formatação

Método Assinatura Descrição
date_string date_string(self: Time) -> string YYYY-MM-DD.
time_string time_string(self: Time) -> string HH:MM:SS.
rfc3339 rfc3339(self: Time) -> string RFC 3339 / ISO 8601 (...Z ou ...+02:00).
to_string to_string(self: Time) -> string Renderização padrão = rfc3339().
format format(self: Time, spec: string) -> string Renderiza com uma especificação de placeholders (veja abaixo).

Placeholders de format (correspondência pelo mais longo primeiro; qualquer outro caractere é copiado literalmente):

Token Significado Exemplo
YYYY Ano com 4 dígitos 2024
YY Ano com 2 dígitos 24
MMMM Nome completo do mês January
MMM Nome abreviado do mês Jan
MM Mês com 2 dígitos 01..12
DD Dia com 2 dígitos 01..31
HH Hora com 2 dígitos 00..23
mm Minuto com 2 dígitos 00..59
ss Segundo com 2 dígitos 00..59
Day Nome completo do dia da semana Monday
Dy Nome abreviado do dia da semana Mon

Parsing

Método Assinatura Descrição
parse_rfc3339 Time::parse_rfc3339(s: string) -> !Time Faz o parse de 2024-01-15T14:23:45Z / ...+02:00 / ...-05:30.
parse_date Time::parse_date(s: string) -> !Time Faz o parse de uma data simples YYYY-MM-DD como meia-noite UTC.

Ambos retornam !Time: leia .ok/.err/.val, ou propagate com ?. Segundos fracionários e variantes em minúsculas ainda não são aceitos por parse_rfc3339.

Exemplo — construir, inspecionar, formatar, comparar

import stdlib::time::*;

fn main() -> i32 {
    let t: Time = Time::from_datetime(2026, 5, 8, 14, 23, 45);
    println!(t.rfc3339());              // 2026-05-08T14:23:45Z
    println!(t.date_string());          // 2026-05-08
    println!(t.time_string());          // 14:23:45
    println!(t.format("Day, MMMM DD")); // Friday, May 08

    println!(t.year());                 // 2026
    println!(t.month().short_name());   // May
    println!(t.weekday().name());       // Friday
    println!(t.day_of_year());
    println!(t.is_leap_year());

    let a: Time = Time::from_date(2026, 1, 1);
    let b: Time = Time::from_date(2026, 5, 8);
    println!(a.before(b));              // true
    println!(b.diff(a).as_days());      // days between

    let later: Time = t.add(Duration::from_hours(6));
    println!(later.rfc3339());
    return 0;
}

Exemplo — componentes civis e round-trips Unix

Cada acessor de componente é calculado sob demanda a partir de unix_nanos no fuso horário próprio do instante. Os construtores from_unix* e os acessores unix* são inversas exatas em suas respectivas precisões.

import stdlib::time::*;

fn main() -> i32 {
    let t: Time = Time::from_datetime(2024, 2, 29, 23, 59, 59);
    println!(t.year(), t.month_int(), t.day());   // 2024 2 29
    println!(t.hour(), t.minute(), t.second());   // 23 59 59
    println!(t.is_leap_year());                   // true (2024)
    println!(t.day_of_year());                    // 60
    println!(t.weekday().name());                 // Thursday

    let u: Time = Time::from_unix(1735689600);    // 2025-01-01T00:00:00Z
    println!(u.rfc3339());
    println!(u.unix());                            // 1735689600
    println!(u.unix_millis());                     // 1735689600000
    println!(Time::from_unix_millis(u.unix_millis()).equal(u));  // true

    // Sub-second precision survives from_unix_nanos.
    let p: Time = Time::from_unix_nanos(1500000000_500000000);
    println!(p.nanosecond());                      // 500000000
    return 0;
}

Exemplo — add/sub, diff, truncate e round

add/sub são exatos e preservam o offset de fuso; diff produz uma Duration com sinal. truncate arredonda para baixo ao múltiplo de d, round para o mais próximo (empates arredondam para cima). Ambos ancoram no epoch Unix em UTC e tratam Duration::zero() como operação sem efeito.

import stdlib::time::*;

fn main() -> i32 {
    let t: Time = Time::from_datetime(2026, 5, 8, 14, 37, 50);

    let later: Time = t.add(Duration::from_hours(2));
    println!(later.time_string());            // 16:37:50
    let earlier: Time = t.sub(Duration::from_minutes(37).add(Duration::from_secs(50)));
    println!(earlier.time_string());          // 14:00:00

    let d: Duration = later.diff(t);
    println!(d.as_hours());                   // 2
    println!(t.diff(later).is_negative());    // true

    println!(t.truncate(Duration::from_hours(1)).time_string());   // 14:00:00
    println!(t.round(Duration::from_hours(1)).time_string());      // 15:00:00 (37m rounds up)
    println!(t.round(Duration::from_minutes(15)).time_string());   // 14:45:00
    println!(t.truncate(Duration::zero()).equal(t));               // true
    return 0;
}

Exemplo — parsing e fusos horários

import stdlib::time::*;

fn main() -> i32 {
    let r: !Time = Time::parse_rfc3339("2024-03-21T18:45:30+02:00");
    if r.ok {
        let t: Time = r.val;
        println!("unix:", t.unix());
        println!(t.tz_offset());        // 120
    } else {
        println!("parse failed:", r.err);
    }

    let dr: !Time = Time::parse_date("2026-05-08");
    if dr.ok { println!(dr.val.rfc3339()); }

    let utc: Time = Time::from_datetime(2026, 5, 8, 12, 0, 0);
    println!(utc.with_tz(540).rfc3339()); // ...+09:00 (JST)
    println!(utc.to_utc().tz_offset());   // 0
    return 0;
}

Exemplo — propagação, fallback com ?? e a armadilha dos segundos fracionários

parse_rfc3339/parse_date retornam !Time, então se compõem com ? (propagate o err) e ?? (substitui um fallback). O parser é estrito: rejeita segundos fracionários, t/z minúsculos e quaisquer caracteres extras ao final.

import stdlib::time::*;

fn round_trip(s: string) -> !string {
    let t: Time = Time::parse_rfc3339(s)?;   // propagate err on failure
    return ok(t.rfc3339());
}

fn main() -> i32 {
    let r: !string = round_trip("2024-03-21T18:45:30+02:00");
    if r.ok { println!(r.val); }              // 2024-03-21T18:45:30+02:00

    // Fractional seconds are NOT accepted yet -> err.
    let bad: !Time = Time::parse_rfc3339("2024-03-21T18:45:30.5Z");
    println!(bad.ok);                         // false
    println!(bad.err);                        // rfc3339: expected Z or ±HH:MM

    let dr: !Time = Time::parse_date("2026-05-08");
    if dr.ok { println!(dr.val.rfc3339()); }  // 2026-05-08T00:00:00Z

    // ?? supplies a fallback instant when the parse fails.
    let parsed: Time = Time::parse_rfc3339("nope") ?? Time::epoch();
    println!(parsed.unix());                  // 0
    return 0;
}

Weekday

pub enum Weekday {
    Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday,
}
Item Assinatura Descrição
from_int Weekday::from_int(n: i32) -> Weekday Converte número em enum (Sunday=0, Saturday=6). Valores fora do intervalo são ajustados com módulo euclidiano.
name name(self: Weekday) -> string Nome completo em inglês ("Friday").
short_name short_name(self: Weekday) -> string Abreviação de 3 letras ("Fri").

Month

pub enum Month {
    January, February, March, April, May, June,
    July, August, September, October, November, December,
}
Item Assinatura Descrição
from_int Month::from_int(n: i32) -> Month Converte 1..12 em enum; valores fora do intervalo são limitados à borda.
to_int to_int(self: Month) -> i32 Convencional 1..12.
name name(self: Month) -> string Nome completo em inglês ("May").
short_name short_name(self: Month) -> string Abreviação de 3 letras ("May").
days days(self: Month, year: i32) -> i32 Dias neste mês para o ano especificado (fevereiro ciente de bissexto).

Exemplo — enums

import stdlib::time::*;

fn main() -> i32 {
    println!(Weekday::from_int(5).short_name());   // Fri
    println!(Weekday::Monday().name());            // Monday
    println!(Month::from_int(5).short_name());     // May
    println!(Month::December().to_int());          // 12
    println!(Month::February().days(2024));        // 29 (leap)
    println!(Month::February().days(2025));        // 28
    return 0;
}

Stopwatch

pub struct Stopwatch {
    !start_ns: i64,
}

Um cronômetro de relógio monotônico. Lê o relógio monotônico do runtime (não afetado por mudanças no relógio de parede) ao chamar start/reset e reporta uma Duration decorrida sob demanda.

Método Assinatura Descrição
start Stopwatch::start() -> Stopwatch Inicia; lê o relógio monotônico imediatamente.
elapsed elapsed(self: Stopwatch) -> Duration Tempo desde o último start/reset. O cronômetro continua rodando, portanto chamadas repetidas são cumulativas.
reset reset(self: *Stopwatch) -> Duration Reinicia o relógio; retorna a Duration decorrida até o ponto do reset.

Exemplo — tempo total decorrido vs. reset por volta

import stdlib::time::*;

fn work() {
    let mut x: i64 = 0;
    for let i: i32 = 0; i < 10000; i++ { x = x + (i as i64); }
}

fn main() -> i32 {
    // elapsed() is cumulative — the clock keeps running.
    let sw: Stopwatch = Stopwatch::start();
    work();
    let t1: Duration = sw.elapsed();
    work();
    let t2: Duration = sw.elapsed();   // total since start, not since t1
    println!(t1.as_nanos() <= t2.as_nanos());   // true
    println!("elapsed:", t2.to_string());

    // reset() mutates in place — call it via &mut and chain laps.
    let mut lap: Stopwatch = Stopwatch::start();
    work();  let a: Duration = (&mut lap).reset();
    work();  let b: Duration = (&mut lap).reset();
    println!("lap a:", a.as_nanos(), "lap b:", b.as_nanos());
    return 0;
}

Canais de timer

Função Assinatura Descrição
after after(d: Duration) -> chan<i64> Inicializa uma corrotina que dorme por d e então envia now_ns() uma vez no canal retornado. Capacidade 1.
tick tick(d: Duration) -> chan<i64> Inicializa uma corrotina que emite now_ns() no canal retornado a cada d, indefinidamente (sem stop() ainda). Capacidade 4, intervalo mínimo de 1 ms.

after é o sentinela de timeout: dispute-o com seu trabalho em um select!. tick serve para tickers com vida de processo (heartbeats, flushes periódicos).

Os canais carregam now_ns() (o timestamp monotônico no momento do disparo). Normalmente você não se importa com o valor — apenas que o canal ficou pronto.

Exemplo — timer bloqueante e ticker

import stdlib::time::*;

fn main() -> i32 {
    let timer: chan<i64> = after(Duration::from_millis(50));
    let fired_at: i64 = timer.recv();   // wakes ~50ms later
    println!("fired at", fired_at);

    let beat: chan<i64> = tick(Duration::from_millis(10));
    println!("tick", beat.recv());
    return 0;
}

Exemplo — timeout com after() + select!

O uso idiomático de after é como braço de deadline em um select!: vence o primeiro canal que ficar pronto. Inicialize seu trabalho em um canal, dispute-o contra o timer e ramifique com base no braço que disparou.

import stdlib::time::*;

fn _work(c: chan<i32>) {
    sleep_ms(5);
    c.send(42);
}

fn main() -> i32 {
    let work: chan<i32> = make_chan(1);
    spawn _work(work);

    let timeout: chan<i64> = after(Duration::from_millis(200));

    let mut result: i32 = -1;
    select! {
        v = work.recv()    => { result = v; }
        t = timeout.recv() => { result = -1; }
    }

    if result < 0 {
        println!("timed out");
    } else {
        println!("got", result);
    }
    return 0;
}

Exemplo — heartbeat periódico com tick()

tick dispara repetidamente. Multiplexe-o com seu canal de trabalho em um select! para intercalar ações periódicas (heartbeats, flushes de métricas) com o tratamento de requisições.

import stdlib::time::*;

fn _producer(c: chan<i32>) {
    sleep_ms(5);
    c.send(1);
    sleep_ms(5);
    c.send(2);
}

fn main() -> i32 {
    let work: chan<i32> = make_chan(2);
    spawn _producer(work);

    let beat: chan<i64> = tick(Duration::from_millis(1));

    let mut got: i32 = 0;
    let mut beats: i32 = 0;
    while got < 2 {
        select! {
            v = work.recv() => { got = v; }
            t = beat.recv() => { beats = beats + 1; }
        }
    }
    println!("done, received", got);
    return 0;
}

Funções auxiliares de relógio

Acesso direto ao relógio monotônico — use estas funções quando quiser um handle de i64 avulso em vez de um Stopwatch.

Função Assinatura Descrição
time_now_ns time_now_ns() -> i64 Timestamp monotônico em nanossegundos.
time_now_us time_now_us() -> i64 Timestamp monotônico em microssegundos.
time_now_ms time_now_ms() -> i64 Timestamp monotônico em milissegundos.
time_since_ns time_since_ns(start_ns: i64) -> i64 Nanossegundos decorridos desde start_ns (limitado a 0 se o relógio tiver regredido).
time_since_us time_since_us(start_ns: i64) -> i64 Microssegundos decorridos desde start_ns.
time_since_ms time_since_ms(start_ns: i64) -> i64 Milissegundos decorridos desde start_ns.
import stdlib::time::*;

fn work() {
    let mut x: i64 = 0;
    for let i: i32 = 0; i < 1000; i++ { x = x + (i as i64); }
}

fn main() -> i32 {
    let t0: i64 = time_now_ns();
    work();
    println!(time_since_ns(t0), "ns");
    println!(time_now_ms(), time_now_us());
    return 0;
}