EdtfDate

EdtfDate :: # (opaque)

EDTF date-only profile edtf-gregorian-date-v1: YYYY[-MM[-DD]][?~%]. Four ASCII year digits use Gregorian astronomical years 0000..9999. Each omitted component preserves reduced resolution. One trailing qualifier applies to Whole; it does not supply a tolerance or resolve a timezone. This subset is not EDTF Level 0 conformance, which also requires timestamps and intervals. No source spelling or native persistence format is promised.

Source: Library of Congress EDTF published specification, February 4, 2019, https://www.loc.gov/standards/datetime/ (accessed 2026-09-06), Level 0 Date and Level 1 Qualification of a date (complete). Unit fixtures below transcribe example meanings; expected fields are independent of parsing/serialization.

Parsing is bounded by 64 UTF-8 bytes. Valid partial grammar prefixes return Incomplete. Invalid fields/grammar return Malformed. Selected recognized excluded forms (negative year, long year, year-only mask) return UnsupportedForm; recognition does not validate every excluded EDTF feature. Native conversion checks calendar, resolution, year range and qualifier scope explicitly.

Generic codecs use canonical strings and typed literals validate at compile time:

import time.EdtfDate
expect {
    date : EdtfDate
    date = "1984?"
    parsed : Try({ date : EdtfDate }, [InvalidJson(Str), MissingRequiredField(Str), Encoding([InvalidJson(Str)]), InvalidEdtfDate(EdtfDate.Error)])
    parsed = Json.parse(Json.to_str({ date: date }))
    parsed == Ok({ date: date })
}
parser_for : encoding -> (state -> Try({ value : EdtfDate, rest : state }, [InvalidEdtfDate(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 -> (EdtfDate, state -> Try(state, err))
    where [
        encoding.encode_str : Str, state -> Try(state, err),
    ]
from_quote : Str -> Try(EdtfDate, [BadQuotedBytes(Str)])

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

fact_at : EdtfDate, U64 -> [End, Item(SemanticFact)]
Error : [Malformed, Incomplete, TooLarge, OutOfRange, UnsupportedForm, UnsupportedCalendar, UnsupportedResolution, UnsupportedQualification]