import { DateValue, parseAbsoluteToLocal, ZonedDateTime, toZoned, getLocalTimeZone, parseAbsolute, } from "@internationalized/date"; import { DateRange } from "react-aria"; import { DatePickerAuxSubProps } from ":/components/Forms/DatePicker/DatePickerAux"; /** * Checks if a given time zone is valid and supported by the environment. * @param timezone The time zone to validate. * @returns true if the time zone is valid and supported, false otherwise. */ export const isValidTimeZone = (timezone: string) => { try { // Check if Intl is available and supports time zones if (!Intl || !Intl.DateTimeFormat().resolvedOptions().timeZone) { throw new Error("Time zones are not available in this environment"); } // Test if the provided time zone is valid Intl.DateTimeFormat(undefined, { timeZone: timezone }); return true; // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (_error) { // If an error occurs, it could be due to an invalid time zone or lack of Intl support return false; } }; /** * Parses an ISO 8601 date and time string with a UTC offset or in UTC. * @param rawDate - The raw date string to parse. * @param timezone - (Optional) The timezone to use for parsing. If not provided, the local timezone will be used. * @returns The parsed ZonedDateTime object or undefined if the input date is undefined. * @throws {Error} - If an invalid date or timezone format is encountered. */ export const parseDateValue = ( rawDate: string | undefined, timezone?: string, ): undefined | ZonedDateTime => { if (!rawDate) { return undefined; } try { if (timezone && !isValidTimeZone(timezone)) { throw new Error("Invalid timezone provided."); } return timezone ? parseAbsolute(rawDate, timezone) : parseAbsoluteToLocal(rawDate); } catch (e) { const errorMessage = e instanceof Error ? ": " + e.message : "."; throw new Error("Failed to parse date value" + errorMessage); } }; /** * Parses a range of ISO 8601 date and time string with a UTC offset or in UTC. * @param rawRange - The range of raw date strings to parse. * @param timezone - (Optional) The timezone to use for parsing. If not provided, the local timezone will be used. * @returns The parsed DateRange object or undefined if the input date range is undefined or uncomplete. */ export const parseRangeDateValue = ( rawRange: [string, string] | undefined, timezone?: string, ): DateRange | undefined => { if (!rawRange || !rawRange[0] || !rawRange[1]) { return undefined; } return { start: parseDateValue(rawRange[0], timezone)!, end: parseDateValue(rawRange[1], timezone)!, }; }; /** * Converts a DateValue object to an ISO 8601 formatted string in UTC. * @param date - The DateValue object to convert. * @param timezone - (Optional) The timezone to use for the conversion. If not provided, the local timezone will be used. * @returns The string representation of the date or an empty string if the input date is null. * @throws {Error} - If an invalid date or timezone format is encountered. */ export const convertDateValueToString = ( date: DateValue | null, timezone?: string, ): string => { try { if (!date) { return ""; } const localTimezone = timezone || getLocalTimeZone(); if (!isValidTimeZone(localTimezone)) { throw new Error("Invalid timezone provided."); } return toZoned(date, localTimezone).toAbsoluteString(); } catch (e) { const errorMessage = e instanceof Error ? ": " + e.message : "."; throw new Error("Failed to convert date value to string" + errorMessage); } }; export const getDefaultPickerOptions = (props: DatePickerAuxSubProps): any => ({ minValue: parseDateValue(props.minValue, props.timezone), maxValue: parseDateValue(props.maxValue, props.timezone), shouldCloseOnSelect: true, granularity: "day", isDisabled: props.disabled, label: props.label, });