All files / client/src/utils useTimeFormatters.ts

82.35% Statements 14/17
100% Branches 0/0
80% Functions 12/15
82.35% Lines 14/17

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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84                                                                608x 6119x   6119x 3297x 289x 15x 662x   23981x 634x 23936x         6x   103x         2823x   459x                                                    
import { useMemo, useSyncExternalStore } from "react";
import { getLocaleSnapshot, subscribeToLocale } from "shared/utils/setLocale";
import {
  Time,
  formatProgramItemDuration,
  formatRelativeTime,
  formattedCurrentTime,
  getDate,
  getDateAndTime,
  getLocalDateAndTime,
  getLocalTimezone,
  getShortDate,
  getShortWeekdayAndTime,
  getTime,
  getTimezone,
  getWeekdayAndTime,
} from "shared/utils/timeFormatter";
import {
  getFormattedInterval,
  getFormattedTime,
} from "client/views/program-item/programItemUtils";
 
// The formatters, bound to the language the UI is currently in. Components take
// them from here rather than importing them directly, which a lint rule
// enforces: the active language lives in module state, which React cannot see,
// so a component formatting a time has nothing to re-render or recompute on when
// the language changes and keeps the string it first produced.
//
// Binding them here gives that dependency a place to live. The returned
// functions get a new identity on a switch, which invalidates every memoized
// expression that calls one, so call sites read exactly as they would if the
// language were not a concern at all.
export const useTimeFormatters = (): TimeFormatters => {
  const locale = useSyncExternalStore(subscribeToLocale, getLocaleSnapshot);
 
  return useMemo(
    () => ({
      getWeekdayAndTime: (time: Time) => getWeekdayAndTime(time, locale),
      getDateAndTime: (time: Time) => getDateAndTime(time, locale),
      getTime: (time: Time) => getTime(time, locale),
      getShortWeekdayAndTime: (time: Time) =>
        getShortWeekdayAndTime(time, locale),
      getShortDate: (time: Time) => getShortDate(time, locale),
      getDate: (time: Time) => getDate(time, locale),
      getTimezone: (time: Time) => getTimezone(time, locale),
      getLocalDateAndTime: (time: Time) => getLocalDateAndTime(time, locale),
      getLocalTimezone: (time: Time) => getLocalTimezone(time, locale),
      formattedCurrentTime: (currentTime: Date) =>
        formattedCurrentTime(currentTime, locale),
      formatRelativeTime: (from: Date, to: Date) =>
        formatRelativeTime(from, to, locale),
      // Carries no language of its own, and is here so that nothing in the
      // client has a reason to reach for the formatter module directly
      formatProgramItemDuration,
      getFormattedTime: (time: Date, timeNow: Date) =>
        getFormattedTime(time, timeNow, locale),
      getFormattedInterval: (startTime: Date, endTime: Date, timeNow: Date) =>
        getFormattedInterval(startTime, endTime, timeNow, locale),
    }),
    [locale],
  );
};
 
interface TimeFormatters {
  getWeekdayAndTime: (time: Time) => string;
  getDateAndTime: (time: Time) => string;
  getTime: (time: Time) => string;
  getShortWeekdayAndTime: (time: Time) => string;
  getShortDate: (time: Time) => string;
  getDate: (time: Time) => string;
  getTimezone: (time: Time) => string;
  getLocalDateAndTime: (time: Time) => string;
  getLocalTimezone: (time: Time) => string;
  formattedCurrentTime: (currentTime: Date) => string;
  formatRelativeTime: (from: Date, to: Date) => string;
  formatProgramItemDuration: (mins: number) => string;
  getFormattedTime: (time: Date, timeNow: Date) => string;
  getFormattedInterval: (
    startTime: Date,
    endTime: Date,
    timeNow: Date,
  ) => string;
}