import { DateTime, Duration } from 'luxon' const DURATION_PLACEHOLDER = '-- h -- min' // Luxon ships no types in Pimp (see src/types/luxon.d.ts) — this is the slice of // DateTime/Duration's shape CpTripDetails actually relies on. export interface TripDuration { shiftTo(...units: string[]): TripDuration toHuman(opts: { maximumFractionDigits?: number; unitDisplay?: string }): string toISO(): string | null toObject(): Record } export interface TripDateTime { diff(other: TripDateTime, units: string[]): TripDuration toFormat(format: string): string toISO(): string | null } export const fromGMTToLocal = (date: string, zone: string, locale = 'en'): TripDateTime => { return DateTime.fromISO(date, { zone: 'utc' }).setZone(zone).setLocale(locale) } export const getDuration = (end: TripDateTime, start: TripDateTime): TripDuration => { return end.diff(start, ['hours', 'minutes']) } export const getFormattedDuration = (duration?: TripDuration | null, locale = 'en'): string => { if (!duration) return DURATION_PLACEHOLDER // Concatenates values to make toHuman more readable (ex: 26h becomes 1d, 2hr) const shiftedDuration = duration.shiftTo('hours', 'minutes').toObject() const entries = Object.entries(shiftedDuration).filter(([, amount]) => amount) const formattedDuration = Duration.fromObject(Object.fromEntries(entries), { locale }) return ( formattedDuration.toHuman({ unitDisplay: 'short', maximumFractionDigits: 0, }) || DURATION_PLACEHOLDER ) } export const getDayCountBetweenDates = ({ departureDate, arrivalDate, isArrival = false, }: { arrivalDate: TripDateTime departureDate: TripDateTime isArrival?: boolean }): number => { const departureDay = departureDate.toFormat('yyyy-LL-dd') const arrivalDay = arrivalDate.toFormat('yyyy-LL-dd') if (isArrival) { return DateTime.fromISO(departureDay).diff(DateTime.fromISO(arrivalDay), 'days').days } return DateTime.fromISO(arrivalDay).diff(DateTime.fromISO(departureDay), 'days').days }