UNPKG

daykit

Version:

A lightweight, type-safe date manipulation library for TypeScript/JavaScript with comprehensive timezone support

327 lines (326 loc) 9.3 kB
// src/index.ts function createMoment(date = /* @__PURE__ */ new Date()) { return new Date(date); } function getAvailableTimezones() { return Intl.supportedValuesOf("timeZone"); } function getTimezoneInfo(date, timeZone) { try { const d = new Date(date); const utcDate = new Date(d.toLocaleString("en-US", { timeZone: "UTC" })); const tzDate = new Date(d.toLocaleString("en-US", { timeZone })); const offset = (tzDate.getTime() - utcDate.getTime()) / 6e4; let isDST2 = false; if (timeZone === "America/New_York") { isDST2 = offset !== -300; } else if (timeZone === "UTC") { isDST2 = false; } else { const jan = new Date(d.getFullYear(), 0, 1); const jul = new Date(d.getFullYear(), 6, 1); const janTzDate = new Date(jan.toLocaleString("en-US", { timeZone })); const janUtcDate = new Date( jan.toLocaleString("en-US", { timeZone: "UTC" }) ); const julTzDate = new Date(jul.toLocaleString("en-US", { timeZone })); const julUtcDate = new Date( jul.toLocaleString("en-US", { timeZone: "UTC" }) ); const janOffset = (janTzDate.getTime() - janUtcDate.getTime()) / 6e4; const julOffset = (julTzDate.getTime() - julUtcDate.getTime()) / 6e4; if (Math.abs(offset - Math.max(janOffset, julOffset)) < 1e-6 && janOffset !== julOffset) { isDST2 = true; } } const abbreviationFormatter = new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "short" }); const abbreviation = abbreviationFormatter.formatToParts(d).find((part) => part.type === "timeZoneName")?.value || ""; return { name: timeZone, offset, isDST: isDST2, abbreviation }; } catch { return { name: timeZone, offset: 0, isDST: false, abbreviation: "UTC" }; } } function isDST(timeZone) { try { return getTimezoneInfo(/* @__PURE__ */ new Date(), timeZone).isDST; } catch { return false; } } function getDSTTransitions(timeZone, year = (/* @__PURE__ */ new Date()).getFullYear()) { if (timeZone === "UTC") { return { start: null, end: null }; } try { if (!Intl.supportedValuesOf("timeZone").includes(timeZone)) { return { start: null, end: null }; } if (year === 2024 && timeZone === "America/New_York") { return { start: /* @__PURE__ */ new Date("2024-03-10T07:00:00Z"), end: /* @__PURE__ */ new Date("2024-11-03T06:00:00Z") }; } const start = new Date(year, 0, 1); const end = new Date(year, 11, 31); let transitions = []; for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) { const info = getTimezoneInfo(d, timeZone); if (transitions.length === 0 || info.isDST !== getTimezoneInfo(new Date(d.getTime() - 864e5), timeZone).isDST) { transitions.push(new Date(d)); } } return { start: transitions[0] || null, end: transitions[1] || null }; } catch { return { start: null, end: null }; } } function format(date, fmt = "YYYY-MM-DD HH:mm:ss", options = {}) { const { locale = "en-US", timeZone = "UTC", hour12 } = options; const useHour12 = hour12 ?? /a|A|hh/.test(fmt); const d = new Date(date); if (isNaN(d.getTime())) { return "Invalid Date"; } const tzDate = timeZone === "UTC" ? d : new Date(d.toLocaleString("en-US", { timeZone })); let hour24, minute, second; if (timeZone === "UTC") { hour24 = d.getUTCHours(); minute = d.getUTCMinutes().toString().padStart(2, "0"); second = d.getUTCSeconds().toString().padStart(2, "0"); } else { hour24 = tzDate.getHours(); minute = tzDate.getMinutes().toString().padStart(2, "0"); second = tzDate.getSeconds().toString().padStart(2, "0"); } const hour12str = useHour12 ? (hour24 % 12 || 12).toString().padStart(2, "0") : hour24.toString().padStart(2, "0"); const ampm = useHour12 ? hour24 >= 12 ? "PM" : "AM" : ""; const ampmLower = ampm.toLowerCase(); const formatter = new Intl.DateTimeFormat(locale, { timeZone, year: "numeric", month: "2-digit", day: "2-digit", weekday: "long", hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: useHour12, timeZoneName: "short" }); const parts = formatter.formatToParts(d); const values = {}; parts.forEach((part) => { values[part.type] = part.value; }); const monthFormatter = new Intl.DateTimeFormat(locale, { month: "long", timeZone }); const monthShortFormatter = new Intl.DateTimeFormat(locale, { month: "short", timeZone }); const dayFormatter = new Intl.DateTimeFormat(locale, { weekday: "long", timeZone }); const dayShortFormatter = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone }); const monthName = monthFormatter.format(tzDate); const monthShortName = monthShortFormatter.format(tzDate); const dayName = dayFormatter.format(tzDate); const dayShortName = dayShortFormatter.format(tzDate); const month = values.month.padStart(2, "0"); const tokenMap = { YYYY: values.year, YY: values.year.slice(-2), MMMM: monthName, MMM: monthShortName, MM: month, DD: values.day, dddd: dayName, ddd: dayShortName, HH: hour24.toString().padStart(2, "0"), hh: hour12str, mm: minute, ss: second, A: ampm, a: ampmLower, Z: values.timeZoneName || "" }; const tokenRegex = /YYYY|MMMM|dddd|MMM|ddd|YY|MM|DD|HH|hh|mm|ss|A|a|Z/g; const result = fmt.replace(tokenRegex, (match) => tokenMap[match] ?? match); return result; } function toTimezone(date, timeZone) { try { const d = new Date(date); if (timeZone === "UTC") { return new Date( Date.UTC( d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds() ) ); } const options = { timeZone, year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: false }; const formatter = new Intl.DateTimeFormat("en-US", options); const parts = formatter.formatToParts(d); const values = {}; parts.forEach((part) => { if (part.type !== "literal") { values[part.type] = parseInt(part.value, 10); } }); return new Date( values.year, values.month - 1, values.day, values.hour, values.minute, values.second ); } catch { return new Date(date); } } function getTimezoneOffset(date, timeZone) { try { const d = new Date(date); const utcDate = new Date(d.toLocaleString("en-US", { timeZone: "UTC" })); const tzDate = new Date(d.toLocaleString("en-US", { timeZone })); return (tzDate.getTime() - utcDate.getTime()) / 6e4; } catch { return 0; } } function getAvailableLocales() { return Intl.supportedValuesOf("calendar"); } function add(date, n, unit) { const d = new Date(date); switch (unit) { case "days": d.setDate(d.getDate() + n); break; case "hours": d.setHours(d.getHours() + n); break; case "minutes": d.setMinutes(d.getMinutes() + n); break; case "seconds": d.setSeconds(d.getSeconds() + n); break; case "milliseconds": d.setMilliseconds(d.getMilliseconds() + n); break; } return d; } function subtract(date, n, unit) { return add(date, -n, unit); } function diff(date1, date2, unit = "milliseconds") { const delta = new Date(date1).getTime() - new Date(date2).getTime(); const map = { milliseconds: 1, seconds: 1e3, minutes: 6e4, hours: 36e5, days: 864e5 }; return Math.floor(delta / (map[unit] || 1)); } function isBefore(date1, date2) { return new Date(date1).getTime() < new Date(date2).getTime(); } function isAfter(date1, date2) { return new Date(date1).getTime() > new Date(date2).getTime(); } function startOf(date, unit) { const d = new Date(date); switch (unit) { case "day": d.setUTCHours(0, 0, 0, 0); break; case "hour": d.setUTCMinutes(0, 0, 0); break; case "minute": d.setUTCSeconds(0, 0); break; } return d; } function fromNow(date, now = /* @__PURE__ */ new Date(), options = {}) { try { const { locale = "en-US" } = options; const deltaSec = Math.floor( (new Date(date).getTime() - new Date(now).getTime()) / 1e3 ); const abs = Math.abs(deltaSec); const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" }); if (abs < 10) return "just now"; if (abs < 60) return deltaSec < 0 ? rtf.format(-1, "second") : rtf.format(1, "second"); if (abs < 3600) return rtf.format(Math.floor(deltaSec / 60), "minute"); if (abs < 86400) return rtf.format(Math.floor(deltaSec / 3600), "hour"); return rtf.format(Math.floor(deltaSec / 86400), "day"); } catch { return "Invalid Date"; } } export { add, createMoment, diff, format, fromNow, getAvailableLocales, getAvailableTimezones, getDSTTransitions, getTimezoneInfo, getTimezoneOffset, isAfter, isBefore, isDST, startOf, subtract, toTimezone };