concept

Timestamp Contract

also called Instant versus Wall Clock, Time Representation Contract

The explicit agreement about what a time value in a payload means — an instant, a local reading or a calendar date — without which a single string silently denotes different moments in different systems.

timestampsrfc3339time-zonesdstdata-quality

A client sends 2027-03-28T02:30:00. Nightly reporting has been slightly wrong for European users for years, and on one Sunday in March about 40 minutes of events disappear while on a Sunday in October some appear twice. The string looks like a timestamp and is not one. It is a wall-clock reading with no offset, so something downstream must guess which instant it meant, and the guess is usually the server's own zone.

A timestamp contract states which of three different things a field holds: an instant (a point on the timeline), a local reading (a clock face, meaningful only with a zone), or a calendar date (a day, with no instant at all). The three are not convertible without information most payloads do not carry.

Why it matters

Daylight-saving transitions make the ambiguity fatal twice a year. At the spring transition local clocks jump from 02:00 to 03:00, so that hour does not exist and timestamps inside it are either rejected or silently shifted by a lenient parser. At the autumn transition the hour occurs twice, so one string maps to two instants an hour apart: ordering breaks, and deduplication keyed on (user, timestamp) merges two real events into one.

The rest of the year the error is a constant offset, which is worse in one way: nothing looks broken, so the contract is never fixed, and every consumer that stores the wrong value raises the cost of fixing it later.

Implementation patterns

  • Put an explicit offset on the wire. RFC 3339 form, 2027-03-28T02:30:00+01:00, is self-describing. Accepting a naive string is accepting a guess.
  • Store three values when local meaning matters: the instant in UTC, the original UTC offset, and the IANA zone id. The offset cannot be recovered from the instant, and the zone id is needed for "the same time next week" across a transition, which the offset cannot answer.
  • Resolve zone rules at read time from a maintained database rather than baking future offsets into stored schedules, because governments change the rules with months of notice.
  • Reject a timestamp without an offset at the boundary with a 400. A tolerant reader here is a corrupting reader.
  • Keep calendar dates as dates. A birthday, an invoice period or a hotel check-in day should never pass through a UTC conversion.
  • Record the server's receipt instant separately from the client's claimed instant, and treat the client's as untrusted. Device clocks are wrong, sometimes by years after a battery pull.
  • Bucket reports in a named zone and state it on the report, because "daily" is a local-calendar concept and the choice changes the numbers.

Industry example

The conventions here are specified rather than vendor-specific: RFC 3339 (2002) defines the internet date and time format, and the IANA Time Zone Database is the maintained source of zone rules that every platform depends on. Mobile and IoT systems meet the problem hardest, because a device that loses power can boot with a clock at the epoch, which both breaks TLS certificate validation and poisons any event it emits until time is resynchronised. Any payload carrying a device-generated time needs the receipt instant alongside it, or the data cannot be trusted at all.

Failure scenarios

  • The vanished hour: events in the spring transition dropped or shifted, visible only as a dip in one hour on one day a year.
  • The doubled hour: duplicate keys in the autumn, with ordering inversions that make a state machine replay out of sequence.
  • Constant drift: a one or two hour error for a whole region, mistaken for a reporting definition problem for years.
  • Future-dated schedules stored as fixed offsets, which fire an hour late after a rule change.
  • Leap-second and smeared-clock surprises, where a monotonic assumption in a dedup window breaks during a smear.
  • Database columns that look right. Storage is usually correct in type; the damage happened at the parser, so querying the data will not reveal it.

Trade-offs

Carrying instant, offset and zone is three fields instead of one, plus validation at every boundary and a migration for data already stored wrongly. The alternative is cheaper to write and permanently ambiguous. The honest framing is that the cost of the contract is fixed and small, while the cost of not having it rises with every consumer that has already stored the wrong thing — which is why it is nearly always right to pay at design time and nearly always deferred.

When not to use it

A system with one deployment region, one user zone and no scheduling can live with naive local times for a long time, and saying so explicitly in the design note is better than pretending otherwise. For purely internal monotonic sequencing, a logical clock or a sequence number is a better tool than any wall-clock value. And where only elapsed duration matters, store the duration: converting two instants to a difference loses nothing and avoids the entire question.

Interview question

Q: Your mobile client stamps each event and the server stores that value. Analytics reports a persistent discrepancy with server-side counts, and once a year a cohort of events goes missing. Tell me what you would change in the payload, in the storage schema and in the ingest validation, and what you would do about the six months of data already stored.

What a strong answer covers: naming the instant / local reading / calendar date distinction · an explicit offset on the wire and the three stored values · rejecting naive strings at the boundary rather than guessing · keeping the server receipt instant as the trustworthy value and treating the device clock as untrusted · and a backfill plan that is explicit about which historical rows can be corrected (those with a recoverable zone from another field) and which are unrecoverable and must be labelled rather than silently repaired.

Quick check

Quiz: Why is the UTC instant alone insufficient when a user asks "what time did I submit this?" — Because the original UTC offset is not recoverable from the instant, and reconstructing it from the user's current zone gives the wrong answer for anyone who has travelled or whose zone rules changed.

Flashcard: What happens to a wall-clock timestamp in the spring daylight-saving gap? — The local time does not exist, so it is either rejected or silently shifted; the matching autumn case is ambiguous, mapping one string to two instants.