This is the CalConnect TC DATETIME machine-readable test suite for the ISO 8601 series, developed as joint work with ISO/TC 154/WG 5.
The test suite covers the syntaxes introduced by ISO 8601-1:2026 (Date and time — Representations for information interchange — Part 1: Basic rules) and ISO 8601-2:2026 (Part 2: Extensions).
This repository contains conformance tests in a format-agnostic YAML data model that can be converted into JSON, YAML and XML for consumption by test suite implementations.
The requirement statement fields in requirements/ are independent
paraphrases of the normative provisions of ISO 8601-1:2026 and ISO 8601-2:2026.
They are written in original language and do not reproduce verbatim text from the ISO standards.
ISO 8601-1:2026 and ISO 8601-2:2026 are copyrighted by the International Organization for Standardization (ISO). The requirement statements in this repository are original works that describe the same technical constraints using different language, prepared under the principle that functional specifications are not subject to copyright protection.
The test suite follows the OGC Model for Standards Specification (ModSpec) terminology:
Requirement |
An atomic testable criterion (a normative "shall" statement) |
|---|---|
Requirements class |
A set of consistent requirements on a single standardization target type |
Conformance test |
An individual test that verifies one or more requirements (pass/fail) |
Conformance class |
A group of conformance tests corresponding 1:1 to a requirements class |
Conformance suite |
All conformance classes for a standard |
Dependency |
A requirements class that must be passed before another can be tested |
Profile |
A set of conformance classes from one or more standards |
The standardization target type is iso-8601-date-time-processor — a software implementation that parses, generates, validates, or computes ISO 8601 date/time expressions.
Identifiers use a CURIE-like notation with type prefixes:
|
Test suite manifest (e.g. |
|
Requirements class (e.g. |
|
Requirement (e.g. |
|
Conformance class (e.g. |
|
Conformance test (e.g. |
|
Profile (e.g. |
|
Conformance result set (e.g. |
Cross-part references use a part prefix: 8601-1:conf-class:calendar-date.
These local identifiers expand to RFC 5141 URNs under the ISO namespace:
urn:iso:std:iso:8601:-{part}:ed-1:en:tech:tc154.wg5:{type}:{name}
Requirement clause fields use RFC 5141 partial URNs to identify the source
clause within the standard. The format follows the pattern:
iso:8601:-{part}:ed-1:en:clause:{number}Examples:
-
iso:8601:-1:ed-1:en:clause:5.2.2.1— Part 1, Clause 5.2.2.1 -
iso:8601:-2:ed-1:en:clause:7.2.2— Part 2, Clause 7.2.2 -
iso:8601:-1:ed-1:en:clause:4,5.1— Part 1, Clauses 4 and 5.1
|
expression → components |
Parse an expression and validate extracted components |
|
components → expression |
Build an expression from components |
|
expression → boolean |
Check if expression is syntactically valid |
|
two expressions → boolean |
Two expressions resolve to the same instant |
|
expression + operation → result |
Date/time arithmetic |
|
expression → expression |
Parse → extract → generate → compare (exact or semantic equivalence) |
iso-8601-test-suite/
├── suite.yaml # Machine-readable suite manifest
├── scripts/
│ ├── run-tests # Test runner (ruby scripts/run-tests)
│ └── validate # Validation script (ruby scripts/validate)
├── lib/ # Shared Ruby modules
│ ├── term.rb # Terminal output (colors, icons)
│ ├── yaml_store.rb # Cached YAML loader with error tracking
│ ├── suite_index.rb # Cross-reference index (IDs → files)
│ ├── component_vocab.rb # Component vocabulary (data-driven)
│ ├── stats.rb # Error/warning/pass tracker
│ ├── schema_validator.rb # YAML Schema validation engine
│ ├── test_type_handlers.rb # Test type handler registry (OCP)
│ ├── test_suite_loader.rb # Test list builder
│ ├── adapter_loader.rb # Adapter discovery and instantiation
│ ├── exec_adapter.rb # JSON protocol adapter for external processes
│ ├── graph_util.rb # Directed cycle detection
│ └── hash_util.rb # Hash key normalization
├── adapters/ # Pluggable implementation adapters
│ ├── TEMPLATE.rb # Ruby adapter interface template
│ ├── README.adoc # Adapters overview
│ ├── ruby/ # Ruby Date/DateTime/Time
│ │ ├── README.adoc
│ │ ├── date.rb # In-process adapter (loaded by Ruby adapter loader)
│ │ └── date-exec.rb # Exec wrapper — same logic, any Ruby runtime
│ ├── python/ # Python datetime
│ │ ├── README.adoc
│ │ └── datetime.py
│ ├── node/ # Node.js Date
│ │ ├── README.adoc
│ │ └── datetime.js
│ ├── c/ # C strftime/strptime
│ │ ├── README.adoc
│ │ └── stdio.c
│ ├── cpp/ # C++ std::chrono
│ │ ├── README.adoc
│ │ └── chrono.cpp
│ ├── java/ # Java java.time
│ │ ├── README.adoc
│ │ └── JavaDateTime.java
│ └── rust/ # Rust chrono (one source, two crate versions)
│ ├── README.adoc
│ ├── chrono-latest/ # chrono = "0.4" (resolves to latest 0.4.x)
│ └── chrono-0419/ # chrono = "=0.4.19" (pinned)
├── schema/
│ ├── suite.yaml # YAML Schema for suite manifest
│ ├── requirements-class.yaml # YAML Schema for requirements class files
│ ├── conformance-class.yaml # YAML Schema for conformance class files
│ ├── conformance-result.yaml # YAML Schema for conformance result files
│ ├── profile.yaml # YAML Schema for profile files
│ ├── components.yaml # Component vocabulary schema
│ └── meta.yaml # Shared YAML Schema types
├── requirements/
│ ├── 8601-1/ # ISO 8601-1 requirements (9 files)
│ └── 8601-2/ # ISO 8601-2 requirements (13 files)
├── tests/
│ ├── 8601-1/ # ISO 8601-1 conformance tests (9 files)
│ └── 8601-2/ # ISO 8601-2 conformance tests (13 files)
├── profiles/ # Profile definitions (8 + TEMPLATE)
├── results/ # Conformance test results per implementation
│ ├── TEMPLATE.yaml # Template for new result files
│ └── ruby-date.yaml # Ruby Date/DateTime/Time results
├── site/ # Vue 3 dashboard (Vite + Tailwind)
│ ├── src/ # Vue components, views, composables
│ ├── public/ # Static assets (logos, JSON data)
│ └── dist/ # Production build output| Requirements class | Source | Dependencies |
|---|---|---|
|
Clauses 4, 5.1 |
(core) |
|
Clause 5.2.2 |
fundamentals |
|
Clause 5.2.3 |
fundamentals |
|
Clause 5.2.4 |
fundamentals |
|
Clause 5.3 |
fundamentals |
|
Clause 5.4 |
calendar-date, time-of-day |
|
Clause 5.5.2 |
fundamentals |
|
Clause 5.5 |
date-and-time, duration |
|
Clause 5.6 |
time-interval |
| Requirements class | Source | Dependencies |
|---|---|---|
|
Clause 7 |
8601-1:fundamentals |
|
Clause 4.4.1 |
explicit-representation |
|
Clause 4.4.2 |
explicit-representation |
|
Clause 4.4.3 |
explicit-representation |
|
Clause 8 |
8601-1:date-and-time |
|
Clause 9 |
8601-1:calendar-date |
|
Clause 4.8 |
8601-1:fundamentals |
|
Clause 5 |
8601-1:fundamentals |
|
Clause 6 |
8601-1:date-and-time |
|
Clause 10 |
8601-1:time-interval |
|
Clause 12 |
8601-1:date-and-time |
|
Clause 13 |
8601-1:recurring-time-interval |
|
Clause 14 |
8601-1:date-and-time, 8601-1:duration |
Profiles use a traceability field that explicitly selects specific requirements
from conformance classes. This replaces the legacy conformance_classes field
(which referenced entire classes wholesale).
| Profile | Description | Add’l tests |
|---|---|---|
|
All ISO 8601-1:2026 requirements (9 classes) |
— |
|
All ISO 8601-1 + ISO 8601-2 requirements (22 classes) |
— |
|
Basic format representations only (no delimiters) |
— |
|
Extended Date/Time Format Level 0 |
10 |
|
EDTF Level 1 (uncertain, approximate, unspecified) |
19 |
|
EDTF Level 2 (structured date/time expressions) |
24 |
|
RFC 3339 (Internet date/time) |
9 |
|
W3C Date and Time Formats (NOTE-datetime) |
13 |
-
99 requirements in ISO 8601-1:2026 across 9 requirements classes
-
158 requirements in ISO 8601-2:2026 across 13 requirements classes
-
51 additional requirements across 8 profiles
-
308 total requirements
-
275 conformance tests for ISO 8601-1
-
363 conformance tests for ISO 8601-2
-
95 additional tests across 8 profiles
-
733 total tests
The scripts/run-tests script executes test definitions from the YAML files
against a pluggable adapter. The default adapter uses Ruby’s standard library
Date, DateTime, and Time classes. No gem dependencies are required.
ruby scripts/run-tests # run all 733 tests (default: ruby-date)
ruby scripts/run-tests --adapter ruby-date # explicit adapter selection
ruby scripts/run-tests --profile rfc-3339 # run RFC 3339 profile
ruby scripts/run-tests --profile w3c-datetime # run W3C Datetime profile
ruby scripts/run-tests --profile edtf-level-0 # run EDTF Level 0 profile
ruby scripts/run-tests --class calendar-date # run a single conformance class (32 tests)
ruby scripts/run-tests --class time-of-day # run time-of-day tests
ruby scripts/run-tests --list # list available adapters, profiles, and classes
ruby scripts/run-tests --verbose # show test descriptions
ruby scripts/run-tests --quiet # only show summary
ruby scripts/run-tests --output results/new.yaml # write results to YAML fileA profile runs all tests from its traceability requirements plus the profile-specific
additional tests. For example, --profile rfc-3339 runs tests from the calendar-date,
time-of-day, and date-and-time conformance classes (filtered by RFC 3339’s
traceability requirements) plus 15 RFC 3339-specific additional tests.
Adapters wrap a specific date/time implementation so the test harness can execute conformance tests against it. There are two adapter shapes:
| Shape | When to use |
|---|---|
Ruby class ( |
The implementation is Ruby or has a Ruby binding. The harness loads it in-process — fastest, no JSON marshalling overhead. |
External process ( |
Anything else: JavaScript, Python, Rust, C, C++, Java, a binary you built by hand. The harness spawns a long-lived child process and talks to it over newline-delimited JSON. |
Every adapter implements the same logical interface, regardless of shape:
|
Parse an expression; return |
|
Extract components (year, month, day, etc.) from a parsed object. |
|
Build an expression from components; return |
|
Check if two parsed objects represent the same instant. |
|
Optional: execute an arithmetic test (default: not-supported). |
|
Optional: which conformance classes the implementation claims. Tests outside this list are recorded as |
|
Optional: which profiles the implementation explicitly targets. When set, the dashboard uses this to scope per-library statistics (overrides inference from |
-
Copy
adapters/TEMPLATE.rbtoadapters/{name}.rb -
Rename the class to
{CamelCaseName}Adapter(e.g.ruby-date.rb→RubyDateAdapter) -
Implement the required methods and set
declared_conformance_classes/declared_profiles -
Run:
ruby scripts/run-tests --adapter {name}
The class naming convention converts the filename by capitalizing each
hyphen-separated segment and appending Adapter (e.g. my-lib → MyLibAdapter).
The exec: adapter type lets you test any implementation in any language —
JavaScript, Python, Rust, an XML parser, a web API — without writing Ruby
code. It communicates with a child process via a newline-delimited JSON
protocol.
ruby scripts/run-tests --adapter "exec:python3 adapters/python/datetime.py"
ruby scripts/run-tests --adapter "exec:node adapters/node/datetime.js"
ruby scripts/run-tests --adapter exec:./adapters/rust/chrono-latest/target/release/rust-chronoThe harness starts the adapter process once and keeps it running for all tests. For each operation, it writes one JSON command to stdin and reads one JSON response from stdout — each on a single line.
Request:
{"method":"<name>","params":{...}}Response:
{"result":<value>}Error response:
{"error":"<message>"}Protocol methods:
| Method | Description |
|---|---|
|
Return |
|
Params: |
|
Params: |
|
Params: |
|
Params: |
|
Params: |
|
No params. |
|
No params. |
The parsed value is an opaque handle (any JSON value) managed by the adapter
process. When try_parse succeeds, the adapter caches the internal parsed
object and returns a handle. The harness passes this handle to
extract_components and equivalent. The adapter looks up the cached object
by handle.
See adapters/python/datetime.py for a working example (Python standard library).
See adapters/node/datetime.js for a working example (JavaScript Date).
See adapters/TEMPLATE.rb for the full adapter interface specification.
The repository ships adapters for 24 standard-library implementations across
7 language families. Each adapter’s declared_conformance_classes and
declared_profiles reflect what the implementation actually claims to
implement — by convention, every adapter currently declares only the 4
ISO 8601-1 Core classes and targets profile:iso-8601-1-core.
| Family | Adapter ID | Implementation / invocation |
|---|---|---|
Ruby Date |
|
Ruby |
Ruby Date |
|
Same Ruby adapter run under specific Ruby versions (via |
Python datetime |
|
Python |
Python datetime |
|
Same Python adapter run under specific Python versions (via |
Node.js Date |
|
JavaScript |
Node.js Date |
|
Same Node adapter under specific Node versions (via |
C stdio |
|
BSD/macOS |
C stdio |
|
glibc |
C++ chrono |
|
C20 `std::chrono` compiled with Homebrew LLVM (`/opt/homebrew/opt/llvm/bin/clang`) |
C++ chrono |
|
Same source compiled with Apple Clang ( |
Rust chrono |
|
Rust |
Rust chrono |
|
Rust |
Java java.time |
|
Java 21 |
Java java.time |
|
Same source compiled under Java 8 / Java 15 (via AdoptOpenJDK) |
The Ruby, Python, Node, and C adapters are compiled (or interpreted) on each invocation — no separate build step. The Rust and Java adapters require a one-time build:
# Rust (both versions share adapters/rust/chrono-latest/src/main.rs)
cargo build --release --manifest-path adapters/rust/chrono-latest/Cargo.toml
cargo build --release --manifest-path adapters/rust/chrono-0419/Cargo.tomlJava is recompiled on each invocation via the exec: command in
lib/test_suite/capability_matrix.rb, so no separate build step is needed.
scripts/regenerate-all-results runs the full test suite against every
adapter defined in CapabilityMatrix::ADAPTER_DEFS and writes one
results/{id}.yaml per adapter:
ruby scripts/regenerate-all-results # all 24
ruby scripts/regenerate-all-results --only rust-chrono,java-8 # subset
ruby scripts/regenerate-all-results --skip c-stdio-glibc # skip slow Docker buildAfter regenerating result YAMLs, refresh the dashboard JSON:
ruby scripts/capability-matrix # writes site/public/summary.json + detail.jsonThe Rust adapter binaries are not rebuilt automatically — if you edit
adapters/rust/chrono-latest/src/main.rs, rebuild both Cargo projects
before running regenerate-all-results (see above).
Profiles define subsets of ISO 8601 for specific use cases. To create one:
-
Copy
profiles/TEMPLATE.yamltoprofiles/{name}.yaml -
Set
id: profile:{name},name, anddescription -
Define
traceability— list the specific requirements selected from each conformance class:traceability: - conformance_class: conf-class:calendar-date requirements: - req:cal-date-parse-001 - req:cal-date-gen-001
-
Optionally add
additional_requirementsandadditional_testsfor profile-specific rules -
Validate:
ruby scripts/validate -
Test against an adapter:
ruby scripts/run-tests --profile {name}
Available requirement IDs are defined in requirements/8601-1/.yaml and
requirements/8601-2/.yaml. Each requirements class file lists its requirements
with their req: identifiers.
The test suite is format-agnostic — the YAML test definitions can be consumed by any implementation in any language. The general procedure is:
-
An implementation declares which requirements classes it supports.
-
For each supported requirements class, the implementation runs all conformance tests in the corresponding conformance class.
-
An implementation passes a conformance class if all tests pass.
-
An implementation passes a profile if all referenced conformance classes pass (including additional profile-specific tests).
Results are recorded in YAML files under results/, one file per implementation,
following the schema in schema/conformance-result.yaml.
Each result file contains:
-
Implementation metadata — name, language, version, modules/classes used
-
Verification mode —
automated(test script),manual(human), orinferred(known behavior without execution) -
Per-class results — aggregate status (
passed,failed,partial,not-supported) with optional default method (e.g.Date.parse(expr)) -
Per-test results — individual
pass/fail/error/not-supportedstatus with documented API calls, actual output, and exception details -
Profile results — results for profile-specific additional tests (e.g. RFC 3339, EDTF Level 0/1/2, W3C Datetime)
Example result entry:
- test: conf-test:cal-date-parse-001
result: pass
method:
code: 'Date.parse("19850412")'
language: ruby
api: Date.parse
actual:
valid: true
returned: "1985-04-12"To add a new implementation result:
-
Copy
results/TEMPLATE.yamltoresults/{implementation}.yaml -
Set
id: result:{name}and fill in implementation metadata -
For each conformance class, record aggregate status and individual test results
-
Use
verification: automatedif a script ran the tests,manualif a human tested and recorded results, orinferredif results are based on known behavior
The scripts/validate script performs 15 phases of validation:
-
YAML syntax — all files parse correctly
-
Schema references — every data file has a
$schemaheader -
Schema validation — each file is validated against its declared YAML Schema (type checking, required fields, pattern matching, enum values,
oneOfdiscriminated unions) -
Requirements indexing — unique IDs, correct structure
-
Conformance test indexing — unique IDs, valid requirement references
-
Requirements coverage — every requirement is referenced by at least one test
-
Profile references — all conformance class references resolve
-
Result cross-references — all test IDs in result files match test definitions
-
Dependency graph — no cycles in requirements class dependencies
-
Source consistency — source clause references are consistent across related files
-
URN format — clause references follow RFC 5141 URN format
-
Statistics — summary counts (requirements, tests, files)
-
Pattern coverage — pattern fields are present where expected
-
Component validity — component keys match the vocabulary in
schema/components.yaml -
Test ID naming — test IDs follow the
conf-test:{prefix}-{type}-{NNN}convention
ruby scripts/validate # run all checks (colored output)
ruby scripts/validate -q # quiet — only show errors
ruby scripts/validate -v # verbose — show every file processedThis is also run automatically via GitHub Actions on every push that modifies YAML files.
Each YAML file includes a $schema reference for validation in editors that
support YAML Schema (e.g. VS Code with the YAML extension).
The site/ directory contains a Vue 3 dashboard (Vite + Tailwind CSS) that
visualizes conformance results across implementations and profiles.
The site loads two JSON files produced by the capability-matrix script:
-
site/public/summary.json— libraries, profiles, categories, requirements with pass/fail summaries (~24 KB gzipped) -
site/public/detail.json— full test details and profile traceability (~164 KB gzipped)
ruby scripts/capability-matrix # regenerate both files
ruby scripts/capability-matrix -o site/public # explicit output directorycd site
npm run build # outputs to site/dist/
npm run preview # preview the production build locallyThe build includes pre-compressed .gz files for all assets. The data loads
progressively: the summary renders the dashboard and navigation instantly, and
full test details load on demand when viewing the matrix or detail pages.
A conformant implementation for a particular feature shall pass all conformance tests within the corresponding conformance class.
This is ongoing work by CalConnect TC DATETIME in collaboration with ISO/TC 154/WG 5.