All files / shared/utils zonedTime.ts

100% Statements 8/8
100% Branches 6/6
100% Functions 2/2
100% Lines 8/8

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50      681x         681x             27x 27x                                   681x           27x 27x       27x        
import { TZDate } from "@date-fns/tz";
import { TIMEZONE } from "shared/utils/timezone";
 
const HOUR_MS = 60 * 60 * 1000;
 
// Whether an instant lands on exactly this wall-clock hour in the event timezone.
// Converting a known instant into a timezone is unambiguous, so this is the same
// answer on every host.
const rendersAtWallClockHour = (
  instant: Date,
  year: number,
  month: number,
  day: number,
  hour: number,
): boolean => {
  const zoned = new TZDate(instant, TIMEZONE);
  return (
    zoned.getFullYear() === year &&
    zoned.getMonth() === month &&
    zoned.getDate() === day &&
    zoned.getHours() === hour
  );
};
 
// The instant at a given wall-clock hour in the event timezone.
//
// The hour the autumn transition repeats occurs twice, and which of the two the
// TZDate constructor lands on depends on the host's own timezone - so a UTC
// server and a Finnish browser would disagree by an hour about the same
// configured time. The first occurrence is picked explicitly here, which is the
// same answer everywhere.
//
// The hour the spring transition skips does not exist at all; that resolves
// forward, to the same instant the wall clock an hour later would give.
export const atWallClockHourInEventTimezone = (
  year: number,
  month: number,
  day: number,
  hour: number,
): Date => {
  const candidate = new TZDate(year, month, day, hour, 0, 0, TIMEZONE);
  const hourEarlier = new Date(candidate.getTime() - HOUR_MS);
 
  // Rendering the same wall clock an hour earlier means the constructor landed
  // on the second occurrence of a repeated hour
  return rendersAtWallClockHour(hourEarlier, year, month, day, hour)
    ? hourEarlier
    : new Date(candidate);
};