Backend · TypeScript, Node.js

The timezone bug that judged people unfairly

The problem

The system judges each salesperson's week: did they hit their calling target between Monday and Sunday? Miss it, and a case is raised with their manager. That verdict affects someone's standing at work, so the week boundary has to be exactly right.

What failed first

I shipped the bug myself. My first version used the server's local date maths: build a date, set the time to midnight. It worked perfectly, because the server was in India.

On a server running in UTC, the Monday-to-Sunday window slides by five and a half hours. Work logged on Sunday evening, the end-of-week push the verdict exists to count, lands in the wrong week. The person did the work, and the system says they didn't.

The trap worth knowing: setting the scheduled job's timezone does not fix this. The job's timezone decides when it runs. It does nothing about which week a timestamp falls into. Those are two different problems that look like one.

The decision

Week boundaries are computed as exact instants in the organisation's own timezone, using offsets read from the Intl API.

// How far `timeZone` is ahead of UTC at a given instant, in milliseconds.
function offsetAt(instant: number, timeZone: string): number {
  const parts = new Intl.DateTimeFormat("en-US", {
    timeZone,
    hourCycle: "h23",
    year: "numeric", month: "2-digit", day: "2-digit",
    hour: "2-digit", minute: "2-digit", second: "2-digit",
  }).formatToParts(instant);
  const part = (type: string) => Number(parts.find((p) => p.type === type)?.value);
  const wall = Date.UTC(part("year"), part("month") - 1, part("day"),
    part("hour"), part("minute"), part("second"));
  return wall - (instant - (instant % 1000));
}

// The UTC instant of local midnight on a calendar date in `timeZone`.
export function zonedMidnight(year: number, month: number, day: number, timeZone: string) {
  const wall = Date.UTC(year, month - 1, day);
  const guess = wall - offsetAt(wall, timeZone); // first guess
  return wall - offsetAt(guess, timeZone);       // re-read the offset where we landed
}

The fairness rule sits on top: the system only judges a week once it is completely over in the organisation's timezone, so someone on a Monday-to-Saturday schedule can still catch up on Sunday.

The result

The module has no imports at all, on purpose, so its tests run on their own without starting the application. The same suite runs under TZ=UTC, TZ=Asia/Kolkata and TZ=America/New_York, and all three runs must produce identical results. Not similar. Identical.

When your code judges people, "works on my server" is not a passing grade.

All case studies