20 September 2026
How countdowns actually work across timezones (and why yours may be wrong)
Most 'days until Christmas' counters lie to half the internet. This post walks through why a countdown built in New York disagrees with one built in Tokyo — and how to make one that doesn't.
Every December, my group chat lights up with people arguing about how many days are left until Christmas. Someone in London says twelve. Someone in Sydney says eleven. Someone in Los Angeles insists it's still thirteen. Nobody has miscounted. They're all reading different countdowns.
This kind of disagreement is the norm for any live countdown that spans timezones — and the root cause is one of the sneakiest bugs in date-arithmetic land. Once you see it, you'll spot it in half the countdown apps on the web.
Why "how many days until Christmas" is a harder question than it sounds
At first glance, the answer looks trivial: take today's date, subtract it from December 25, print the number of days. And it is trivial — until you notice that "today's date" and "December 25" both need a timezone to be meaningful.
Consider this concrete example. Right now it's 11 pm on 23 December in London, and 8 am on 24 December in Tokyo. If a countdown asks "how many days until Christmas?" and both users are looking at the same page, what should it show?
- London user: Christmas Day starts at 00:00 on 25 December in London. Twenty-five hours away. The counter should say 1 day, 1 hour.
- Tokyo user: Christmas Day starts at 00:00 on 25 December in Tokyo. Sixteen hours away. The counter should say 16 hours.
Two different answers to the same question, both correct. Any countdown that shows the same number to both is lying to at least one of them.
The three common bugs
Most countdown apps handle this incorrectly. There are three flavors of mistake, and each is easy to make by accident.
Bug 1: Counting to a UTC midnight
The laziest implementation is to represent the target as an instant in time — say, "Christmas is at 2026-12-25T00:00:00Z" — and subtract it from the current UTC time. Simple, but wrong for almost everyone. UTC midnight on Christmas Day is 4 pm on 24 December in Los Angeles and 9 am on 25 December in Tokyo. The countdown ends at completely different local moments depending on where you are.
You can spot this bug because the counter always ends at the same global moment for every viewer, regardless of their location.
Bug 2: Counting to the server's local midnight
A slightly better version computes the target using the server's timezone. If the server is in Frankfurt, everyone sees a countdown to midnight Frankfurt time — the countdown ends at the right moment for Germans and the wrong moment for everyone else.
You can spot this bug by comparing what the counter says to a viewer in Melbourne with what a viewer in Cape Town sees at the same instant. If the two numbers differ by the offset between Frankfurt and each of their timezones (rather than by the offset between Melbourne and Cape Town), the server timezone is leaking through.
Bug 3: Counting to your local midnight — but computed wrong
The correct behaviour is to count to your local midnight on the target date. Getting this right requires resolving "December 25" to a moment in your timezone, not the server's, not UTC's.
Even developers who know this sometimes get it wrong. The classic mistake
is to construct the target with new Date("2026-12-25") in JavaScript,
which parses as UTC midnight. On the client that gets displayed in local
time, which usually means the countdown ends at the wrong hour on Christmas
Eve. The right approach is new Date(2026, 11, 25) — the numeric
constructor, which builds the date in the browser's local timezone.
Why "days" is even weirder than "hours"
Time-difference bugs get worse when you round down to whole days. Consider a target that's 30 hours away. Is that "1 day" or "2 days"?
The intuitive answer is "1 day, 6 hours" — 30 hours is more than one day but less than two. But many countdowns show a big number labeled "days" without any hours breakdown, so the choice between rounding up ("2 days") and rounding down ("1 day") matters a lot for perception.
We picked Math.ceil — always round up — because the natural reading of a
countdown is "how many sleeps until X". If Christmas Eve is coming, you
want to see "1 day" until midnight, not "0". That said, the tick that
follows shows hours and minutes and seconds too, so the whole thing is
self-consistent: 1 day, 3 hours, 27 minutes, 14 seconds still reads as one
sleep to go.
The daycount approach
The days-until page on daycount handles this by building the
target date with the plain numeric new Date() constructor. That
constructor uses the browser's local timezone. Then we take the difference
in milliseconds and divide.
Here's the essential logic, simplified:
// Server side: compute the current year's target
const target = nextOccurrence({month: 12, day: 25}, new Date());
// Client side (via useNow hook):
const now = new Date();
const diffMs = target.getTime() - now.getTime();
const days = Math.max(0, Math.ceil(diffMs / 86400000));
The target is always constructed in the viewer's timezone, because
new Date(year, month, day) reads the browser's zone. That means a viewer
in Sydney sees a target aligned with 25 December 00:00 in Sydney, and a
viewer in Los Angeles sees a target aligned with 25 December 00:00 in Los
Angeles. Neither viewer's countdown is a lie.
You can verify this on any of our holiday countdown pages — open the same page in two browser windows with different system timezones and watch the numbers disagree, but each be correct in its own frame.
What about the "rolling" versus "dated" split?
There's a second layer of nuance. Two very different questions look similar:
- "How many days until Christmas?" — the next Christmas, whichever year that happens to be. If it's already January, roll to the next December.
- "How many days until Christmas 2026?" — a specific dated event that won't roll over.
Most countdown apps only answer one. We answer both, at different URLs:
- The rolling question lives at /days-until/christmas, which auto-rolls to the next occurrence on the morning of 26 December each year.
- The dated question lives at /countdown/christmas-2026, which counts down to that specific date and, once it's passed, shows "already happened" rather than rolling.
The rolling page is useful for "add this to my calendar" or "I want a counter I can bookmark forever". The dated page is useful for "I want to share this specific 2026 date with my friends" and for the year-specific queries people type into Google. Both are cross-linked, so from either page you can jump to the other.
Daylight saving is a separate rabbit hole
If you're really paying attention, you'll notice we haven't talked about daylight saving. When the clocks go back at the end of October in Europe, the 25 October has 25 hours in it. When they go forward at the end of March, 27 March has 23 hours. A countdown that spans one of those changes loses or gains an hour.
For days until we don't care, because our unit is calendar days and
Math.ceil handles the smoothing. For a countdown showing hours, minutes,
and seconds it does matter — but only for viewers in a timezone that
observes DST, and only for the hour of the changeover. In practice the
difference is invisible unless you're stress-testing.
For timezone conversion pages, DST matters much more, because the offset between two cities depends on their current DST state. New York is 5 hours behind London in December, but 4 hours behind in July. We recompute offsets against the browser's live IANA rules every time you load the page, so you always see the currently-correct number.
The takeaway
Countdowns look trivial. They aren't. Any counter that shows the same number to every viewer, regardless of timezone, is silently wrong for most of them. The fix is not conceptually hard — resolve the target date in the viewer's timezone — but it's easy enough to get wrong that most apps don't bother.
If you build a countdown into your own product, the three things worth checking:
- Does the target date get constructed in the viewer's timezone? Test by opening it from two devices set to different zones and comparing. The numbers should differ by the offset between them.
- Does the counter show the same total-day number to a viewer at 11 pm as to a viewer at 1 am the same day in the same city? If it flips at UTC midnight instead of at local midnight, you've got the UTC bug.
- Does the counter roll to the next year automatically after the target passes? If not, you'll have a stale "days until Christmas 2026" page that says -14 days on 8 January 2027.
If you'd rather not build your own, that's what we made daycount for. Every counter on the site does the right thing in every timezone, no signup, all client-side.