diff --git a/src/configs.ts b/src/configs.ts index f3bd561..ba903cf 100644 --- a/src/configs.ts +++ b/src/configs.ts @@ -18,6 +18,9 @@ export function init(opts: ConfigureOptions) { if ('time' in formats) { Object.assign(options.formats.time, formats.time) } + if ('dateTime' in formats) { + Object.assign(options.formats.dateTime, formats.dateTime) + } } return $locale.set(initialLocale) diff --git a/src/includes/formatters.ts b/src/includes/formatters.ts index 5037200..9d8c192 100644 --- a/src/includes/formatters.ts +++ b/src/includes/formatters.ts @@ -3,7 +3,7 @@ import { getOptions, getCurrentLocale } from './utils'; import { monadicMemoize } from './memoize' const getIntlFormatterOptions = ( - type: 'time' | 'number' | 'date', + type: 'time' | 'number' | 'date' | 'dateTime', name: string ): any => { const { formats } = getOptions(); @@ -67,3 +67,23 @@ export const getTimeFormatter: MemoizedIntlFormatter< return new Intl.DateTimeFormat(locale, options) }) + +export const getDateTimeFormatter: MemoizedIntlFormatter< + Intl.DateTimeFormat, + Intl.DateTimeFormatOptions +> = monadicMemoize(({ locale, format, ...options } = {}) => { + locale = locale || getCurrentLocale() + if (locale == null) { + throw new Error( + '[precompile-intl-runtime] A "locale" must be set to format datetime values' + ) + } + + if (format) { + options = getIntlFormatterOptions('dateTime', format); + } else if (Object.keys(options).length === 0) { + options = getIntlFormatterOptions('dateTime', 'short') + } + + return new Intl.DateTimeFormat(locale, options) +}) diff --git a/src/includes/utils.ts b/src/includes/utils.ts index fcd9901..45d7d5f 100644 --- a/src/includes/utils.ts +++ b/src/includes/utils.ts @@ -3,6 +3,7 @@ interface Formats { number: Record date: Record time: Record + dateTime: Record } interface Options { fallbackLocale: string @@ -12,6 +13,35 @@ interface Options { warnOnMissingMessages: boolean } +export const dateFormats = { + short: { month: 'numeric', day: 'numeric', year: '2-digit' }, + medium: { month: 'short', day: 'numeric', year: 'numeric' }, + long: { month: 'long', day: 'numeric', year: 'numeric' }, + full: { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }, +} as const; + +export const timeFormats = { + short: { hour: 'numeric', minute: 'numeric' }, + medium: { hour: 'numeric', minute: 'numeric', second: 'numeric' }, + long: { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + timeZoneName: 'short', + }, + full: { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + timeZoneName: 'short', + }, +} as const; + +const dateTimeFormat = Object.fromEntries( + (["short", "medium", "long", "full"] as const) + .map(f => [f, { dateStyle: dateFormats[f], timeStyle: timeFormats[f] }]) +); + export const defaultFormats: Formats = { number: { scientific: { notation: 'scientific' }, @@ -19,28 +49,9 @@ export const defaultFormats: Formats = { compactLong: { notation: 'compact', compactDisplay: 'long' }, compactShort: { notation: 'compact', compactDisplay: 'short' }, }, - date: { - short: { month: 'numeric', day: 'numeric', year: '2-digit' }, - medium: { month: 'short', day: 'numeric', year: 'numeric' }, - long: { month: 'long', day: 'numeric', year: 'numeric' }, - full: { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }, - }, - time: { - short: { hour: 'numeric', minute: 'numeric' }, - medium: { hour: 'numeric', minute: 'numeric', second: 'numeric' }, - long: { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - timeZoneName: 'short', - }, - full: { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - timeZoneName: 'short', - }, - }, + date: dateFormats, + time: timeFormats, + dateTime: dateTimeFormat, } const defaultOptions: Options = { diff --git a/src/index.ts b/src/index.ts index 7cd6c0d..78c2ccb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,6 +23,7 @@ export { $isLoading as isLoading } from './stores/loading' import { formatTime, formatDate, + formatDateTime, formatNumber } from './stores/formatters'; export { @@ -33,6 +34,7 @@ export { $formatDate as date, $formatNumber as number, $formatTime as time, + $formatDateTime as dateTime, $getJSON as json, } from './stores/formatters' @@ -41,6 +43,7 @@ export { getDateFormatter, getNumberFormatter, getTimeFormatter, + getDateTimeFormatter, } from './includes/formatters' @@ -80,3 +83,7 @@ export function __date(value: Date, format = "short"): string { export function __time(value: Date, format = "short"): string { return formatTime(getCurrentLocale(), value, { format }); } + +export function __dateTime(value: Date, format = "short"): string { + return formatDateTime(getCurrentLocale(), value, { format }); +} diff --git a/src/stores/formatters.ts b/src/stores/formatters.ts index 88a733c..b13d6f1 100644 --- a/src/stores/formatters.ts +++ b/src/stores/formatters.ts @@ -4,6 +4,7 @@ import { MessageFormatter, TimeFormatter, DateFormatter, + DateTimeFormatter, NumberFormatter, JsonGetter, } from '../types/index' @@ -12,6 +13,7 @@ import { hasLocaleQueue } from '../includes/loaderQueue' import { getTimeFormatter, getDateFormatter, + getDateTimeFormatter, getNumberFormatter, } from '../includes/formatters' import { getOptions, getCurrentLocale, getPossibleLocales } from '../includes/utils'; @@ -74,6 +76,11 @@ export const formatDate: DateFormatter = (currentLocale, d, options) => { return getDateFormatter({ locale, ...options }).format(d); } +export const formatDateTime: DateTimeFormatter = (currentLocale, dt, options) => { + const locale = currentLocale || getCurrentLocale(); + return getDateTimeFormatter({ locale, ...options }).format(dt) +} + export const formatNumber: NumberFormatter = (currentLocale, n, options) => { const locale = currentLocale || getCurrentLocale(); return getNumberFormatter({ locale, ...options }).format(n) @@ -82,5 +89,6 @@ export const formatNumber: NumberFormatter = (currentLocale, n, options) => { export const $format = /*@__PURE__*/derived([$locale, $dictionary], ([currentLocale]) => formatMessage.bind(null, currentLocale)); export const $formatTime = /*@__PURE__*/derived([$locale], ([currentLocale]) => formatTime.bind(null, currentLocale)); export const $formatDate = /*@__PURE__*/derived([$locale], ([currentLocale]) => formatDate.bind(null, currentLocale)); +export const $formatDateTime = /*@__PURE__*/derived([$locale], ([currentLocale]) => formatDateTime.bind(null, currentLocale)); export const $formatNumber = /*@__PURE__*/derived([$locale], ([currentLocale]) => formatNumber.bind(null, currentLocale)); export const $getJSON = /*@__PURE__*/derived([$locale, $dictionary], () => getJSON); diff --git a/src/types/index.ts b/src/types/index.ts index ce61304..9815836 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -45,6 +45,12 @@ export type DateFormatter = ( options?: IntlFormatterOptions ) => string +export type DateTimeFormatter = ( + currentLocale: string, + d: Date | number, + options?: IntlFormatterOptions +) => string + export type NumberFormatter = ( currentLocale: string, d: number,