Library Interface
Status: informative. This document is a recommendation, not a conformance rule. Conformance is defined by the test vectors alone (spec §12) — an implementation that passes
vectors.jsonwith a completely different API is still a conforming DTRExp implementation. But if you are building one; start here. Users who learn one DTRExp library should know them all.
Why This Exists
Section titled “Why This Exists”An expression like T0900:1800 E1:5 means the same thing in every language. The code around it should too. When parsing, coverage checks and warnings are named the same everywhere, switching languages costs nothing but syntax:
// JavaScriptconst dtr = parse('T0900:1800 E1:5');dtr.covers('2026-07-07T10:00:00Z', { tz: 'Europe/Berlin' }); // —> true# Pythondtr = dtrexp.parse('T0900:1800 E1:5')dtr.covers(instant, tz='Europe/Berlin') # —> TrueSame vocabulary, same defaults, same mental model.
Naming: the Words Are Fixed, the Casing Is Yours
Section titled “Naming: the Words Are Fixed, the Casing Is Yours”An operation name in this document is a sequence of words — render it in your language’s convention:
| Convention | Languages (e.g.) | covers | covers in | to rrule |
|---|---|---|---|---|
| camelCase | JavaScript, Java, Swift | covers | coversIn | toRRule |
| PascalCase | Go, C# | Covers | CoversIn | ToRRule |
| snake_case | Python, Rust, Ruby | covers | covers_in | to_rrule |
The words never change; the rendering always follows the language. In other words; coversIn, CoversIn and covers_in are the same operation — while isCovered or matches would be a different vocabulary, which is exactly what this document exists to prevent.
Core (Tier 1)
Section titled “Core (Tier 1)”Every DTRExp library provides these four operations; everything about them is fixed except the rendering above.
parse(expression)
Section titled “parse(expression)”Parses the source text into an expression object.
- expression
String— Required. The DTRExp source text.
returns an Expression (or your language’s equivalent).
Fails with a positioned parse error on invalid input — a character position and a message, per the grammar and static rules of spec §§2–8. Note that parse failure is the only failure: a syntactically valid expression never fails later.
validate(expression)
Section titled “validate(expression)”Checks the source text without throwing. Never fails; typo-shaped input comes back as data.
- expression
String— Required. The DTRExp source text.
returns a result object with:
- valid
Boolean— whether the expression parses. - errors — positioned syntax errors. Empty when valid.
- warnings — positioned §9.1 unsatisfiability warnings. An expression can be valid and warned; that is the point of the distinction.
Expression.covers(instant [, zone])
Section titled “Expression.covers(instant [, zone])”The reason this format exists: “is this instant inside the set?”
- instant — Required. The language’s native instant type (
Date,datetime,time.Time,Instant, epoch milliseconds). - zone
String— Optional. An IANA time-zone identifier (e.g."Europe/Berlin"). Default:"UTC".
returns Boolean.
This is the hot path. Keep it to the §9 cost model: one field extraction, then integer comparisons — no date-object iteration.
Expression.warnings
Section titled “Expression.warnings”The §9.1 warnings of a parsed expression; property or method per your language’s idiom. Same content as validate().warnings — exposed on the instance so code that parses directly doesn’t lose them.
What May Vary — and What May Not
Section titled “What May Vary — and What May Not”Idiom wins on shape:
- Types follow the language:
datetimein Python,time.Timein Go,Instantin Java, epoch millis + a zone handle in Rust. - Failure follows the language: exceptions,
Result,(value, error)returns. - Preloaded zones: where zone lookup by identifier is costly or fallible, a variant named
covers in(rendered per convention:coversIn,CoversIn,covers_in) MAY take the language’s zone object instead of the IANA string. Where the language supports overloading, overloadingcoversitself is equally fine — then no extra name exists at all. - Extras are fine: a convenience that accepts ISO strings, extra overloads.
The vocabulary does not:
- The four operations —
parse,validate,covers,warnings— and nothing else for these jobs. NotisCovered, notcheck, notmatches. - The zone parameter of
coversis an IANA identifier and its default is UTC. Always both — though in overloading languages the identifier form and the UTC default may live on different overloads ofcovers(a zone-object overload carrying the default, a string overload taking the identifier); what must hold is that the no-zone call means UTC and the IANA-string form exists. - Errors and warnings carry a position.
Extended (Tier 2)
Section titled “Extended (Tier 2)”None of these affect conformance. But if you implement the operation, use the name — the reference implementation (dtrexp-js) is the model:
| Operation | Meaning |
|---|---|
next(after [, zone]) | First covered interval after an instant. |
intersect(a, b, window) | The covered intervals of two expressions, clipped to a finite window. |
describe([locale]) | Human-readable text of the expression. |
toRRule() | RFC 5545 export. Constrained cadences need RFC 7529 SKIP=BACKWARD — see spec §9.2. |
toString() | The canonical form of the expression (not necessarily the original source). Where the language gives every object a built-in string conversion (Java’s toString, Go’s String, Python’s __str__), it returns the source verbatim until the canonical operation is implemented — never a debug wrapper. |
Checklist for a New Implementation
Section titled “Checklist for a New Implementation”- Vendor
vectors.jsonverbatim; wire it as your test suite (spec §12). The vectors — not this document, not the prose — are the contract. - Implement Core with the names above.
- Add Extended operations as your users need them; keep the names.