RfcDuration

RfcDuration :: # (opaque)

Positive RFC 5545 duration values for events and PERIOD starts. Profile rfc5545-positive-duration-v1 implements section 3.3.6's week, day and hour/minute/second grammar, including optional plus and ASCII case. Calendar days remain distinct from coordinate seconds; leap seconds are not counted. Years, months, fractions, lists and content lines are malformed. Negative and zero durations return NonPositive; alarm durations are outside this positive profile. Source spelling is not retained.

Parsing visits at most 256 bytes. Days and the coordinate microsecond tail must each fit nonnegative I64. Interpretation may still exceed calendar, boundary or provider ranges. No rules are consulted during parsing.

import time.RfcDuration
expect {
    value = RfcDuration.parse("P15DT5H0M20S")?
    RfcDuration.components(value) == { days: 15.I64, seconds: 18020.I64 }
}
parser_for : encoding -> (state -> Try({ value : RfcDuration, rest : state }, [InvalidRfcDuration(Error), Encoding(err), ..]))
    where [
        encoding.parse_str : encoding, state -> Try({ value : Str, rest : state }, err),
    ]

Generic encodings carry canonical text, never the opaque backing record. Encoding failures remain distinct from this profile's validation errors. The encoding owns framing and its work limits; parse bounds the decoded text.

encoder_for : encoding -> (RfcDuration, state -> Try(state, err))
    where [
        encoding.encode_str : Str, state -> Try(state, err),
    ]
from_quote : Str -> Try(RfcDuration, [BadQuotedBytes(Str)])

Typed quoted literals use the same checked profile at compile time. Runtime interpolation remains Str followed by an explicit parse call.

is_eq : RfcDuration, RfcDuration -> Bool

Equality compares nominal days and coordinate seconds, without an anchor.

to_duration : RfcDuration -> Duration

Lower into the shared appointment engine. Days advance the original source before the coordinate tail. The RFC timed policy selects the first fold occurrence and uses the offset before a gap (erratum 4271).

to_text : RfcDuration -> Str

Canonical semantic value text, not a versioned persistence envelope. Weeks become days; accurate time components become seconds.

fact_count : RfcDuration -> U64

Calendar days and accurate seconds remain separate components, without an invented anchor or conversion of calendar days to elapsed seconds.

fact_at : RfcDuration, U64 -> [End, Item(SemanticFact)]
Error : [Malformed, NonPositive, OutOfRange, TooLarge]