any-date-parser
Version:
Parse a wide range of date formats including human-input dates
1,604 lines (1,586 loc) • 40.8 kB
JavaScript
// src/normalizeLocale/normalizeLocale.ts
function normalizeLocale(name) {
name = name.replace(/_/g, "-");
name = name.replace(/[.:][\w-]*$/, "");
try {
return new Intl.Locale(name).baseName;
} catch (e) {
return "en-US";
}
}
// src/data/defaultLocale.ts
var detectedLocale;
if (typeof navigator !== "undefined") {
const nav = navigator;
detectedLocale = Array.isArray(nav.languages) ? nav.languages[0] : nav.language;
} else if (typeof process !== "undefined") {
const env = process.env;
detectedLocale = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE;
}
var defaultLocale = normalizeLocale(detectedLocale || "en-US");
var defaultLocale_default = defaultLocale;
// src/MaybeValidDate/MaybeValidDate.ts
var MaybeValidDate = class extends Date {
invalid = null;
isValid() {
return !Number.isNaN(this.valueOf());
}
};
// src/data/twoDigitYears.ts
var twoDigitYears = {};
for (let i = 0; i < 100; i++) {
const key = (i <= 9 ? "0" : "") + i;
twoDigitYears[key] = i + (i > 51 ? 1900 : 2e3);
}
var twoDigitYears_default = twoDigitYears;
// src/data/numberingSystems.ts
var startCodes = {
arab: 1632,
arabext: 1776,
bali: 6992,
beng: 2534,
deva: 2406,
fullwide: 65296,
gujr: 2790,
khmr: 6112,
knda: 3302,
laoo: 3792,
limb: 6470,
mlym: 3430,
mong: 6160,
mymr: 4160,
orya: 2918,
tamldec: 3046,
telu: 3174,
thai: 3664,
tibt: 3872
};
var chineseGroup = "[\uFF11\uFF12\uFF13\uFF14\uFF15\uFF16\uFF17\uFF18\uFF19\uFF10\u4E00\u4E8C\u4E09\u56DB\u4E94\u516D\u4E03\u516B\u4E5D\u3007\\d]";
var defaultLookup = {
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
"\uFF10": 0,
"\uFF11": 1,
"\uFF12": 2,
"\uFF13": 3,
"\uFF14": 4,
"\uFF15": 5,
"\uFF16": 6,
"\uFF17": 7,
"\uFF18": 8,
"\uFF19": 9,
"\u3007": 0,
\u4E00: 1,
\u4E8C: 2,
\u4E09: 3,
\u56DB: 4,
\u4E94: 5,
\u516D: 6,
\u4E03: 7,
\u516B: 8,
\u4E5D: 9
};
// src/buildDigits/buildDigits.ts
var cache = {};
function buildDigits(nsName) {
if (cache[nsName]) {
return cache[nsName];
}
if (nsName === "fullwide" || nsName === "hanidec") {
return { group: chineseGroup, lookup: { ...defaultLookup } };
}
const startCode = startCodes[nsName];
if (!startCode) {
return { group: "\\d", lookup: { ...defaultLookup } };
}
const start = String.fromCharCode(startCode);
const end = String.fromCharCode(startCode + 9);
const lookup = {};
for (let i = 0; i < 10; i++) {
lookup[String.fromCharCode(startCode + i)] = i;
}
cache[nsName] = {
group: `[${start}-${end}]`,
lookup
};
return cache[nsName];
}
// src/data/timezoneNames.ts
var timezoneNames = {
"Eastern Daylight Time": -240,
"Eastern Standard Time": -300,
"Central Daylight Time": -300,
"Central Standard Time": -360,
"Mountain Daylight Time": -360,
"Mountain Standard Time": -420,
"Pacific Daylight Time": -420,
"Pacific Standard Time": -480,
"Coordinated Universal Time": 0,
ACDT: 630,
// Australian Central Daylight Savings Time
ACST: 570,
// Australian Central Standard Time
ACT: 480,
// ASEAN Common Time
ADT: -180,
// Atlantic Daylight Time
AEDT: 660,
// Australian Eastern Daylight Savings Time
AEST: 600,
// Australian Eastern Standard Time
AFT: 270,
// Afghanistan Time
AKDT: -480,
// Alaska Daylight Time
AKST: -540,
// Alaska Standard Time
AMST: -180,
// Amazon Summer Time (Brazil)
AMT: -240,
// Amazon Time (Brazil)
ART: -180,
// Argentina Time
AST: 180,
// Arabia Standard Time
AWDT: 540,
// Australian Western Daylight Time
AWST: 480,
// Australian Western Standard Time
AZOST: -60,
// Azores Standard Time
AZT: 240,
// Azerbaijan Time
BDT: 360,
// Bangladesh Daylight Time (Bangladesh Daylight saving time keeps UTC+06 offset)
BIOT: 360,
// British Indian Ocean Time
BIT: -720,
// Baker Island Time
BOT: -240,
// Bolivia Time
BRST: -120,
// Brasilia Summer Time
BRT: -180,
// Brasilia Time
BTT: 360,
// Bhutan Time
CAT: 120,
// Central Africa Time
CCT: 390,
// Cocos Islands Time
CDT: -300,
// Central Daylight Time (North America)
CEDT: 120,
// Central European Daylight Time
CEST: 120,
// Central European Summer Time (Cf. HAEC)
CET: 60,
// Central European Time
CHADT: 825,
// Chatham Daylight Time
CHAST: 765,
// Chatham Standard Time
CHOT: 480,
// Choibalsan
ChST: 600,
// Chamorro Standard Time
CHUT: 600,
// Chuuk Time
CIST: -480,
// Clipperton Island Standard Time
CIT: 480,
// Central Indonesia Time
CKT: -600,
// Cook Island Time
CLST: -180,
// Chile Summer Time
CLT: -240,
// Chile Standard Time
COST: -240,
// Colombia Summer Time
COT: -300,
// Colombia Time
CST: -360,
// Central Standard Time (North America)
CT: 480,
// China time
CVT: -60,
// Cape Verde Time
CXT: 420,
// Christmas Island Time
DAVT: 420,
// Davis Time
DDUT: 600,
// Dumont d'Urville Time
DFT: 60,
// AIX specific equivalent of Central European Time
EASST: -300,
// Easter Island Standard Summer Time
EAST: -360,
// Easter Island Standard Time
EAT: 180,
// East Africa Time
ECT: -300,
// Ecuador Time
EDT: -240,
// Eastern Daylight Time (North America)
EEDT: 180,
// Eastern European Daylight Time
EEST: 180,
// Eastern European Summer Time
EET: 120,
// Eastern European Time
EGST: 0,
// Eastern Greenland Summer Time
EGT: -60,
// Eastern Greenland Time
EIT: 540,
// Eastern Indonesian Time
EST: -300,
// Eastern Standard Time (North America)
FET: 180,
// Further-eastern European Time
FJT: 720,
// Fiji Time
FKST: -180,
// Falkland Islands Standard Time
FKT: -240,
// Falkland Islands Time
FNT: -120,
// Fernando de Noronha Time
GALT: -360,
// Galapagos Time
GAMT: -540,
// Gambier Islands
GET: 240,
// Georgia Standard Time
GFT: -180,
// French Guiana Time
GILT: 720,
// Gilbert Island Time
GIT: -540,
// Gambier Island Time
GMT: 0,
// Greenwich Mean Time
GST: -120,
// South Georgia and the South Sandwich Islands
GYT: -240,
// Guyana Time
HADT: -540,
// Hawaii-Aleutian Daylight Time
HAEC: 120,
// Heure Avancée d'Europe Centrale francised name for CEST
HAST: -600,
// Hawaii-Aleutian Standard Time
HKT: 480,
// Hong Kong Time
HMT: 300,
// Heard and McDonald Islands Time
HOVT: 420,
// Khovd Time
HST: -600,
// Hawaii Standard Time
IBST: 0,
// International Business Standard Time
ICT: 420,
// Indochina Time
IDT: 180,
// Israel Daylight Time
IOT: 180,
// Indian Ocean Time
IRDT: 270,
// Iran Daylight Time
IRKT: 480,
// Irkutsk Time
IRST: 210,
// Iran Standard Time
IST: 120,
// Israel Standard Time
JST: 540,
// Japan Standard Time
KGT: 360,
// Kyrgyzstan time
KOST: 660,
// Kosrae Time
KRAT: 420,
// Krasnoyarsk Time
KST: 540,
// Korea Standard Time
LHST: 630,
// Lord Howe Standard Time
LINT: 840,
// Line Islands Time
MAGT: 720,
// Magadan Time
MART: -510,
// Marquesas Islands Time
MAWT: 300,
// Mawson Station Time
MDT: -360,
// Mountain Daylight Time (North America)
MET: 60,
// Middle European Time Same zone as CET
MEST: 120,
// Middle European Summer Time Same zone as CEST
MHT: 720,
// Marshall Islands
MIST: 660,
// Macquarie Island Station Time
MIT: -510,
// Marquesas Islands Time
MMT: 390,
// Myanmar Time
MSK: 180,
// Moscow Time
MST: -420,
// Mountain Standard Time (North America)
MUT: 240,
// Mauritius Time
MVT: 300,
// Maldives Time
MYT: 480,
// Malaysia Time
NCT: 660,
// New Caledonia Time
NDT: -90,
// Newfoundland Daylight Time
NFT: 660,
// Norfolk Time
NPT: 345,
// Nepal Time
NST: -150,
// Newfoundland Standard Time
NT: -150,
// Newfoundland Time
NUT: -660,
// Niue Time
NZDT: 780,
// New Zealand Daylight Time
NZST: 720,
// New Zealand Standard Time
OMST: 360,
// Omsk Time
ORAT: 300,
// Oral Time
PDT: -420,
// Pacific Daylight Time (North America)
PET: -300,
// Peru Time
PETT: 720,
// Kamchatka Time
PGT: 600,
// Papua New Guinea Time
PHOT: 780,
// Phoenix Island Time
PKT: 300,
// Pakistan Standard Time
PMDT: -120,
// Saint Pierre and Miquelon Daylight time
PMST: -180,
// Saint Pierre and Miquelon Standard Time
PONT: 660,
// Pohnpei Standard Time
PST: -480,
// Pacific Standard Time (North America)
PYST: -180,
// Paraguay Summer Time (South America)
PYT: -240,
// Paraguay Time (South America)
RET: 240,
// Réunion Time
ROTT: -180,
// Rothera Research Station Time
SAKT: 660,
// Sakhalin Island time
SAMT: 240,
// Samara Time
SAST: 120,
// South African Standard Time
SBT: 660,
// Solomon Islands Time
SCT: 240,
// Seychelles Time
SGT: 480,
// Singapore Time
SLST: 330,
// Sri Lanka Standard Time
SRET: 660,
// Srednekolymsk Time
SRT: -180,
// Suriname Time
SST: 480,
// Singapore Standard Time
SYOT: 180,
// Showa Station Time
TAHT: -600,
// Tahiti Time
THA: 420,
// Thailand Standard Time
TFT: 300,
// Indian/Kerguelen
TJT: 300,
// Tajikistan Time
TKT: 780,
// Tokelau Time
TLT: 540,
// Timor Leste Time
TMT: 300,
// Turkmenistan Time
TOT: 780,
// Tonga Time
TVT: 720,
// Tuvalu Time
UCT: 0,
// Coordinated Universal Time
ULAT: 480,
// Ulaanbaatar Time
USZ1: 120,
// Kaliningrad Time
UTC: 0,
// Coordinated Universal Time
UYST: -120,
// Uruguay Summer Time
UYT: -180,
// Uruguay Standard Time
UZT: 300,
// Uzbekistan Time
VET: -240,
// Venezuelan Standard Time
VLAT: 600,
// Vladivostok Time
VOLT: 240,
// Volgograd Time
VOST: 360,
// Vostok Station Time
VUT: 660,
// Vanuatu Time
WAKT: 720,
// Wake Island Time
WAST: 120,
// West Africa Summer Time
WAT: 60,
// West Africa Time
WEDT: 60,
// Western European Daylight Time
WEST: 60,
// Western European Summer Time
WET: 0,
// Western European Time
WIT: 420,
// Western Indonesian Time
WST: 480,
// Western Standard Time
YAKT: 540,
// Yakutsk Time
YEKT: 300
// Yekaterinburg Time
};
var timezoneNames_default = timezoneNames;
// src/data/baseLookups.ts
var baseLookups = {
zone: timezoneNames_default,
year: twoDigitYears_default,
meridiem: { am: 0, pm: 12, "a.m.": 0, "p.m.": 12 },
month: {
january: 1,
jan: 1,
february: 2,
feb: 2,
march: 3,
mar: 3,
april: 4,
apr: 4,
may: 5,
june: 6,
jun: 6,
july: 7,
jul: 7,
august: 8,
aug: 8,
september: 9,
sep: 9,
october: 10,
oct: 10,
november: 11,
nov: 11,
december: 12,
dec: 12
},
dayname: {
sunday: 0,
sun: 0,
monday: 1,
mon: 1,
tuesday: 2,
tue: 2,
wednesday: 3,
wed: 3,
thursday: 4,
thu: 4,
friday: 5,
fri: 5,
saturday: 6,
sat: 6
},
digit: {}
};
var baseLookups_default = baseLookups;
// src/data/templates.ts
var latn = {
MONTHNAME: "january|february|march|april|may|june|july|august|september|october|november|december|jan\\.?|feb\\.?|mar\\.?|apr\\.?|may\\.?|jun\\.?|jul\\.?|aug\\.?|sep\\.?|oct\\.?|nov\\.?|dec\\.?",
DAYNAME: "sunday|monday|tuesday|wednesday|thursday|friday|saturday|sun\\.?|mon\\.?|tue\\.?|wed\\.?|thu\\.?|fri\\.?|sat\\.?",
ZONE: "\\(?(?:" + Object.keys(timezoneNames_default).join("|") + ")\\)?",
MERIDIEM: "am|pm|a.m.|p.m.",
ORDINAL: "st|nd|rd|th|\\.",
YEAR: "\\d{4}|\\d{2}",
YEAR2: "\\d{2}",
YEAR4: "\\d{4}",
YEAR6: "-\\d{6}|\\+?\\d{5,6}",
MONTH: "1[0-2]|0?[1-9]",
MONTH2: "1[0-2]|0[1-9]",
DAY: "3[01]|[12]\\d|0?[1-9]",
DAY2: "3[01]|[12]\\d|0[1-9]",
OFFSET: "(?:GMT)?[\xB1\u2212+-](?:[01]\\d[0-5]\\d|[01]\\d:[0-5]\\d|[01]?\\d)",
H24: "[01]\\d|2[0-3]",
H12: "0?[1-9]|1[012]",
MIN: "[0-5]\\d",
// Although JavaScript dates can't account for leap seconds,
// we can parse "60" and JavaScript will roll over to the next day
SEC: "[0-5]\\d|60",
MS: "\\d{9}|\\d{6}|\\d{1,3}",
GAP: "[\\s/.,-]{1,}"
};
var other = {
...latn,
YEAR: "*{4}|*{2}",
YEAR4: "*{4}",
YEAR6: "-*{6}|\\+?*{5,6}",
MONTH: "*{1,2}",
MONTH2: "*{2}",
DAY: "*{1,2}",
DAY2: "*{2}",
OFFSET: "(?:GMT)?[\xB1\u2212+-](?:*{4}|*{2}:*{2}|*{1,2})",
H24: "*{2}",
H12: "*{1,2}",
MIN: "*{2}",
SEC: "*{2}",
MS: "*{9}|*{6}|*{3}"
};
// src/LocaleHelper/LocaleHelper.ts
var cache2 = {};
var LocaleHelper = class _LocaleHelper {
/**
* The locale string
*/
locale;
/**
* Lookups for zone, year, meridiem, month, dayname, digit
*/
lookups;
/**
* Template variables including MONTHNAME, MONTH, ZONE, etc.
*/
vars;
/**
* The numbering system to use (latn=standard arabic digits)
*/
numberingSystem;
/**
* The base name of the locale (e.g. en-US)
*/
baseName;
/**
* Date options for the locale
* @property locale: string;
* @property calendar: string;
* @property numberingSystem: string;
* @property timeZone: string;
* @property hour12?: boolean;
* @property weekday?: string;
* @property era?: string;
* @property year?: string;
* @property month?: string;
* @property day?: string;
* @property hour?: string;
* @property minute?: string;
* @property second?: string;
* @property timeZoneName?: string;
*/
dateOptions;
/**
* Get a singleton instance with the given locale
* @param locale such as en, en-US, es, fr-FR, etc.
* @returns
*/
static factory(locale = defaultLocale_default) {
if (!cache2[locale.toLowerCase()]) {
cache2[locale.toLowerCase()] = new _LocaleHelper(locale);
}
return cache2[locale.toLowerCase()];
}
/**
* Create a new instance with the given locale
* @param locale such as en, en-US, es, fr-FR, etc.
*/
constructor(locale = defaultLocale_default) {
this.locale = locale;
this.lookups = { ...baseLookups_default };
this.vars = { ...latn };
const fmt = new Intl.NumberFormat(this.locale);
this.numberingSystem = fmt.resolvedOptions().numberingSystem;
this.dateOptions = new Intl.DateTimeFormat(this.locale).resolvedOptions();
this.baseName = new Intl.Locale(this.locale).baseName;
this.build();
}
/**
* Cast a string to an integer, minding numbering system
* @param digitString Such as "2020" or "二〇二〇"
* @returns
*/
toInt(digitString) {
if (typeof digitString === "number") {
return digitString;
}
if (typeof digitString !== "string") {
return void 0;
}
if (this.numberingSystem === "latn" && !this.baseName.startsWith("zh")) {
const num = parseInt(digitString, 10);
if (!isNaN(num)) {
return num;
}
}
let latnDigitString = "";
for (let i = 0; i < digitString.length; i++) {
latnDigitString += String(this.lookups.digit[digitString[i]]);
}
return parseInt(latnDigitString, 10);
}
millisecondToInt(msString) {
if (typeof msString === "number") {
return msString;
}
if (typeof msString !== "string") {
return void 0;
}
const digits = msString.slice(0, 3);
if (digits.length === 1) {
return this.toInt(digits) * 100;
} else if (digits.length === 2) {
return this.toInt(digits) * 10;
}
return this.toInt(digits);
}
monthNameToInt(monthName) {
if (typeof monthName !== "string") {
return void 0;
}
const lower = monthName.toLocaleLowerCase(this.locale).replace(/\.$/, "");
return this.lookups.month[lower] || 12;
}
h12ToInt(digitString, ampm) {
if (typeof digitString !== "string") {
return void 0;
}
const meridiemOffset = this.lookups.meridiem[ampm?.toLowerCase()] || 0;
let hourInt = this.toInt(digitString);
if (hourInt < 12 && meridiemOffset === 12) {
hourInt += 12;
}
if (hourInt === 12 && meridiemOffset === 0) {
hourInt = 0;
}
return hourInt;
}
zoneToOffset(zoneName) {
if (typeof zoneName !== "string") {
return void 0;
}
zoneName = zoneName.replace(/[^a-z\s]/gi, "");
return this.lookups.zone[zoneName];
}
/**
* Convert an offset string to Numeric minutes (e.g. "-0500", "+5", "+03:30")
* @param offsetString
*/
offsetToMinutes(offsetString) {
if (typeof offsetString !== "string") {
return void 0;
}
const captured = offsetString.match(/^(?:GMT)?([±−+-])(..?):?(..)?$/);
if (captured) {
const [, sign, hours, minutes] = captured;
return (sign === "-" || sign === "\u2212" ? -1 : 1) * (this.toInt(hours) * 60 + this.toInt(minutes || 0));
}
return 0;
}
/**
* Build lookups for digits, month names, day names, and meridiems based on the locale
*/
build() {
if (this.dateOptions.numberingSystem === "latn") {
this.lookups.digit = defaultLookup;
} else {
this.buildNumbers();
}
if (!/^en/i.test(this.locale)) {
this.buildMonthNames();
this.buildDaynames();
if (!/zh/i.test(this.locale)) {
this.buildMeridiems();
}
}
}
/**
* Build lookups for non-arabic digits
*/
buildNumbers() {
const { group, lookup } = buildDigits(this.numberingSystem);
this.lookups.digit = lookup;
for (const name in other) {
if (!other.hasOwnProperty(name)) {
continue;
}
this.vars[name] = other[name].replace(/\*/g, group);
}
}
/**
* Build lookup for month names
*/
buildMonthNames() {
const vars = {};
const lookup = {};
if (/^fi/i.test(this.locale)) {
const months = "tammi|helmi|maalis|huhti|touko|kes\xE4|hein\xE4|elo|syys|loka|marras|joulu";
months.split("|").forEach((month, idx) => {
["", "k", "kuu", "kuuta"].forEach((suffix, i) => {
const maybePeriod = i < 2 ? "\\.?" : "";
vars[month + suffix + maybePeriod] = true;
lookup[month + suffix] = idx + 1;
});
});
} else {
const dates = [];
const findMonth = (item) => item.type === "month";
for (let monthIdx = 0; monthIdx < 12; monthIdx++) {
dates.push(new Date(2017, monthIdx, 1));
}
const dateStyles = ["full", "long", "medium"];
for (const dateStyle of dateStyles) {
const format2 = Intl.DateTimeFormat(this.locale, { dateStyle });
for (let monthIdx = 0; monthIdx < 12; monthIdx++) {
const parts = format2.formatToParts(dates[monthIdx]);
let text = parts.find(findMonth).value.toLocaleLowerCase(this.locale);
if (/^\d+$/.test(text)) {
continue;
}
if (/^ko/i.test(this.locale)) {
text += "\uC6D4";
}
if (dateStyle === "medium") {
text = text.replace(/\.$/, "");
vars[`${text}\\.?`] = true;
} else {
vars[text] = true;
}
lookup[text] = monthIdx + 1;
}
}
const format = Intl.DateTimeFormat(this.locale, { month: "short" });
for (let monthIdx = 0; monthIdx < 12; monthIdx++) {
const parts = format.formatToParts(dates[monthIdx]);
let text = parts.find(findMonth).value.toLocaleLowerCase(this.locale);
text = text.replace(/\.$/, "");
vars[`${text}\\.?`] = true;
lookup[text] = monthIdx + 1;
}
}
this.vars.MONTHNAME = Object.keys(vars).join("|");
this.lookups.month = lookup;
}
/**
* Build lookup for day name
*/
buildDaynames() {
const dates = [];
const findDay = (item) => item.type === "weekday";
for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
dates.push(new Date(2017, 0, dayIndex + 1));
}
const weekdays = ["long", "short"];
const list = [];
const lookup = {};
for (const weekday of weekdays) {
const format = Intl.DateTimeFormat(this.locale, { weekday });
for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
const parts = format.formatToParts(dates[dayIndex]);
let text = parts.find(findDay).value.toLocaleLowerCase(this.locale);
if (weekday === "short") {
text = text.replace(/\.$/, "");
list.push(`${text}\\.?`);
} else {
list.push(text);
}
lookup[text] = dayIndex;
}
}
this.vars.DAYNAME = list.join("|");
this.lookups.dayname = lookup;
}
/**
* Build lookup for am/pm
*/
buildMeridiems() {
const dates = [new Date(2017, 0, 1), new Date(2017, 0, 1, 23, 0, 0)];
const findDayPeriod = (item) => item.type === "dayPeriod";
const list = [];
const lookup = {};
const format = Intl.DateTimeFormat(this.locale, { timeStyle: "long" });
for (let i = 0; i < 2; i++) {
const parts = format.formatToParts(dates[i]);
const dayPeriod = parts.find(findDayPeriod);
if (!dayPeriod) {
return;
}
const text = dayPeriod.value.toLocaleLowerCase(this.locale);
list.push(text);
lookup[text] = i * 12;
}
this.vars.MERIDIEM = list.join("|");
this.lookups.meridiem = lookup;
}
/**
* Compile template into a RegExp and return it
* @param template The template string such as (_YEAR_)-(_MONTH_)-(_DAY_)
*/
compile(template) {
const regexString = template.replace(/_([A-Z0-9]+)_/g, ($0, $1) => {
if (!this.vars[$1]) {
throw new Error(`Template string contains invalid variable _${$1}_`);
}
return this.vars[$1];
});
return new RegExp(regexString, "i");
}
};
// src/data/mdyLocales.ts
var mdyLocales = [
"ee-TG",
// Togo (Ewe)
"en-AS",
// American Samoa
"en-CA",
// Canada
"en-FM",
// Federated States of Micronesia
"en-GH",
// Ghana
"en-GU",
// Guam
"en-KE",
// Kenya
"en-KY",
// Cayman Islands
"en-MH",
// Marshall Islands
"en-MP",
// Northern Mariana Islands
"en-US",
// United States
"en-VI",
// US Virgin Islands
"en-WS",
// Western Samoa
"jp-JP",
// Japan
"sm-AS",
// American Samoa (Samoan)
"sm-SM"
// Samoa
];
// src/data/unitShortcuts.ts
var unitShortcuts = {
y: "year",
M: "month",
d: "day",
w: "week",
h: "hour",
m: "minute",
s: "second",
ms: "millisecond"
};
var unitShortcuts_default = unitShortcuts;
// src/patterns/patterns.ts
var nowGetter = {
now: () => /* @__PURE__ */ new Date()
};
function handlerWith(units) {
return function handler(matches) {
const result = {};
for (let i = 0, len = units.length; i < len; i++) {
const unit = units[i];
if (unit) {
result[unit] = matches[i];
}
}
return result;
};
}
function compile(helper) {
const patterns = [
{
name: "timestampWithOffset",
regex: helper.compile(
"^(_YEAR4_)-(_MONTH_)-(_DAY_)[T ](_H24_):(_MIN_):(_SEC_)(?:.(_MS_))? ?(_OFFSET_|Z)?$"
),
handler: handlerWith([
"",
"year",
"month",
"day",
"hour",
"minute",
"second",
"millisecond",
"offset"
])
},
{
name: "timestampWithZone",
regex: helper.compile(
"^(_YEAR4_)-(_MONTH_)-(_DAY_)[T ](_H24_):(_MIN_):(_SEC_)(?:.(_MS_))?\\s*(_ZONE_)$"
),
handler: handlerWith([
"",
"year",
"month",
"day",
"hour",
"minute",
"second",
"millisecond",
"zone"
])
},
{
name: "timestampWithOffsetAndZone",
regex: helper.compile(
"^(_YEAR4_)-(_MONTH_)-(_DAY_)[T ](_H24_):(_MIN_):(_SEC_)(?:.(_MS_))? (_OFFSET_|Z)\\s*(_ZONE_)$"
),
handler: handlerWith([
"",
"year",
"month",
"day",
"hour",
"minute",
"second",
"millisecond",
"offset"
])
},
{
name: "chinese",
regex: helper.compile(
`^(${chineseGroup}{2,4})\\s*\u5E74\\s*(${chineseGroup}{1,2})\\s*\u6708\\s*(${chineseGroup}{1,2})\\s*\u65E5$`
),
handler: handlerWith(["", "year", "month", "day"])
},
{
name: "korean",
regex: helper.compile("^(_YEAR_)\uB144\\s*(_MONTH_)\uC6D4\\s*(_DAY_)\uC77C$"),
handler: handlerWith(["", "year", "month", "day"])
},
{
name: "twitter",
regex: /^(?:sun|mon|tue|wed|thu|fri|sat) (jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{2}) (\d{2}):(\d{2}):(\d{2}) ([+-]\d{4}) (\d{4})$/i,
handler: handlerWith([
"",
"monthname",
"day",
"hour",
"minute",
"second",
"offset",
"year"
])
},
{
name: "today",
regex: /^(now|today|tomorrow|yesterday)$/i,
handler: function(match) {
const now = nowGetter.now();
const aDay = 24 * 60 * 60 * 1e3;
const keyword = match[0].toLowerCase();
const toAdd = {
now: 0,
today: 0,
tomorrow: aDay,
yesterday: -1 * aDay
}[keyword];
if (toAdd !== 0) {
now.setTime(now.getTime() + toAdd);
}
const result = {
year: now.getFullYear(),
month: now.getMonth() + 1,
day: now.getDate()
};
if (keyword === "now") {
return {
...result,
hour: now.getHours(),
minute: now.getMinutes(),
second: now.getSeconds(),
millisecond: now.getMilliseconds()
};
}
return result;
}
},
{
name: "ago",
regex: /^(\+|-|in|) ?([\d.]+) ?(years?|months?|weeks?|days?|hours?|minutes?|seconds?|milliseconds?|ms|s|m|h|w|d|M|y)( ago)?$/i,
handler: function([, sign, amount, unit, isAgo]) {
amount = parseFloat(amount);
if (unit.length <= 2) {
unit = unitShortcuts_default[unit];
} else {
unit = unit.replace(/s$/, "");
unit = unit.toLowerCase();
}
if (unit === "week") {
unit = "day";
amount *= 7;
}
if (sign === "-" || isAgo) {
amount *= -1;
}
const now = nowGetter.now();
if (unit === "millisecond") {
now.setUTCMilliseconds(now.getUTCMilliseconds() + amount);
} else if (unit === "second") {
now.setUTCSeconds(now.getUTCSeconds() + amount);
} else if (unit === "minute") {
now.setUTCMinutes(now.getUTCMinutes() + amount);
} else if (unit === "hour") {
now.setUTCHours(now.getUTCHours() + amount);
} else if (unit === "day") {
now.setUTCDate(now.getUTCDate() + amount);
} else if (unit === "month") {
now.setUTCMonth(now.getUTCMonth() + amount);
} else if (unit === "year") {
now.setUTCFullYear(now.getUTCFullYear() + amount);
}
return {
year: now.getUTCFullYear(),
month: now.getUTCMonth() + 1,
day: now.getUTCDate(),
hour: now.getUTCHours(),
minute: now.getUTCMinutes(),
second: now.getUTCSeconds(),
millisecond: now.getUTCMilliseconds()
};
}
},
{
name: "atSeconds",
regex: /^@(\d+)$/,
handler: function(matches) {
const seconds = parseInt(matches[1], 10);
const date = new Date(seconds * 1e3);
return {
year: date.getUTCFullYear(),
month: date.getUTCMonth() + 1,
day: date.getUTCDate(),
hour: date.getUTCHours(),
minute: date.getUTCMinutes(),
second: date.getUTCSeconds()
};
}
},
{
name: "microsoftJson",
regex: /^\/Date\((\d+)([+-]\d{4})?\)\/$/,
handler: function(matches) {
const milliseconds = parseInt(matches[1], 10);
const date = new Date(milliseconds);
return {
year: date.getUTCFullYear(),
month: date.getUTCMonth() + 1,
day: date.getUTCDate(),
hour: date.getUTCHours(),
minute: date.getUTCMinutes(),
second: date.getUTCSeconds(),
millisecond: date.getUTCMilliseconds(),
offset: matches[2]
};
}
},
//
// partial-matching formats
//
{
name: "full24",
regex: helper.compile(
"(?:^|[\\sT])(_H24_):(_MIN_):(_SEC_)(?:\\.(_MS_))?(Z)?$"
),
handler: handlerWith([
"",
"hour",
"minute",
"second",
"millisecond",
"zone"
])
},
{
name: "zone24",
regex: helper.compile(
"(_H24_):(_MIN_):(_SEC_)(?:\\.(_MS_))?[\\s\\[(]*(_ZONE_)?[\\s\\])]*$"
),
handler: handlerWith([
"",
"hour",
"minute",
"second",
"millisecond",
"zone"
])
},
{
name: "hms12WithOffset",
regex: helper.compile(
"(_H12_):(_MIN_):(_SEC_)\\s*(_MERIDIEM_)\\s*(_OFFSET_)"
),
handler: handlerWith([
"",
"hour",
"minute",
"second",
"meridiem",
"offset"
])
},
{
name: "hms12",
regex: helper.compile("(_H12_):(_MIN_):(_SEC_)\\s*(_MERIDIEM_)"),
handler: handlerWith(["", "hour", "minute", "second", "meridiem"])
},
{
name: "hms24",
regex: helper.compile("(_H24_):(_MIN_):(_SEC_)(?:\\.(_MS_))?"),
handler: handlerWith(["", "hour", "minute", "second", "millisecond"])
},
{
name: "hm12",
regex: helper.compile("(_H12_):(_MIN_)\\s*(_MERIDIEM_)"),
handler: handlerWith(["", "hour", "minute", "meridiem"])
},
{
name: "hm24",
regex: helper.compile("(_H24_):(_MIN_)"),
handler: handlerWith(["", "hour", "minute"])
},
{
name: "h12",
regex: helper.compile("(_H12_)\\s*(_MERIDIEM_)"),
handler: handlerWith(["", "hour", "meridiem"])
},
{
name: "8digit",
regex: /^((?:19|20)\d{2})(\d{2})(\d{2})$/,
handler: handlerWith(["", "year", "month", "day"])
},
{
name: "yearMonthnameDay",
regex: helper.compile(
"(_YEAR4_)[\\s.-]+(_MONTHNAME_)[\\s,.-]+(_DAY_)(_ORDINAL_)?"
),
handler: handlerWith(["", "year", "monthname", "day"])
},
{
name: "dayMonthnameYear",
regex: helper.compile(
"(_DAY_)(_ORDINAL_)?[\\s.-]*(_MONTHNAME_)[\\s,.-]+(_YEAR_)"
),
handler: handlerWith(["", "day", "", "monthname", "year"])
},
{
name: "monthnameDayYear",
regex: helper.compile(
"(_MONTHNAME_)[\\s.-]*(_DAY_)(_ORDINAL_)?[\\s,.-]+(_YEAR_)"
),
handler: handlerWith(["", "monthname", "day", "", "year"])
},
{
name: "dayMonthname",
regex: helper.compile("\\b(_DAY_)(_ORDINAL_)?[\\s.-]*(_MONTHNAME_)\\b"),
handler: handlerWith(["", "day", "", "monthname"])
},
{
name: "monthnameDay",
regex: helper.compile("\\b(_MONTHNAME_)[\\s.-]*(_DAY_)(_ORDINAL_)?\\b"),
handler: handlerWith(["", "monthname", "day"])
},
{
name: "hmsNoMeridiem",
regex: helper.compile("\\b(_H12_|_H24_):(_MIN_):(_SEC_)\\b"),
handler: handlerWith(["", "hour", "minute", "second"])
},
{
name: "hmNoMeridiem",
regex: helper.compile("\\b(_H12_|_H24_):(_MIN_)\\b"),
handler: handlerWith(["", "hour", "minute"])
},
{
name: "ymd",
regex: helper.compile("(_YEAR4_)([./-])(_MONTH_)\\2+(_DAY_)"),
handler: handlerWith(["", "year", "", "month", "day"])
},
{
name: "mdy",
regex: helper.compile("(_MONTH_)([/-])(_DAY_)\\2(_YEAR_)"),
handler: handlerWith(["", "month", "", "day", "year"])
},
{
name: "dmy",
regex: helper.compile(
"(_DAY_)(?:_ORDINAL_)?[./\\s-]+(_MONTH_)[./\\s-]+(_YEAR_)"
),
handler: handlerWith(["", "day", "month", "year"])
},
{
name: "yearLoose",
regex: helper.compile("_YEAR4_"),
handler: handlerWith(["year"])
},
{
name: "dayMonthnameLoose",
regex: helper.compile("(_DAY_)[\\s.]*(_MONTHNAME_)"),
handler: handlerWith(["", "day", "monthname"])
},
{
name: "monthnameDayLoose",
regex: helper.compile("(_MONTHNAME_)[\\s.]*(_DAY_)"),
handler: handlerWith(["", "monthname", "day"])
},
{
name: "monthname",
regex: helper.compile("_MONTHNAME_"),
handler: handlerWith(["monthname"])
},
{
name: "year4",
regex: helper.compile("_YEAR4_"),
handler: handlerWith(["year"])
},
{
name: "md",
regex: helper.compile("(_MONTH_)[/-](_DAY_)"),
handler: handlerWith(["", "month", "day"])
},
{
name: "dm",
regex: helper.compile("(_DAY_)(?:_ORDINAL_)?[./\\s-]+(_MONTH_)"),
handler: handlerWith(["", "day", "month"])
},
{
name: "day",
regex: helper.compile("_DAY_"),
handler: handlerWith(["day"])
},
{
name: "year2",
regex: helper.compile("_YEAR2_"),
handler: handlerWith(["year"])
},
{
name: "onlyZone",
regex: helper.compile("_ZONE_"),
handler: handlerWith(["zone"])
},
{
name: "onlyOffset",
regex: helper.compile("_OFFSET_"),
handler: handlerWith(["offset"])
}
];
const twoLetterLocale = helper.baseName.slice(0, 5);
if (mdyLocales.includes(twoLetterLocale)) {
return patterns;
}
return patterns.filter((p) => !["mdy", "md"].includes(p.name));
}
// src/PatternMatcher/PatternMatcher.ts
var PatternMatcher = class {
doneChecker;
fallback;
patterns;
formatter;
constructor({
doneChecker: doneChecker2,
fallback,
patterns,
formatter
}) {
this.doneChecker = doneChecker2;
this.fallback = fallback;
this.patterns = patterns;
this.formatter = formatter;
}
attempt(input) {
if (typeof input !== "string") {
return this.fallback(input);
}
let workingString = input.trim();
const rawResult = {};
let hadMatch = false;
for (const pattern of this.patterns) {
const matches = workingString.match(pattern.regex);
if (!matches) {
continue;
}
hadMatch = true;
const result = pattern.handler(matches);
if (result) {
for (const [key, value] of Object.entries(result)) {
if (!(key in rawResult) && value !== void 0) {
rawResult[key] = value;
}
}
workingString = workingString.slice(0, matches.index) + workingString.slice(matches.index + matches[0].length + 1);
workingString = workingString.trim();
if (this.doneChecker(rawResult, workingString)) {
break;
}
}
}
return hadMatch ? this.formatter(rawResult) : this.fallback(input);
}
};
// src/PatternMatcher/getMatcher.ts
var finalFields = [
"year",
"month",
"day",
"hour",
"minute",
"second",
"millisecond",
"offset"
];
var matcherByLocale = {};
function getMatcher(locale) {
if (!matcherByLocale[locale]) {
const helper = LocaleHelper.factory(locale);
matcherByLocale[locale] = new PatternMatcher({
doneChecker,
fallback: getFallback(locale),
patterns: compile(helper),
formatter: getFormatter(helper)
});
}
return matcherByLocale[locale];
}
function doneChecker(res, input) {
return input === "" || /^\s+$/.test(input) || "year" in res && ("month" in res || "monthname" in res) && "day" in res && "hour" in res && "minute" in res && "second" in res && "millisecond" in res && ("zone" in res || "offset" in res);
}
function getFallback(locale) {
return function handleInvalid(input) {
let string = String(input).slice(0, 50);
if (string === "") {
string = "(empty string)";
}
return { invalid: `Unable to parse "${string}" with locale "${locale}"` };
};
}
function getFormatter(helper) {
return function format(extracted) {
const result = {};
for (const [name, value] of Object.entries(extracted)) {
if (name === "monthname") {
if (value) {
const month = helper.monthNameToInt(value);
if (month !== void 0) {
result.month = month;
}
}
} else if (name === "hour" && extracted.meridiem) {
const hour = helper.h12ToInt(value, extracted.meridiem);
if (hour !== void 0) {
result.hour = hour;
}
} else if (name === "zone") {
if (value) {
const offset = helper.zoneToOffset(value);
if (offset !== void 0) {
result.offset = offset;
}
}
} else if (name === "offset") {
const offset = helper.offsetToMinutes(value);
if (offset !== void 0) {
result.offset = offset;
}
} else if (name === "millisecond") {
const casted = helper.millisecondToInt(value);
if (typeof casted === "number") {
result.millisecond = casted;
}
} else if (finalFields.includes(name)) {
const casted = helper.toInt(value);
if (typeof casted === "number") {
result[name] = casted;
}
}
}
if (result.year < 100) {
result.year = twoDigitYears_default[extracted.year];
}
if (result.year && helper.dateOptions.calendar === "buddhist") {
result.year -= 543;
}
return result;
};
}
// src/data/preprocessors.ts
var periodsInsteadOfColons = [
[/([^\d.]+)(\d{1,2})\.(\d{2})\.(\d{2})(\D|$)/, "$1$2:$3:$4$5"],
[/([^\d.]+)(\d{1,2})\.(\d{2})(\D|$)/, "$1$2:$3$4"]
];
var preprocessors = {
ar: [[/ /g, " "]],
// Some built-in formats contain non-breaking space
bn: [[/,/g, ""]],
zh: [
// in Chinese, am/pm comes before the digits
[/早上\s*([\d:]+)/, "$1am"],
[/凌晨\s*([\d:]+)/, "$1am"],
[/上午\s*([\d:]+)/, "$1am"],
[/下午\s*([\d:]+)/, "$1pm"],
[/晚上\s*([\d:]+)/, "$1pm"]
// Chinese "time"
// [/\[.+?時間]/, ''],
],
he: [[/ב/gi, ""]],
// "of" in various languages
de: [[/ um /g, " "]],
pt: [[/\sde /gi, " "]],
es: [[/\sde /gi, " "]],
da: [[/\sden /gi, " "], ...periodsInsteadOfColons],
// Russian symbol after years
ru: [[/ г\./g, ""]],
th: [
// Thai "at/on"
// [/ที่/gi, ''],
[/\s*นาฬิกา\s*/i, ":"],
// "hour"
[/\s*นาที\s*/i, ":"],
// "minute"
[/\s*วินาที\s*/i, " "]
// "second"
],
ko: [
[/\s*시\s*/, ":"],
// "hour"
[/\s*분\s*/, ":"],
// "minute"
[/\s*초\s*/, ""],
// "second"
[/(오전|오후)\s*([\d:]+)/, "$2$1"],
// move AM/PM to the end
[/(\d{4})\. (\d{1,2})\. (\d{1,2})\./, "$1-$2-$3"]
],
fi: periodsInsteadOfColons,
id: periodsInsteadOfColons,
fr: [
[/(\d{2}) h /, "$1:"],
[/(\d{2}) min /, "$1:"],
[/(\d{2}) s /, "$1"]
]
};
var preprocessors_default = preprocessors;
// src/runPreprocessors/runPreprocessors.ts
function runPreprocessors(dateString, locale) {
const twoLetterLocale = locale.slice(0, 2).toLowerCase();
const replacers = preprocessors_default[twoLetterLocale];
if (!replacers) {
return dateString;
}
for (const [find, replace] of replacers) {
dateString = dateString.replace(find, replace);
}
return dateString;
}
// src/main.ts
function attempt(dateStr, locale = defaultLocale_default) {
const matcher = getMatcher(locale);
const processed = runPreprocessors(dateStr, locale);
return matcher.attempt(processed);
}
function fromObject(parsed) {
if (parsed.month && parsed.day && parsed.year === void 0) {
parsed.year = (/* @__PURE__ */ new Date()).getFullYear();
}
if (parsed.second === 60) {
parsed.second = 59;
}
const date = new MaybeValidDate(
parsed.year,
parsed.month - 1,
parsed.day,
parsed.hour || 0,
parsed.minute || 0,
parsed.second || 0,
parsed.millisecond || 0
);
if (typeof parsed.offset === "number") {
return new MaybeValidDate(date.valueOf() - parsed.offset * 60 * 1e3);
}
return date;
}
function fromString(dateStr, locale = defaultLocale_default) {
const result = attempt(dateStr, locale);
const date = result.invalid ? new MaybeValidDate(NaN) : fromObject(result);
if (!date.isValid()) {
date.invalid = `Unable to parse date "${dateStr}"`;
}
return date;
}
function fromAny(any, locale = defaultLocale_default) {
if (any instanceof Date) {
return new MaybeValidDate(any.valueOf());
}
if (typeof any === "number") {
return new MaybeValidDate(any);
}
return fromString(any, locale);
}
Date.fromString = MaybeValidDate.fromString = fromString;
Date.fromAny = MaybeValidDate.fromAny = fromAny;
var parser = {
fromString,
fromAny,
fromObject,
attempt
};
if (typeof window !== "undefined") {
window.anyDateParser = parser;
}
var main_default = parser;
export {
attempt,
main_default as default,
fromAny,
fromObject,
fromString
};