Clock Contract (clock.js · DexClock), Tepna physiological-signal suite
Problem. Every downstream physiological result depends on getting one thing right that the literature almost never discusses: parsing the timestamp out of a consumer device export. Consumer exports carry a zoo of incompatible time formats — ambiguous day/month order, zoned versus zone-less stamps, bare epoch integers, compact 14-digit runs, and time-only rows that silently cross midnight — and the usual reflex (new Date(str) / Date.parse) resolves several of them wrongly but silently, or fabricates the current time when a stamp is missing. Contribution. We state a single time model — UTC-normalized floating wall-clock milliseconds (tMs) — as a citable method, and provide a deterministic pass/fail benchmark of the failure modes it must survive. The model stores each record's local civil time as if it were UTC (tMs = Date.UTC(components-as-written)), which makes displayed time independent of the viewer's timezone and makes two devices that recorded the same wall-clock minute produce the same tMs by construction. Result. The production parser resolves a corpus of 24 vendor/branch cases and upholds 6 contract invariants — viewer-timezone independence, zoned≡floating equivalence, deterministic DMY/MDY disambiguation, monotonic overnight roll, never-fabricate-on-miss, and epoch determinism — all green, regenerated live from the shipping code. Significance. This is a methods/reproducibility note, not a physiological finding; its value is a stated, testable standard for a step the field treats as an afterthought and a corpus others can run against their own parser.
Keywords: timestamp parsing · data provenance · wall-clock time · timezone · DMY/MDY ambiguity · ISO-8601 · epoch time · consumer wearables · reproducibility · cross-device synchronization
Before you can say anything about someone's heart rate at 3 a.m., you have to know which moment “3 a.m.” actually was — and the little date-and-time text that devices write into their files is a mess. Some write the day first, some the month first (is 05/07 May 7th or July 5th?); some include a timezone, most don't; some write just a raw number of seconds; some write only the time and leave you to figure out the date. The lazy way to read these — hand the text to the computer's built-in date reader — quietly gets several of them wrong, and worst of all, when a stamp is missing it often just fills in “right now,” inventing data. This paper writes down one careful rule for reading all of these correctly, and a checklist of tricky cases it must pass. The key trick: store each reading as the wall-clock time the person actually saw, not tied to any timezone, so the same bedtime looks the same whether you open the file in New York or London, and two devices worn the same night line up automatically. We run the checklist against the real code: every case passes. It's plumbing, not a discovery — but it's the plumbing everything else stands on.
Physiological analysis papers report what happened at a time; almost none report how the time itself was parsed. Yet consumer device exports — the raw material of at-home physiology — encode time in mutually incompatible ways, and the default tools resolve them inconsistently. A single archive can contain an O2Ring oximeter writing HH:MM:SS DD/MM/YYYY, a Welltory CSV writing MM/DD/YYYY HH:MM, a Polar sensor logger writing zoned ISO-8601, and a filename embedding a bare YYYYMMDDHHMMSS. Handing any of these to new Date(str) invokes locale- and engine-dependent heuristics: it may read a European 13/05 correctly by luck and an American 05/13 wrongly, attach the parsing machine's timezone to a stamp that had none, or — most dangerously — return the current time for an unparseable input, silently fabricating a data point.
The synthetic-data-frontier perspective explicitly named this as the series' "narrowest first paper." This is that paper. Its contribution is not an algorithm but a stated standard: a single time model, the failure corpus it must survive, and a live tool that proves the shipping parser survives it. We make three claims: (i) the correct canonical unit for zone-less consumer physiology is a floating wall-clock, not a real UTC instant; (ii) every vendor format maps into it deterministically by explicit regex, never by locale heuristics; and (iii) a missing or malformed stamp must resolve to null, never to a fabricated time.
tMsEach record's local civil time is stored encoded as if it were UTC: tMs = Date.UTC(year, month−1, day, hour, min, sec, ms). This is deliberately not a real UTC instant. Consumer devices speak local civil time with no zone; storing a real UTC instant and rendering it with local getters would make displayed time depend on the viewer's timezone (a New-York night reads 03:00 in London). Floating tMs read back with the getUTC* family is viewer-timezone-independent, and two devices recording the same wall-clock minute yield the same tMs by construction — so cross-device synchronization holds without either device sharing a timezone. An optional offsetMin is captured only when the input carried a real zone; the true instant is then recoverable as tMs − offsetMin·60000, but all sorting, alignment and display default to tMs.
The single parser DexClock.parseTimestamp(raw, opts) resolves in a fixed priority, each branch an explicit regex — never Date.parse on a vendor string: (1) numeric epoch (10–13 digits or numeric type, plausible-range gated), localized to the parse-time zone; (2) ISO-8601 with zone — zone authoritative, re-expressed as wall clock with offsetMin captured; (3) ISO / YYYY-MM-DD[ T]HH:MM[:SS] without zone — components verbatim, offsetMin=null; (4) explicit vendor formats — HH:MM:SS DD/MM/YYYY (O2Ring), 14-digit compact, DD/MM/YYYY HH:MM (Welltory), YYYY/MM/DD HH:MM:SS; (5) time-only HH:MM[:SS] combined with a date anchor and rolled forward monotonically past each midnight; (6) fallback → null, never now().
Any row whose day-field exceeds 12 makes the file unambiguous, and that order is locked for the whole file; otherwise the caller's preferDMY hint decides (default true for O2Ring/Welltory, false for the CGM MDY convention). The order is never switched mid-file.
The corpus (Table 1) has one entry per parser branch and per vendor format observed in the suite's real exports, plus adversarial edge cases (implausible epoch, partial date, empty, garbage, anchorless time-only). The invariants (Table 2) encode the contract's behavioural guarantees. Both run against the production clock.js in timestamp-pathology-analysis.html; the pass counts below are read from that tool's live output (window.__tsbench).
The production parser resolved 24 / 24 corpus cases and upheld 6 / 6 contract invariants — all green, regenerated live from the shipping code.
| Category | Raw input | Resolves to | Note |
|---|---|---|---|
| Epoch | "1751925600000" | instant + offsetMin | 13-digit ms; 10-digit s auto-scaled |
| Epoch (bad) | "999" | null | below plausible range |
| Zoned ISO | "…22:00:00+02:00" | 22:00 wall, off=120 | zone in offsetMin, wall kept |
| Floating ISO | "2026-07-07 22:00" | 22:00 float | no zone → offsetMin=null |
| O2Ring | "22:40:01 07/07/2026" | 2026-07-07 22:40:01 | HH:MM:SS DD/MM/YYYY |
| Compact | "20260707224001" | 2026-07-07 22:40:01 | 14-digit filename embed |
| Welltory | "07/05/2026 21:53" | May 7 (DMY) / Jul 5 (MDY) | preferDMY switch, per-file locked |
| Time-only | "00:15" (prev 23:55) | next-day 00:15 | monotonic midnight roll |
| Time-only (bad) | "23:30" (no anchor) | null | never Jan-1-2000 |
| Malformed | "" / "not a date" / null | null | never now() |
| Invariant | Holds | What it guarantees |
|---|---|---|
| B1 · viewer-timezone independence | ✓ | floating path is pure Date.UTC, no getTimezoneOffset → identical tMs on any machine |
| B2 · zoned wall ≡ floating wall | ✓ | a zoned and a zone-less stamp for the same wall instant give the same tMs; zone lives only in offsetMin |
| B3 · DMY 13/05 ≡ MDY 05/13 | ✓ | a day-field >12 disambiguates the file deterministically, regardless of the hint |
| B4 · overnight monotonic roll | ✓ | 22:00→06:00 time-only rows span 8 h, not a 24 h backward jump |
| B5 · never fabricate on miss | ✓ | missing / garbage / anchorless → null; the parser never returns now() |
| B6 · epoch determinism | ✓ | string and numeric epoch inputs resolve to the identical instant |
Invariant B1 deserves a precise statement: only the floating branches (2–5) are viewer-timezone-independent, and by construction — they never call getTimezoneOffset. The epoch branch (1) does consult the runner timezone, deliberately, because a bare epoch is a genuine instant that must be localized to a wall clock; that is the one place the viewer's zone legitimately enters, and it is confined there.
The practical lesson is that timestamp parsing is a correctness surface, not a convenience call, and that the default library path is actively unsafe for consumer physiology on three counts: silent DMY/MDY mis-resolution, spurious timezone attachment, and fabrication-on-miss. The floating wall-clock model neutralizes the first two by construction (components-as-written + getUTC* read-back) and the third by contract (miss → null). The model's non-obvious move — storing local civil time as if UTC rather than as a real instant — is what buys viewer-timezone independence and zero-configuration cross-device alignment, the two properties every multi-signal result in this suite silently relies on. Because the benchmark is a live tool over the shipping parser rather than a frozen table, it doubles as a regression guard: any future parser change that breaks a vendor format or an invariant turns the tool red.
clock.js (DexClock.parseTimestamp) — the single-sourced Clock Contract parser inlined into every bundle; unchanged by this paper.papers/timestamp-pathology-analysis.html loads the real parser via <script src="../clock.js"> and runs the full 24-case corpus + 6 invariants live, rendering the pass/fail tables and exposing window.__tsbench. Unbundled → touches neither the behavior gate nor the provenance gate.now() is exactly what the parser must never call). Re-running yields byte-identical results.papers/timestamp-corpus.json and papers/timestamp-invariants.json record a committed run for diffing.papers/synthetic-data-frontier.html — names the timestamp benchmark as the narrowest first paper.CLAUDE.md §🔒 The Clock Contract — the normative specification this paper benchmarks.Date.UTC / Date.prototype.getUTC* (the floating-time primitives used).