Skip to content
C.W.K.
Stream
Lesson 03 of 05 · published

Two Correct Dates, One Instant

~12 min · measurement, timezone, evidence, design

Level 0Raw Ore
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
At 05:49 UTC on 2026-08-06, a US listing belonged to 2026-08-05 and a Tokyo listing belonged to 2026-08-06. Neither was stale. Neither was wrong.

The observation that ends the argument

Timezone bugs generate long discussions, because everyone involved has a mental model and all the models are locally consistent. The discussion ends when somebody produces a measurement, and this is the measurement: two listings, queried at one instant, resolving to two different session dates. Both correct.

That single fact does more work than any amount of reasoning about offsets. It proves that "today" is not a property of the process. It is a property of each listing, and there is no clock the process could consult — not local, not UTC, not any single exchange's — that would be simultaneously right about all of them.

Which means the shape of the fix is forced. Not a better global date, not a smarter offset table: a date attached per row, resolved from that row's own listing.

A measurement ends a design argument that reasoning cannot. Two people can hold incompatible timezone models indefinitely, because each one works within the examples that person thought of. One captured instant showing two different correct answers is not arguable — and the cost of producing it was two API calls.

What the fix rules out, permanently

Read the consequence carefully, because it is stronger than "we fixed the date bug":

  • No per-run date. The pass cannot stamp its own date on anything, however carefully that date is derived.
  • No single reference market. "Use New York's date for everything" is wrong for two markets out of three.
  • No UTC-as-truth. UTC is unambiguous, which people mistake for correct — but no exchange trades on UTC's calendar, so a UTC date is simply a fourth wrong answer.
  • No "close enough". At 07:11 Asia/Seoul the discrepancy is not an edge case that fires near midnight. It is the normal state of every single run.

The general shape: per-batch versus per-item facts

This generalizes well past markets. Any batch that touches items from different contexts faces the same question: which facts belong to the batch, and which belong to the items?

A batch legitimately owns: when it ran, which version of the code ran it, how long it took, how many items it processed. An item owns: what it is about, when its subject occurred, which authority defines it, what units it is in. The bug is always the same shape — a fact that belongs to the item gets stamped from the batch, because at the moment of writing the code, there happened to be only one item context and the distinction was invisible.

Why one market hid it. A US-only version of this app would have had the same defective line and never once produced a wrong row, because the loop's hour would have been chosen against that single market. The second market is what made the bug expressible, and the third is what made it impossible to paper over. Systems usually acquire this class of defect while they are still small enough for it not to matter — and then keep it.

Code

One instant, two listings, two correct answers·text
MEASURED 2026-08-06 05:49 UTC (= 14:49 Asia/Seoul)

  US ETF proxy
    provider timestamp -> America/New_York
    session date       -> 2026-08-05     <- yesterday, correctly

  Tokyo-listed ETF
    provider timestamp -> Asia/Tokyo
    session date       -> 2026-08-06     <- today, correctly

CANDIDATE GLOBAL DATES AT THAT INSTANT, ALL WRONG FOR SOMEONE:

  local (Seoul)  2026-08-06   wrong for the US row
  UTC            2026-08-06   wrong for the US row
  New York       2026-08-05   wrong for the Tokyo row

# There is no fourth option. The date is per-listing or it is wrong.

External links

Exercise

Take a batch job you own and split its output fields into two lists: facts about the run, and facts about each item. Then check where each field is actually populated from. Any item-fact that is currently populated from run context is this bug, latent — and it will stay invisible until the batch first processes items from two different contexts.
Hint
Item facts hide behind innocuous names: date, region, currency, locale, version, unit. Ask of each one 'if this batch processed two items from different contexts, would this field have to differ between them?' If yes, it cannot be a batch-level value.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.