dtrexp-rs
Rust implementation of DTRExp (read: "DTR Expression") — a compact string expression for date-time ranges and recurrence, evaluated by coverage rather than enumeration.
T0900:1800 E1:5 Mon–Fri, 09:00–18:00
E7#-1 M4 last Sunday of April, every year
20200106/10D every 10 days from 2020-01-06 (cron can't say this)
M!7 every month except July
Scope: parsing, validation and coverage evaluation (the spec's core interface). Rendering, description and RRULE export are out of scope; the reference implementation has them.
Install
Rust 2021 edition. No dependencies, including the zone handling; IANA zones are read straight from the system TZif database.
Usage
use ;
let dtr = parse.unwrap; // business hours, Mon–Fri
// 2026-07-07 (a Tuesday) 10:00:00Z, in ms since the Unix epoch:
let t: i64 = 1_783_418_400_000;
let ok = dtr.covers.unwrap;
// —> true; a weekday, 09:00–18:00 Berlin local time.
// The zone is an evaluation parameter, never part of the expression;
// an empty identifier or "UTC" means UTC.
// Preloaded zone; cannot fail:
let berlin = load.unwrap;
let ok = dtr.covers_in;
Instants are milliseconds since the Unix epoch (UTC); the time zone is passed at evaluation, and the default is Tz::utc(). Note that you parse once (at write/config time) and evaluate many; a Dtrexp value is immutable after parse. covers is a single calendar-field extraction followed by integer comparisons; no occurrence iteration.
Errors and Warnings
Both a ParseError and a Warning carry a position; the byte offset into the source where the problem was detected.
let err = parse.unwrap_err; // hour out of range
err.pos; // 1 — points at the offending token
err.message; // "hour out of range — 24 exists only as the exact token '2400'"
let warnings = validate.unwrap; // parses cleanly
warnings.pos; // 0
warnings.message; // "unsatisfiable — day never exists …" (no February has 30 days)
parse(s)returns the expression or aParseError { pos, message }. Warnings from a clean parse are available viaDtrexp::warnings().validate(s)returnsResult<Vec<Warning>, ParseError>: the warnings on success, or the sameParseErrorwhen the input does not parse. It carries the same warnings asDtrexp::warnings().covers/covers_intake an IANA zone. An identifier that does not resolve is the one runtime failure,UnknownTimeZone { id, message };covers_intakes an already-loadedTzand cannot fail.- Warnings are the spec's §9.1 unsatisfiability lint: expressions that parse but can never match.
Conformance & Quality
- The test suite is driven by the shared
vectors.jsonfrom the spec repo (draft 2.8), vendored attests/vectors.json: every coverage, rejection, warning and quiet vector, including the calendar traps (Feb 29 across 2000/2024/2100,W53existence, DST gap/overlap inEurope/Berlin). Runcargo test. See VECTORS.md for how the suite works. - Hand-rolled TZif reader.
Tz::loadreads the system zoneinfo database directly; there is no time-zone crate underneath. - Zero dependencies.
Related Projects
- dtrexp (spec) — the DTRExp specification (grammar, semantics, conformance vectors) this package implements.
- dtrexp-js — the reference implementation; adds
intersect,next,describe,toRRuleand canonicalization. - dtrexp-py · dtrexp-go · dtrexp-swift · dtrexp-java — the other ports; same core interface.
- dtrexp-wasm — this crate compiled to WebAssembly for JS hosts.
License
© 2026, Onur Yıldırım. MIT License.