Files
edr-platform/apps/edr-passenger-web/portal/src/utils/format.ts
2026-07-14 14:37:57 +03:00

49 lines
2.2 KiB
TypeScript

import { format } from 'date-fns';
// The backend stores and computes every train schedule in Ethiopian time (EAT -
// UTC+3) — see apps/edr-passenger-api's timezone.utils.ts and main.ts, which pin the
// server process to this zone. The frontend previously formatted dates using
// whatever timezone the viewing/generating device happened to be set to, which made
// the on-screen booking pages and the downloaded voucher PDF disagree (by exactly
// the device's offset from EAT) whenever that device wasn't itself in EAT. Pinning
// every display here to the same explicit zone makes them agree by construction.
const APP_TIMEZONE = 'Africa/Addis_Ababa';
// date-fns' `format()` always reads the Date object's LOCAL (device) getters — it has
// no built-in timezone conversion. This shifts the Date so those local getters report
// the wall-clock time in `timeZone` instead of the device's own zone, so `format()`
// calls (here and at other call sites formatting journey times) produce EAT-correct
// output regardless of the device. Exported for pages that call date-fns' `format()`
// directly instead of going through the helpers below.
export function toZonedDate(date: Date, timeZone: string = APP_TIMEZONE): Date {
return new Date(date.toLocaleString('en-US', { timeZone }));
}
export const formatCurrency = (amount: number, currency: string = 'ETB'): string => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency,
minimumFractionDigits: 2,
}).format(amount / 100);
};
export const formatDate = (date: string | Date, formatStr: string = 'MMM dd, yyyy'): string => {
return format(toZonedDate(new Date(date), APP_TIMEZONE), formatStr);
};
export const formatDateTime = (date: string | Date): string => {
return format(toZonedDate(new Date(date), APP_TIMEZONE), 'MMM dd, yyyy h:mm a');
};
export const formatTime = (date: string | Date): string => {
return format(toZonedDate(new Date(date), APP_TIMEZONE), 'h:mm a');
};
export const getTimePeriod = (date: string | Date): string => {
const hour = toZonedDate(new Date(date), APP_TIMEZONE).getHours();
if (hour >= 5 && hour < 12) return 'Morning';
if (hour >= 12 && hour < 17) return 'Afternoon';
if (hour >= 17 && hour < 21) return 'Evening';
return 'Night';
};