# Zone Desk > Read a schedule written as a local wall clock and see the instants it actually > produces. A wall clock inside the hour a zone skips maps to NO instant, and one > inside the hour it repeats maps to TWO. The difference between two zones is a list > of runs, not a number: New York to London is +5h for 337 days and +4h for 28, and > across the equator it takes three values. Adding 24 hours is not adding a day, and > `EST` is a fixed -05:00 that never springs forward. Free browser-side IANA > arithmetic, five paid lanes. Live at https://zone-desk.skillsafe.ai/ · API at https://zone-desk.skillsafe.ai/api.html Derived from the `dotnet-timezone` skill in `github/awesome-copilot` (https://github.com/github/awesome-copilot). Not affiliated with or endorsed by GitHub or Microsoft. ## The one thing to know **A wall clock can name no instant at all, or two.** ```text America/New_York skips 02:00-02:59 on 2026-03-08 02:30 that day exists 0 times repeats 01:00-01:59 on 2026-11-01 01:30 that day exists 2 times, at 2026-11-01T05:30Z (-04:00) and 2026-11-01T06:30Z (-05:00) ``` Which hour is affected differs by zone: `America/New_York` jumps at 02:00 local and `Europe/London` at 01:00, so the same schedule breaks on different readings in each. **The difference between two zones is a list of runs, not a number.** ```text New_York -> London +5h from 2026-01-01 +4h from 2026-03-08 +5h from 2026-03-29 +4h from 2026-10-25 +5h from 2026-11-01 +5h for 337 days, something else for 28 days New_York -> Sydney +14h, +15h, +16h <- 3 distinct values ``` The US and the EU change on different Sundays, so a fixed "9am here is 2pm there" is wrong on 28 days of the year, in two separate windows. Across the equator the two zones move in OPPOSITE directions, so the difference takes a middle value in between. **Adding 24 hours is not adding a day.** ```text a 09:00 job, +24h each time day 0 +24h +48h +72h America/New_York 09:00 09:00 10:00 10:00 Europe/London 09:00 09:00 10:00 10:00 Australia/Sydney 09:00 09:00 09:00 08:00 Asia/Kolkata 09:00 09:00 09:00 09:00 ``` The wall clock is what the schedule promised and it is what moves. `Asia/Kolkata` holds, which is exactly why this never appears in a test suite that runs in UTC. **And the offset is often not a whole number of hours.** ```text Asia/Kolkata Jan +05:30 Jul +05:30 Asia/Kathmandu Jan +05:45 Jul +05:45 Australia/Eucla Jan +08:45 Jul +08:45 Pacific/Chatham Jan +13:45 Jul +12:45 (it moves) Asia/Tehran Jan +03:30 Jul +03:30 ``` Every one a real place with a real population. And `EST` is not a place: this runtime resolves it to a FIXED -05:00 with no transitions, while `CST` resolves to one that moves - the name cannot tell you which you have. ## The hour that is skipped and the hour that repeats | Zone | Transitions in 2026 | The hour it skips | A reading 30 minutes in | The hour it repeats | A reading 30 minutes in | | --- | --- | --- | --- | --- | --- | | `America/New_York` | 2 | 2026-03-08 02:00–02:59 | 02:30: **never** | 2026-11-01 01:00–01:59 | 01:30: **2 times** | | `Europe/London` | 2 | 2026-03-29 01:00–01:59 | 01:30: **never** | 2026-10-25 01:00–01:59 | 01:30: **2 times** | | `Australia/Sydney` | 2 | 2026-10-04 02:00–02:59 | 02:30: **never** | 2026-04-05 02:00–02:59 | 02:30: **2 times** | | `Asia/Kolkata` | 0 | — | — | — | — | Every figure is read from this runtime's own IANA database. **A wall clock inside the skipped hour maps to no instant at all, and one inside the repeated hour maps to two** - so a job at 02:30 in `America/New_York` does not run at all on 2026-03-08. Which hour is affected differs by zone: `America/New_York` jumps at 02:00 local and `Europe/London` at 01:00, so the same schedule breaks on different readings in each. A zone with no transitions never meets either, which is why the bug reproduces for some participants and not others. ## The difference, as runs | Between | The usual difference | It holds for | It is something else for | Distinct values | The runs | | --- | --- | --- | --- | --- | --- | | `New_York` → `London` | +5h | 337 days | **28 days** | **2** | +5h from 2026-01-01; +4h from 2026-03-08; +5h from 2026-03-29; +4h from 2026-10-25; +5h from 2026-11-01 | | `New_York` → `Sydney` | +14h | 182 days | **183 days** | **3** | +16h from 2026-01-01; +15h from 2026-03-08; +14h from 2026-04-04; +15h from 2026-10-03; +16h from 2026-11-01 | | `London` → `Kolkata` | +4h30 | 210 days | **155 days** | **2** | +5h30 from 2026-01-01; +4h30 from 2026-03-29; +5h30 from 2026-10-25 | | `London` → `Tokyo` | +8h | 210 days | **155 days** | **2** | +9h from 2026-01-01; +8h from 2026-03-29; +9h from 2026-10-25 | A difference is not a number, it is a list of runs. **`New_York` to `London` is +5h for 337 days and something else for 28 days** - and not in one block, because the US and the EU change on different Sundays. Across the equator it is worse: `New_York` to `Sydney` takes **3 distinct values** in one year, because the two zones transition in opposite directions. No single stated time difference can describe that. ## The offsets that are not whole hours | Zone | In January | In July | Does it move | Whole hours from UTC | | --- | --- | --- | --- | --- | | `Asia/Kolkata` | +05:30 | +05:30 | no | **no — 30 minutes past** | | `Asia/Kathmandu` | +05:45 | +05:45 | no | **no — 45 minutes past** | | `Australia/Eucla` | +08:45 | +08:45 | no | **no — 45 minutes past** | | `Pacific/Chatham` | +13:45 | +12:45 | yes | **no — 45 minutes past** | | `Asia/Tehran` | +03:30 | +03:30 | no | **no — 30 minutes past** | | `America/St_Johns` | -03:30 | -02:30 | yes | **no — 30 minutes past** | | `Australia/Adelaide` | +10:30 | +09:30 | yes | **no — 30 minutes past** | | `Pacific/Marquesas` | -09:30 | -09:30 | no | **no — 30 minutes past** | **Every zone in this table is a real place with a real population**, and not one of them is a whole number of hours from UTC. Any store that keeps a difference as an integer of hours, any picker that offers whole hours, and any arithmetic that rounds to the hour is wrong for all of them by a fixed amount that never appears in testing anywhere else. `Pacific/Chatham` moves between +13:45 and +12:45, so it is not even a constant fraction. ## What a fixed interval does | Zone | Day 0 | +24h | +48h | +72h | +96h | Does the clock hold | | --- | --- | --- | --- | --- | --- | --- | | `America/New_York` | 09:00 | 09:00 | **10:00** | **10:00** | **10:00** | **no** | | `Europe/London` | 09:00 | 09:00 | **10:00** | **10:00** | **10:00** | **no** | | `Australia/Sydney` | 09:00 | 09:00 | 09:00 | **08:00** | **08:00** | **no** | | `Asia/Kolkata` | 09:00 | 09:00 | 09:00 | 09:00 | 09:00 | yes | A 09:00 job, advanced by exactly 24 hours each time, starting two days before each zone's first transition of 2026. **The clock reading is what the schedule promised and it is what moves**: a zone that springs forward takes the job to 10:00 and leaves it there, because nothing puts it back until the next transition six months later. The zones with no transitions hold, which is exactly why this is never caught in a test suite that runs in UTC. ## Constants and thresholds | Constant | Value | What it decides | | --- | --- | --- | | `EARLY_HOUR` | 7:00 | a participant reading earlier than this is worth naming | | `LATE_HOUR` | 21:00 | at or after this is worth naming | | `SHORT_RUN_DAYS` | 30 | how short a run of one difference has to be before it is called the odd one out | | `MANY_GAPS` | 2 | more distinct differences than this is worth naming on its own | | `DEFAULT_FROM` | 2026-01-01 | the window's start when the sheet does not say, chosen as a literal so a figure is the same on every run | | `DEFAULT_DAYS` | 365 | how long that window is; shorter than a year can miss a transition entirely | None of these decides an offset, an instant or a transition - those come from the runtime's own IANA database and are exact. The thresholds decide only which of the exact facts gets called a warning. ## Sheet grammar A sheet is a header of `KEY: value` lines and a `WITH:` block naming everyone else. ```text JOB: what this is (optional, echoed back) ZONE: America/New_York (the zone the wall clock is written in; assumed UTC) AT: 09:00 (the local reading; `9am` and `21:30` also read. Assumed 09:00) EVERY: tuesday (`day`, `weekdays`, a weekday name, `first monday`, `last friday`, or a day of the month. Assumed week) FROM: 2026-01-01 (the window to examine; assumed 2026-01-01) TO: 2027-01-01 (or `DAYS: 365`; assumed 365) WITH: