𝗠𝘆 𝗔𝗣𝗜 𝗕𝗿𝗼𝗸𝗲 𝗘𝘃𝗲𝗿𝘆 𝗝𝗮𝗻𝘂𝗮𝗿𝘆 𝟭𝘀𝘁

My API broke at exactly 00:00 UTC on January 1st.

It did not break at the users' midnight. It broke at UTC midnight. Users in Tokyo saw broken data starting at 9 AM their time.

The tests passed. The staging environment worked. The error only existed in production because the production server used a different timezone than staging.

The problem lived in this logic:

function getDailyReport(date) { const start = new Date(date).toISOString().split('T')[0]; const end = new Date(start + 'T23:59:59Z');

return db.reports.findMany({ where: { createdAt: { gte: new Date(start), lt: end } } }); }

When you pass a date like "2026-01-01" without a time, the system uses the local timezone.

Our staging server used UTC. Our production server used US-East. This created a five hour offset on every single date query.

We lost five hours of data on every query. The numbers looked close enough that nobody noticed for a full year.

The fix was simple. Force UTC by appending time to the string:

function getDailyReport(date: string) { const start = new Date(${date}T00:00:00Z); const end = new Date(${date}T23:59:59.999Z);

return db.reports.findMany({ where: { createdAt: { gte: start, lt: end } } }); }

The code change took seconds. The system fix took a week. We did these four things:

• Added a timezone assertion in CI to ensure the environment stays in UTC. • Set TZ=UTC in all Dockerfiles to keep every container identical. • Added a timezone check to the deploy script. • Wrote a linter rule to flag any Date constructor that lacks timezone info.

Timezone bugs are dangerous. They do not crash your system. They produce wrong data that looks correct. Your users will not see an error page. They will see incorrect numbers and they will trust them.

Follow these three rules:

  • Never trust the system timezone. Always set TZ=UTC.
  • Never parse dates without timezones.
  • Never assume your CI timezone matches production.

Source: https://dev.to/kollittle/my-api-broke-every-january-1st-the-timezone-bug-i-should-have-caught-in-code-review-51hb