Earlier this week I watched a scheduled job do exactly what it was told and still corrupt a day’s worth of data. The logs said success, the database said otherwise, and the only user who noticed was the one who received yesterday’s digest twice. Have you ever debugged a failure that never raised an exception?
I needed a tiny daily report that aggregates the previous day’s events and posts a summary. I drafted the first version with MonkeyCode’s free model access because the task felt mechanical, and I hosted it on the free server option to avoid spinning up a full VM. Disclosure: This article was prepared as part of MonkeyCode’s product outreach. The model wrote clean-looking code, the tests passed locally, and I deployed it without thinking about timezones.
The symptom was boring
At 00:30 the report arrived with the wrong date on it. At 00:30 in my timezone, the job labeled the report with the previous day, and the previous day’s report also contained the previous day’s data. Users saw the same digest twice, and the log showed a clean run with no errors, which is the most dangerous kind of log.
The generated code looked innocent enough:
const today = new Date().toISOString().slice(0, 10);
const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
Enter fullscreen mode Exit fullscreen mode
Spot the problem? toISOString() always returns UTC. At 00:30 in UTC+8, UTC is still the previous day, so today was the previous day. Subtracting a fixed number of milliseconds also ignores daylight saving time, which makes the bug worse in any timezone that observes it.
Root cause: missing context, not bad logic
The root cause was not the model’s logic; it was the context I never provided. I never told the model that the business day starts at midnight in Asia/Shanghai while the server runs in UTC. The generated code was correct for a machine in UTC and wrong for the product’s calendar, and that distinction is exactly what a model cannot infer from a prompt like “generate a daily report”.
The debugging loop that found it
1. Reproduce with a fixed clock
Stop waiting for midnight. I refactored the script to accept a --now argument, and then the boundary became a five-second repro:
TZ=UTC node report.js --now 2026-08-19T16:30:00.000Z
TZ=Asia/Shanghai node report.js --now 2026-08-19T16:30:00.000Z
Enter fullscreen mode Exit fullscreen mode
Same instant, two different business days. That single command turned a once-a-day bug into something I could run in a loop.
2. Log timezone, not just time
Every log line should include the offset or the timezone name. I started logging both new Date().toISOString() and the business day computed by the job, so the next failure would be visible in the log instead of hiding behind a green checkmark.
3. Separate the instant from the calendar
Store instants as UTC, but compute business dates with Intl.DateTimeFormat and an explicit timeZone:
export function businessDay(now, timeZone) {
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).formatToParts(now);
const values = Object.fromEntries(parts.map((part) => [part.type, part.value]));
return `${values.year}-${values.month}-${values.day}`;
}
Enter fullscreen mode Exit fullscreen mode
en-CA gives you the ISO-like YYYY-MM-DD format without manual padding. The function is pure, which means you can test it with any instant and any timezone.
4. Turn the fix into a regression test
A frozen clock turns a heisenbug into a deterministic test:
import { describe, it, expect, vi } from 'vitest';
import { businessDay } from './report.js';
describe('businessDay', () => {
it('returns the Shanghai date when UTC is still the previous day', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-08-19T16:30:00.000Z'));
expect(businessDay(new Date(), 'Asia/Shanghai')).toBe('2026-08-20');
vi.useRealTimers();
});
});
Enter fullscreen mode Exit fullscreen mode
Now the bug is a regression test, not a memory. Anyone who changes the date logic will see the failure in CI before it reaches the cron job.
What I learned
- The model did not fail; my specification failed. I asked for a daily report without defining what “day” meant.
- A clean log is not proof of correctness. It is proof that the code did not throw, which is a much lower bar.
- Timezones are not a formatting concern; they are a data-integrity concern. One wrong boundary can duplicate or drop a day of events.
Limitations and who should not use this
This fix is not universal. Asia/Shanghai has no daylight saving time, so I avoided the DST trap by luck; if your timezone observes DST, use a library like Temporal or date-fns-tz instead of hand-rolled math. The approach also assumes exactly one business timezone, so if your users span multiple timezones, compute the boundary per user and store the instant, not the label. And if you are using a free server option, remember that you are responsible for knowing what timezone the container thinks it is in; a job that runs on time in UTC can still be a day late for your users.
The next time a model hands you date logic, ask it to define the timezone before you merge. Better yet, write the failing test first and let the model fix that test. The debugging loop I used here — fixed clock, explicit timezone, pure function, regression test — works whether the code came from a human or from free model access. The only difference is how quickly you trust the output.