UNPKG

@sanity/util

Version:

Utilities shared across projects of Sanity

281 lines (280 loc) • 11.6 kB
import { TZDateMini } from "@date-fns/tz"; import { UTCDateMini } from "@date-fns/utc"; import { format as format$1, parse as parse$1, parseISO } from "date-fns"; const sanitizeLocale = (locale) => locale.replace(/@posix$/, ""); function getMonthName(date, style = "long", locale = "en-US") { const validLocale = sanitizeLocale(locale); return new Intl.DateTimeFormat(validLocale, { month: style }).format(date); } function getDayName(date, style = "long", locale = "en-US") { const validLocale = sanitizeLocale(locale); return new Intl.DateTimeFormat(validLocale, { weekday: style }).format(date); } function getLocalizedDate(date, options, locale = "en-US") { const validLocale = sanitizeLocale(locale); return new Intl.DateTimeFormat(validLocale, options).format(date); } function zeroPad(num, length) { return String(num).padStart(length, "0"); } function getOrdinal(day) { const j = day % 10, k = day % 100; return j === 1 && k !== 11 ? `${day}st` : j === 2 && k !== 12 ? `${day}nd` : j === 3 && k !== 13 ? `${day}rd` : `${day}th`; } function getISODayOfWeek(date) { const dow = date.getDay(); return dow === 0 ? 7 : dow; } function getISOWeekYear(date) { const temp = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())), dayOfWeek = getISODayOfWeek(temp); return temp.setUTCDate(temp.getUTCDate() - dayOfWeek + 4), temp.getUTCFullYear(); } function getISOWeekNumber(date) { const temp = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())), dayOfWeek = getISODayOfWeek(temp); temp.setUTCDate(temp.getUTCDate() - dayOfWeek + 4); const yearStart = new Date(Date.UTC(temp.getUTCFullYear(), 0, 1)); return Math.ceil(((temp.valueOf() - yearStart.valueOf()) / 864e5 + 1) / 7); } function getDayOfYear(date) { const startOfYear = new Date(Date.UTC(date.getFullYear(), 0, 1)), diff = date.valueOf() - startOfYear.valueOf() + (startOfYear.getTimezoneOffset() - date.getTimezoneOffset()) * 6e4; return Math.floor(diff / (1e3 * 60 * 60 * 24)) + 1; } function getLocaleWeekYear(date) { return getISOWeekYear(date); } function getFractionalSeconds(date, length) { const ms = zeroPad(date.getMilliseconds(), 3); return length === 1 ? ms.slice(0, 1) : length === 2 ? ms.slice(0, 2) : length === 3 ? ms : `${ms}0`; } function getTimeZoneAbbreviation(date) { const tz = new Intl.DateTimeFormat(sanitizeLocale("en-US"), { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName"); return tz ? tz.value : ""; } function formatMomentLike(date, formatStr) { const escapeSequences = [], escapeToken = "\uE000", processedFormat = formatStr.replace(/\[([^\]]+)\]/g, (_, contents) => (escapeSequences.push(contents), escapeToken)), year = date.getFullYear(), monthIndex = date.getMonth(), dayOfMonth = date.getDate(), dayOfWeek = date.getDay(), hours = date.getHours(), minutes = date.getMinutes(), seconds = date.getSeconds(), isoWeekNum = getISOWeekNumber(date), isoWeekYear = getISOWeekYear(date), localeWeekYear = getLocaleWeekYear(date), unixMs = date.getTime(), unixSec = Math.floor(unixMs / 1e3), tokens = [ // Year // 1970 1971 ... 2029 2030 { key: "YYYY", getValue: () => String(year) }, // 70 71 ... 29 30 { key: "YY", getValue: () => String(year).slice(-2) }, // 1970 1971 ... 9999 +10000 +10001 { key: "Y", getValue: () => String(year) }, // Expanded years, -001970 -001971 ... +001907 +001971 { key: "YYYYY", getValue: () => zeroPad(year, 5) }, // ISO week-year // 1970 1971 ... 2029 2030 { key: "GGGG", getValue: () => String(isoWeekYear) }, // 70 71 ... 29 30 { key: "GG", getValue: () => String(isoWeekYear).slice(-2) }, // "locale" week-year { key: "gggg", getValue: () => String(localeWeekYear) }, { key: "gg", getValue: () => String(localeWeekYear).slice(-2) }, // Quarter { key: "Q", getValue: () => String(Math.floor(monthIndex / 3) + 1) }, { key: "Qo", getValue: () => getOrdinal(Math.floor(monthIndex / 3) + 1) }, // --- Month (using Intl) --- { key: "MMMM", getValue: () => getMonthName(date, "long") }, // e.g. "January" { key: "MMM", getValue: () => getMonthName(date, "short") }, // e.g. "Jan" // For numeric months, we still do a manual approach: { key: "MM", getValue: () => zeroPad(monthIndex + 1, 2) }, { key: "M", getValue: () => String(monthIndex + 1) }, { key: "Mo", getValue: () => getOrdinal(monthIndex + 1) }, // Day of Month { key: "DD", getValue: () => zeroPad(dayOfMonth, 2) }, { key: "D", getValue: () => String(dayOfMonth) }, { key: "Do", getValue: () => getOrdinal(dayOfMonth) }, // --- Day of Week (using Intl) --- { key: "dddd", getValue: () => getDayName(date, "long") }, // e.g. "Monday" { key: "ddd", getValue: () => getDayName(date, "short") }, // e.g. "Mon" { key: "dd", // e.g. "Mo" => first 2 chars of short day name getValue: () => getDayName(date, "short").slice(0, 2) }, { key: "d", getValue: () => String(dayOfWeek) }, { key: "do", getValue: () => getOrdinal(dayOfWeek + 1) }, // Day of the year { key: "DDDD", getValue: () => zeroPad(getDayOfYear(date), 3) }, { key: "DDD", getValue: () => String(getDayOfYear(date)) }, { key: "DDDo", getValue: () => getOrdinal(getDayOfYear(date)) }, // ISO day of week { key: "E", getValue: () => String(getISODayOfWeek(date)) }, // Week of the year // w 1 2 ... 52 53 { key: "w", getValue: () => zeroPad(isoWeekNum, 2) }, // week 1st 2nd ... 52nd 53rd { key: "wo", getValue: () => getOrdinal(isoWeekNum) }, // 01 02 ... 52 53 { key: "ww", getValue: () => zeroPad(isoWeekNum, 2) }, // ISO Week { key: "WW", getValue: () => zeroPad(isoWeekNum, 2) }, { key: "W", getValue: () => String(isoWeekNum) }, { key: "Wo", getValue: () => getOrdinal(isoWeekNum) }, // or "locale" week => replace isoWeekNum // 24h hours { key: "HH", getValue: () => zeroPad(hours, 2) }, { key: "H", getValue: () => String(hours) }, // 12h hours { key: "hh", getValue: () => zeroPad((hours + 11) % 12 + 1, 2) }, { key: "h", getValue: () => String((hours + 11) % 12 + 1) }, // 1 2 ... 23 24 { key: "k", getValue: () => String(hours || 24) }, // 01 02 ... 23 24 { key: "kk", getValue: () => zeroPad(hours || 24, 2) }, // Minutes { key: "mm", getValue: () => zeroPad(minutes, 2) }, { key: "m", getValue: () => String(minutes) }, // Seconds { key: "ss", getValue: () => zeroPad(seconds, 2) }, { key: "s", getValue: () => String(seconds) }, // Fractional seconds (S..SSSS) => handled separately // Timezone offset (Z, ZZ) => handled separately // AM/PM { key: "A", getValue: () => hours < 12 ? "AM" : "PM" }, { key: "a", getValue: () => hours < 12 ? "am" : "pm" }, // Unix timestamps { key: "X", getValue: () => String(unixSec) }, { key: "x", getValue: () => String(unixMs) }, // Eras BC AD { key: "N", getValue: () => year < 0 ? "BC" : "AD" }, { key: "NN", getValue: () => year < 0 ? "BC" : "AD" }, { key: "NNN", getValue: () => year < 0 ? "BC" : "AD" }, // Before Christ, Anno Domini { key: "NNNN", getValue: () => year < 0 ? "Before Christ" : "Anno Domini" }, { key: "NNNNN", getValue: () => year < 0 ? "BC" : "AD" }, // Time zone offset { key: "z", getValue: () => getTimeZoneAbbreviation(date) }, { key: "zz", getValue: () => getTimeZoneAbbreviation(date) }, { key: "Z", getValue: () => format$1(date, "xxx") }, { key: "ZZ", getValue: () => format$1(date, "xx") }, // Time { key: "LTS", getValue: () => getLocalizedDate(date, { timeStyle: "medium" }) }, { key: "LT", getValue: () => getLocalizedDate(date, { timeStyle: "short" }) }, // Date (uppercase = longer names) { key: "LLLL", getValue: () => getLocalizedDate(date, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" }) }, { key: "LLL", getValue: () => getLocalizedDate(date, { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" }) }, { key: "LL", getValue: () => getLocalizedDate(date, { year: "numeric", month: "long", day: "numeric" }) }, { key: "L", getValue: () => getLocalizedDate(date, { year: "numeric", month: "2-digit", day: "2-digit" }) }, // Date (lowercase = shorter names) { key: "llll", getValue: () => getLocalizedDate(date, { weekday: "short", year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" }) }, { key: "lll", getValue: () => getLocalizedDate(date, { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" }) }, { key: "ll", getValue: () => getLocalizedDate(date, { year: "numeric", month: "short", day: "numeric" }) }, { key: "l", getValue: () => getLocalizedDate(date, { year: "numeric", month: "numeric", day: "numeric" }) } ]; tokens.sort((a, b) => b.key.length - a.key.length); const fracSecRegex = new RegExp("(?<!LT)S{1,4}", "g"); let output = processedFormat.replace(fracSecRegex, (match) => getFractionalSeconds(date, match.length)); for (const { key, getValue } of tokens) { const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), tokenRegex = new RegExp(`(^|[^A-Z0-9a-z])(${escapedKey})(?![A-Z0-9a-z])`, "g"); if (output.match(tokenRegex)) { const value = getValue(); output = output.replace(tokenRegex, `$1${value}`); } } return output = output.replace(new RegExp(escapeToken, "g"), () => escapeSequences.shift() || ""), output; } function momentToDateFnsFormat(momentFormat) { const formatMap = { YYYY: "yyyy", YY: "yy", MMMM: "MMMM", MMM: "MMM", MM: "MM", M: "M", DD: "dd", D: "d", dddd: "EEEE", ddd: "EEE", HH: "HH", H: "H", hh: "hh", h: "h", mm: "mm", m: "m", ss: "ss", s: "s", A: "a", a: "a" }; return Object.keys(formatMap).reduce( (acc, key) => acc.replace(new RegExp(key, "g"), formatMap[key]), momentFormat ); } const DEFAULT_DATE_FORMAT = "YYYY-MM-DD", DEFAULT_TIME_FORMAT = "HH:mm", DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone; function format(input, dateFormat, options = { useUTC: !1, timeZone: void 0 }) { const { useUTC, timeZone } = options; return formatMomentLike(useUTC ? new UTCDateMini(input) : timeZone ? new TZDateMini(input, timeZone || DEFAULT_TIMEZONE) : new Date(input), dateFormat); } function parse(dateString, dateFormat, timeZone) { const dnsFormat = dateFormat ? momentToDateFnsFormat(dateFormat) : void 0, parsed = dnsFormat ? parse$1(dateString, dnsFormat, /* @__PURE__ */ new Date()) : parseISO(dateString); return parsed && !isNaN(parsed.getTime()) ? { isValid: !0, date: timeZone && isValidTimeZoneString(timeZone) ? new TZDateMini(parsed, timeZone) : parsed } : { isValid: !1, error: `Invalid date. Must be on the format "${dateFormat}"` }; } function isValidTimeZoneString(timeZone) { return Intl.supportedValuesOf("timeZone").includes(timeZone); } export { DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT, format, isValidTimeZoneString, parse, sanitizeLocale }; //# sourceMappingURL=legacyDateFormat.mjs.map