fix output
This commit is contained in:
+25
@@ -0,0 +1,25 @@
|
||||
import { ValueSetter } from "./Setter.js";
|
||||
|
||||
export class Parser {
|
||||
run(dateString, token, match, options) {
|
||||
const result = this.parse(dateString, token, match, options);
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
setter: new ValueSetter(
|
||||
result.value,
|
||||
this.validate,
|
||||
this.set,
|
||||
this.priority,
|
||||
this.subPriority,
|
||||
),
|
||||
rest: result.rest,
|
||||
};
|
||||
}
|
||||
|
||||
validate(_utcDate, _value, _options) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { constructFrom } from "../../constructFrom.js";
|
||||
import { transpose } from "../../transpose.js";
|
||||
|
||||
const TIMEZONE_UNIT_PRIORITY = 10;
|
||||
|
||||
export class Setter {
|
||||
subPriority = 0;
|
||||
|
||||
validate(_utcDate, _options) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ValueSetter extends Setter {
|
||||
constructor(
|
||||
value,
|
||||
|
||||
validateValue,
|
||||
|
||||
setValue,
|
||||
|
||||
priority,
|
||||
subPriority,
|
||||
) {
|
||||
super();
|
||||
this.value = value;
|
||||
this.validateValue = validateValue;
|
||||
this.setValue = setValue;
|
||||
this.priority = priority;
|
||||
if (subPriority) {
|
||||
this.subPriority = subPriority;
|
||||
}
|
||||
}
|
||||
|
||||
validate(date, options) {
|
||||
return this.validateValue(date, this.value, options);
|
||||
}
|
||||
|
||||
set(date, flags, options) {
|
||||
return this.setValue(date, flags, this.value, options);
|
||||
}
|
||||
}
|
||||
|
||||
export class DateTimezoneSetter extends Setter {
|
||||
priority = TIMEZONE_UNIT_PRIORITY;
|
||||
subPriority = -1;
|
||||
|
||||
constructor(context, reference) {
|
||||
super();
|
||||
this.context = context || ((date) => constructFrom(reference, date));
|
||||
}
|
||||
|
||||
set(date, flags) {
|
||||
if (flags.timestampIsSet) return date;
|
||||
return constructFrom(date, transpose(date, this.context));
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
export const numericPatterns = {
|
||||
month: /^(1[0-2]|0?\d)/, // 0 to 12
|
||||
date: /^(3[0-1]|[0-2]?\d)/, // 0 to 31
|
||||
dayOfYear: /^(36[0-6]|3[0-5]\d|[0-2]?\d?\d)/, // 0 to 366
|
||||
week: /^(5[0-3]|[0-4]?\d)/, // 0 to 53
|
||||
hour23h: /^(2[0-3]|[0-1]?\d)/, // 0 to 23
|
||||
hour24h: /^(2[0-4]|[0-1]?\d)/, // 0 to 24
|
||||
hour11h: /^(1[0-1]|0?\d)/, // 0 to 11
|
||||
hour12h: /^(1[0-2]|0?\d)/, // 0 to 12
|
||||
minute: /^[0-5]?\d/, // 0 to 59
|
||||
second: /^[0-5]?\d/, // 0 to 59
|
||||
|
||||
singleDigit: /^\d/, // 0 to 9
|
||||
twoDigits: /^\d{1,2}/, // 0 to 99
|
||||
threeDigits: /^\d{1,3}/, // 0 to 999
|
||||
fourDigits: /^\d{1,4}/, // 0 to 9999
|
||||
|
||||
anyDigitsSigned: /^-?\d+/,
|
||||
singleDigitSigned: /^-?\d/, // 0 to 9, -0 to -9
|
||||
twoDigitsSigned: /^-?\d{1,2}/, // 0 to 99, -0 to -99
|
||||
threeDigitsSigned: /^-?\d{1,3}/, // 0 to 999, -0 to -999
|
||||
fourDigitsSigned: /^-?\d{1,4}/, // 0 to 9999, -0 to -9999
|
||||
};
|
||||
|
||||
export const timezonePatterns = {
|
||||
basicOptionalMinutes: /^([+-])(\d{2})(\d{2})?|Z/,
|
||||
basic: /^([+-])(\d{2})(\d{2})|Z/,
|
||||
basicOptionalSeconds: /^([+-])(\d{2})(\d{2})((\d{2}))?|Z/,
|
||||
extended: /^([+-])(\d{2}):(\d{2})|Z/,
|
||||
extendedOptionalSeconds: /^([+-])(\d{2}):(\d{2})(:(\d{2}))?|Z/,
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
import { EraParser } from "./parsers/EraParser.js";
|
||||
import { YearParser } from "./parsers/YearParser.js";
|
||||
import { LocalWeekYearParser } from "./parsers/LocalWeekYearParser.js";
|
||||
import { ISOWeekYearParser } from "./parsers/ISOWeekYearParser.js";
|
||||
import { ExtendedYearParser } from "./parsers/ExtendedYearParser.js";
|
||||
import { QuarterParser } from "./parsers/QuarterParser.js";
|
||||
import { StandAloneQuarterParser } from "./parsers/StandAloneQuarterParser.js";
|
||||
import { MonthParser } from "./parsers/MonthParser.js";
|
||||
import { StandAloneMonthParser } from "./parsers/StandAloneMonthParser.js";
|
||||
import { LocalWeekParser } from "./parsers/LocalWeekParser.js";
|
||||
import { ISOWeekParser } from "./parsers/ISOWeekParser.js";
|
||||
import { DateParser } from "./parsers/DateParser.js";
|
||||
import { DayOfYearParser } from "./parsers/DayOfYearParser.js";
|
||||
import { DayParser } from "./parsers/DayParser.js";
|
||||
import { LocalDayParser } from "./parsers/LocalDayParser.js";
|
||||
import { StandAloneLocalDayParser } from "./parsers/StandAloneLocalDayParser.js";
|
||||
import { ISODayParser } from "./parsers/ISODayParser.js";
|
||||
import { AMPMParser } from "./parsers/AMPMParser.js";
|
||||
import { AMPMMidnightParser } from "./parsers/AMPMMidnightParser.js";
|
||||
import { DayPeriodParser } from "./parsers/DayPeriodParser.js";
|
||||
import { Hour1to12Parser } from "./parsers/Hour1to12Parser.js";
|
||||
import { Hour0to23Parser } from "./parsers/Hour0to23Parser.js";
|
||||
import { Hour0To11Parser } from "./parsers/Hour0To11Parser.js";
|
||||
import { Hour1To24Parser } from "./parsers/Hour1To24Parser.js";
|
||||
import { MinuteParser } from "./parsers/MinuteParser.js";
|
||||
import { SecondParser } from "./parsers/SecondParser.js";
|
||||
import { FractionOfSecondParser } from "./parsers/FractionOfSecondParser.js";
|
||||
import { ISOTimezoneWithZParser } from "./parsers/ISOTimezoneWithZParser.js";
|
||||
import { ISOTimezoneParser } from "./parsers/ISOTimezoneParser.js";
|
||||
import { TimestampSecondsParser } from "./parsers/TimestampSecondsParser.js";
|
||||
import { TimestampMillisecondsParser } from "./parsers/TimestampMillisecondsParser.js";
|
||||
|
||||
/*
|
||||
* | | Unit | | Unit |
|
||||
* |-----|--------------------------------|-----|--------------------------------|
|
||||
* | a | AM, PM | A* | Milliseconds in day |
|
||||
* | b | AM, PM, noon, midnight | B | Flexible day period |
|
||||
* | c | Stand-alone local day of week | C* | Localized hour w/ day period |
|
||||
* | d | Day of month | D | Day of year |
|
||||
* | e | Local day of week | E | Day of week |
|
||||
* | f | | F* | Day of week in month |
|
||||
* | g* | Modified Julian day | G | Era |
|
||||
* | h | Hour [1-12] | H | Hour [0-23] |
|
||||
* | i! | ISO day of week | I! | ISO week of year |
|
||||
* | j* | Localized hour w/ day period | J* | Localized hour w/o day period |
|
||||
* | k | Hour [1-24] | K | Hour [0-11] |
|
||||
* | l* | (deprecated) | L | Stand-alone month |
|
||||
* | m | Minute | M | Month |
|
||||
* | n | | N | |
|
||||
* | o! | Ordinal number modifier | O* | Timezone (GMT) |
|
||||
* | p | | P | |
|
||||
* | q | Stand-alone quarter | Q | Quarter |
|
||||
* | r* | Related Gregorian year | R! | ISO week-numbering year |
|
||||
* | s | Second | S | Fraction of second |
|
||||
* | t! | Seconds timestamp | T! | Milliseconds timestamp |
|
||||
* | u | Extended year | U* | Cyclic year |
|
||||
* | v* | Timezone (generic non-locat.) | V* | Timezone (location) |
|
||||
* | w | Local week of year | W* | Week of month |
|
||||
* | x | Timezone (ISO-8601 w/o Z) | X | Timezone (ISO-8601) |
|
||||
* | y | Year (abs) | Y | Local week-numbering year |
|
||||
* | z* | Timezone (specific non-locat.) | Z* | Timezone (aliases) |
|
||||
*
|
||||
* Letters marked by * are not implemented but reserved by Unicode standard.
|
||||
*
|
||||
* Letters marked by ! are non-standard, but implemented by date-fns:
|
||||
* - `o` modifies the previous token to turn it into an ordinal (see `parse` docs)
|
||||
* - `i` is ISO day of week. For `i` and `ii` is returns numeric ISO week days,
|
||||
* i.e. 7 for Sunday, 1 for Monday, etc.
|
||||
* - `I` is ISO week of year, as opposed to `w` which is local week of year.
|
||||
* - `R` is ISO week-numbering year, as opposed to `Y` which is local week-numbering year.
|
||||
* `R` is supposed to be used in conjunction with `I` and `i`
|
||||
* for universal ISO week-numbering date, whereas
|
||||
* `Y` is supposed to be used in conjunction with `w` and `e`
|
||||
* for week-numbering date specific to the locale.
|
||||
*/
|
||||
export const parsers = {
|
||||
G: new EraParser(),
|
||||
y: new YearParser(),
|
||||
Y: new LocalWeekYearParser(),
|
||||
R: new ISOWeekYearParser(),
|
||||
u: new ExtendedYearParser(),
|
||||
Q: new QuarterParser(),
|
||||
q: new StandAloneQuarterParser(),
|
||||
M: new MonthParser(),
|
||||
L: new StandAloneMonthParser(),
|
||||
w: new LocalWeekParser(),
|
||||
I: new ISOWeekParser(),
|
||||
d: new DateParser(),
|
||||
D: new DayOfYearParser(),
|
||||
E: new DayParser(),
|
||||
e: new LocalDayParser(),
|
||||
c: new StandAloneLocalDayParser(),
|
||||
i: new ISODayParser(),
|
||||
a: new AMPMParser(),
|
||||
b: new AMPMMidnightParser(),
|
||||
B: new DayPeriodParser(),
|
||||
h: new Hour1to12Parser(),
|
||||
H: new Hour0to23Parser(),
|
||||
K: new Hour0To11Parser(),
|
||||
k: new Hour1To24Parser(),
|
||||
m: new MinuteParser(),
|
||||
s: new SecondParser(),
|
||||
S: new FractionOfSecondParser(),
|
||||
X: new ISOTimezoneWithZParser(),
|
||||
x: new ISOTimezoneParser(),
|
||||
t: new TimestampSecondsParser(),
|
||||
T: new TimestampMillisecondsParser(),
|
||||
};
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { dayPeriodEnumToHours } from "../utils.js";
|
||||
|
||||
export class AMPMMidnightParser extends Parser {
|
||||
priority = 80;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "b":
|
||||
case "bb":
|
||||
case "bbb":
|
||||
return (
|
||||
match.dayPeriod(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
|
||||
case "bbbbb":
|
||||
return match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
case "bbbb":
|
||||
default:
|
||||
return (
|
||||
match.dayPeriod(dateString, {
|
||||
width: "wide",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setHours(dayPeriodEnumToHours(value), 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["a", "B", "H", "k", "t", "T"];
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { dayPeriodEnumToHours } from "../utils.js";
|
||||
|
||||
export class AMPMParser extends Parser {
|
||||
priority = 80;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "a":
|
||||
case "aa":
|
||||
case "aaa":
|
||||
return (
|
||||
match.dayPeriod(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
|
||||
case "aaaaa":
|
||||
return match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
case "aaaa":
|
||||
default:
|
||||
return (
|
||||
match.dayPeriod(dateString, {
|
||||
width: "wide",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setHours(dayPeriodEnumToHours(value), 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["b", "B", "H", "k", "t", "T"];
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import {
|
||||
isLeapYearIndex,
|
||||
parseNDigits,
|
||||
parseNumericPattern,
|
||||
} from "../utils.js";
|
||||
|
||||
const DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
const DAYS_IN_MONTH_LEAP_YEAR = [
|
||||
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
|
||||
];
|
||||
|
||||
// Day of the month
|
||||
export class DateParser extends Parser {
|
||||
priority = 90;
|
||||
subPriority = 1;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "d":
|
||||
return parseNumericPattern(numericPatterns.date, dateString);
|
||||
case "do":
|
||||
return match.ordinalNumber(dateString, { unit: "date" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(date, value) {
|
||||
const year = date.getFullYear();
|
||||
const isLeapYear = isLeapYearIndex(year);
|
||||
const month = date.getMonth();
|
||||
if (isLeapYear) {
|
||||
return value >= 1 && value <= DAYS_IN_MONTH_LEAP_YEAR[month];
|
||||
} else {
|
||||
return value >= 1 && value <= DAYS_IN_MONTH[month];
|
||||
}
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setDate(value);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"Y",
|
||||
"R",
|
||||
"q",
|
||||
"Q",
|
||||
"w",
|
||||
"I",
|
||||
"D",
|
||||
"i",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import {
|
||||
isLeapYearIndex,
|
||||
parseNDigits,
|
||||
parseNumericPattern,
|
||||
} from "../utils.js";
|
||||
|
||||
export class DayOfYearParser extends Parser {
|
||||
priority = 90;
|
||||
|
||||
subpriority = 1;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "D":
|
||||
case "DD":
|
||||
return parseNumericPattern(numericPatterns.dayOfYear, dateString);
|
||||
case "Do":
|
||||
return match.ordinalNumber(dateString, { unit: "date" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(date, value) {
|
||||
const year = date.getFullYear();
|
||||
const isLeapYear = isLeapYearIndex(year);
|
||||
if (isLeapYear) {
|
||||
return value >= 1 && value <= 366;
|
||||
} else {
|
||||
return value >= 1 && value <= 365;
|
||||
}
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMonth(0, value);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"Y",
|
||||
"R",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"w",
|
||||
"I",
|
||||
"d",
|
||||
"E",
|
||||
"i",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { setDay } from "../../../setDay.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
// Day of week
|
||||
export class DayParser extends Parser {
|
||||
priority = 90;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
// Tue
|
||||
case "E":
|
||||
case "EE":
|
||||
case "EEE":
|
||||
return (
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, { width: "short", context: "formatting" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
|
||||
// T
|
||||
case "EEEEE":
|
||||
return match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
// Tu
|
||||
case "EEEEEE":
|
||||
return (
|
||||
match.day(dateString, { width: "short", context: "formatting" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
|
||||
// Tuesday
|
||||
case "EEEE":
|
||||
default:
|
||||
return (
|
||||
match.day(dateString, { width: "wide", context: "formatting" }) ||
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, { width: "short", context: "formatting" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 6;
|
||||
}
|
||||
|
||||
set(date, _flags, value, options) {
|
||||
date = setDay(date, value, options);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["D", "i", "e", "c", "t", "T"];
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { dayPeriodEnumToHours } from "../utils.js";
|
||||
|
||||
// in the morning, in the afternoon, in the evening, at night
|
||||
export class DayPeriodParser extends Parser {
|
||||
priority = 80;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "B":
|
||||
case "BB":
|
||||
case "BBB":
|
||||
return (
|
||||
match.dayPeriod(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
|
||||
case "BBBBB":
|
||||
return match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
case "BBBB":
|
||||
default:
|
||||
return (
|
||||
match.dayPeriod(dateString, {
|
||||
width: "wide",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.dayPeriod(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setHours(dayPeriodEnumToHours(value), 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["a", "b", "t", "T"];
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
export class EraParser extends Parser {
|
||||
priority = 140;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
// AD, BC
|
||||
case "G":
|
||||
case "GG":
|
||||
case "GGG":
|
||||
return (
|
||||
match.era(dateString, { width: "abbreviated" }) ||
|
||||
match.era(dateString, { width: "narrow" })
|
||||
);
|
||||
|
||||
// A, B
|
||||
case "GGGGG":
|
||||
return match.era(dateString, { width: "narrow" });
|
||||
// Anno Domini, Before Christ
|
||||
case "GGGG":
|
||||
default:
|
||||
return (
|
||||
match.era(dateString, { width: "wide" }) ||
|
||||
match.era(dateString, { width: "abbreviated" }) ||
|
||||
match.era(dateString, { width: "narrow" })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set(date, flags, value) {
|
||||
flags.era = value;
|
||||
date.setFullYear(value, 0, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["R", "u", "t", "T"];
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigitsSigned } from "../utils.js";
|
||||
|
||||
export class ExtendedYearParser extends Parser {
|
||||
priority = 130;
|
||||
|
||||
parse(dateString, token) {
|
||||
if (token === "u") {
|
||||
return parseNDigitsSigned(4, dateString);
|
||||
}
|
||||
|
||||
return parseNDigitsSigned(token.length, dateString);
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setFullYear(value, 0, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["G", "y", "Y", "R", "w", "I", "i", "e", "c", "t", "T"];
|
||||
}
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, parseNDigits } from "../utils.js";
|
||||
|
||||
export class FractionOfSecondParser extends Parser {
|
||||
priority = 30;
|
||||
|
||||
parse(dateString, token) {
|
||||
const valueCallback = (value) =>
|
||||
Math.trunc(value * Math.pow(10, -token.length + 3));
|
||||
return mapValue(parseNDigits(token.length, dateString), valueCallback);
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMilliseconds(value);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["t", "T"];
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class Hour0To11Parser extends Parser {
|
||||
priority = 70;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "K":
|
||||
return parseNumericPattern(numericPatterns.hour11h, dateString);
|
||||
case "Ko":
|
||||
return match.ordinalNumber(dateString, { unit: "hour" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 11;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
const isPM = date.getHours() >= 12;
|
||||
if (isPM && value < 12) {
|
||||
date.setHours(value + 12, 0, 0, 0);
|
||||
} else {
|
||||
date.setHours(value, 0, 0, 0);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["h", "H", "k", "t", "T"];
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class Hour0to23Parser extends Parser {
|
||||
priority = 70;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "H":
|
||||
return parseNumericPattern(numericPatterns.hour23h, dateString);
|
||||
case "Ho":
|
||||
return match.ordinalNumber(dateString, { unit: "hour" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 23;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setHours(value, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["a", "b", "h", "K", "k", "t", "T"];
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class Hour1To24Parser extends Parser {
|
||||
priority = 70;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "k":
|
||||
return parseNumericPattern(numericPatterns.hour24h, dateString);
|
||||
case "ko":
|
||||
return match.ordinalNumber(dateString, { unit: "hour" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 24;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
const hours = value <= 24 ? value % 24 : value;
|
||||
date.setHours(hours, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["a", "b", "h", "H", "K", "t", "T"];
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class Hour1to12Parser extends Parser {
|
||||
priority = 70;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "h":
|
||||
return parseNumericPattern(numericPatterns.hour12h, dateString);
|
||||
case "ho":
|
||||
return match.ordinalNumber(dateString, { unit: "hour" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 12;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
const isPM = date.getHours() >= 12;
|
||||
if (isPM && value < 12) {
|
||||
date.setHours(value + 12, 0, 0, 0);
|
||||
} else if (!isPM && value === 12) {
|
||||
date.setHours(0, 0, 0, 0);
|
||||
} else {
|
||||
date.setHours(value, 0, 0, 0);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["H", "K", "k", "t", "T"];
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import { setISODay } from "../../../setISODay.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, parseNDigits } from "../utils.js";
|
||||
|
||||
// ISO day of week
|
||||
export class ISODayParser extends Parser {
|
||||
priority = 90;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
const valueCallback = (value) => {
|
||||
if (value === 0) {
|
||||
return 7;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
switch (token) {
|
||||
// 2
|
||||
case "i":
|
||||
case "ii": // 02
|
||||
return parseNDigits(token.length, dateString);
|
||||
// 2nd
|
||||
case "io":
|
||||
return match.ordinalNumber(dateString, { unit: "day" });
|
||||
// Tue
|
||||
case "iii":
|
||||
return mapValue(
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, {
|
||||
width: "short",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// T
|
||||
case "iiiii":
|
||||
return mapValue(
|
||||
match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// Tu
|
||||
case "iiiiii":
|
||||
return mapValue(
|
||||
match.day(dateString, {
|
||||
width: "short",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// Tuesday
|
||||
case "iiii":
|
||||
default:
|
||||
return mapValue(
|
||||
match.day(dateString, {
|
||||
width: "wide",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, {
|
||||
width: "short",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 7;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date = setISODay(date, value);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"y",
|
||||
"Y",
|
||||
"u",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"w",
|
||||
"d",
|
||||
"D",
|
||||
"E",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { constructFrom } from "../../../constructFrom.js";
|
||||
import { getTimezoneOffsetInMilliseconds } from "../../../_lib/getTimezoneOffsetInMilliseconds.js";
|
||||
import { timezonePatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseTimezonePattern } from "../utils.js";
|
||||
|
||||
// Timezone (ISO-8601)
|
||||
export class ISOTimezoneParser extends Parser {
|
||||
priority = 10;
|
||||
|
||||
parse(dateString, token) {
|
||||
switch (token) {
|
||||
case "x":
|
||||
return parseTimezonePattern(
|
||||
timezonePatterns.basicOptionalMinutes,
|
||||
dateString,
|
||||
);
|
||||
case "xx":
|
||||
return parseTimezonePattern(timezonePatterns.basic, dateString);
|
||||
case "xxxx":
|
||||
return parseTimezonePattern(
|
||||
timezonePatterns.basicOptionalSeconds,
|
||||
dateString,
|
||||
);
|
||||
case "xxxxx":
|
||||
return parseTimezonePattern(
|
||||
timezonePatterns.extendedOptionalSeconds,
|
||||
dateString,
|
||||
);
|
||||
case "xxx":
|
||||
default:
|
||||
return parseTimezonePattern(timezonePatterns.extended, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
set(date, flags, value) {
|
||||
if (flags.timestampIsSet) return date;
|
||||
return constructFrom(
|
||||
date,
|
||||
date.getTime() - getTimezoneOffsetInMilliseconds(date) - value,
|
||||
);
|
||||
}
|
||||
|
||||
incompatibleTokens = ["t", "T", "X"];
|
||||
}
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
import { constructFrom } from "../../../constructFrom.js";
|
||||
import { getTimezoneOffsetInMilliseconds } from "../../../_lib/getTimezoneOffsetInMilliseconds.js";
|
||||
import { timezonePatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseTimezonePattern } from "../utils.js";
|
||||
|
||||
// Timezone (ISO-8601. +00:00 is `'Z'`)
|
||||
export class ISOTimezoneWithZParser extends Parser {
|
||||
priority = 10;
|
||||
|
||||
parse(dateString, token) {
|
||||
switch (token) {
|
||||
case "X":
|
||||
return parseTimezonePattern(
|
||||
timezonePatterns.basicOptionalMinutes,
|
||||
dateString,
|
||||
);
|
||||
case "XX":
|
||||
return parseTimezonePattern(timezonePatterns.basic, dateString);
|
||||
case "XXXX":
|
||||
return parseTimezonePattern(
|
||||
timezonePatterns.basicOptionalSeconds,
|
||||
dateString,
|
||||
);
|
||||
case "XXXXX":
|
||||
return parseTimezonePattern(
|
||||
timezonePatterns.extendedOptionalSeconds,
|
||||
dateString,
|
||||
);
|
||||
case "XXX":
|
||||
default:
|
||||
return parseTimezonePattern(timezonePatterns.extended, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
set(date, flags, value) {
|
||||
if (flags.timestampIsSet) return date;
|
||||
return constructFrom(
|
||||
date,
|
||||
date.getTime() - getTimezoneOffsetInMilliseconds(date) - value,
|
||||
);
|
||||
}
|
||||
|
||||
incompatibleTokens = ["t", "T", "x"];
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { setISOWeek } from "../../../setISOWeek.js";
|
||||
import { startOfISOWeek } from "../../../startOfISOWeek.js";
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
// ISO week of year
|
||||
export class ISOWeekParser extends Parser {
|
||||
priority = 100;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "I":
|
||||
return parseNumericPattern(numericPatterns.week, dateString);
|
||||
case "Io":
|
||||
return match.ordinalNumber(dateString, { unit: "week" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 53;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
return startOfISOWeek(setISOWeek(date, value));
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"y",
|
||||
"Y",
|
||||
"u",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"w",
|
||||
"d",
|
||||
"D",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { startOfISOWeek } from "../../../startOfISOWeek.js";
|
||||
import { constructFrom } from "../../../constructFrom.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigitsSigned } from "../utils.js";
|
||||
|
||||
// ISO week-numbering year
|
||||
export class ISOWeekYearParser extends Parser {
|
||||
priority = 130;
|
||||
|
||||
parse(dateString, token) {
|
||||
if (token === "R") {
|
||||
return parseNDigitsSigned(4, dateString);
|
||||
}
|
||||
|
||||
return parseNDigitsSigned(token.length, dateString);
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
const firstWeekOfYear = constructFrom(date, 0);
|
||||
firstWeekOfYear.setFullYear(value, 0, 4);
|
||||
firstWeekOfYear.setHours(0, 0, 0, 0);
|
||||
return startOfISOWeek(firstWeekOfYear);
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"G",
|
||||
"y",
|
||||
"Y",
|
||||
"u",
|
||||
"Q",
|
||||
"q",
|
||||
"M",
|
||||
"L",
|
||||
"w",
|
||||
"d",
|
||||
"D",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
import { setDay } from "../../../setDay.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, parseNDigits } from "../utils.js";
|
||||
|
||||
// Local day of week
|
||||
export class LocalDayParser extends Parser {
|
||||
priority = 90;
|
||||
parse(dateString, token, match, options) {
|
||||
const valueCallback = (value) => {
|
||||
// We want here floor instead of trunc, so we get -7 for value 0 instead of 0
|
||||
const wholeWeekDays = Math.floor((value - 1) / 7) * 7;
|
||||
return ((value + options.weekStartsOn + 6) % 7) + wholeWeekDays;
|
||||
};
|
||||
|
||||
switch (token) {
|
||||
// 3
|
||||
case "e":
|
||||
case "ee": // 03
|
||||
return mapValue(parseNDigits(token.length, dateString), valueCallback);
|
||||
// 3rd
|
||||
case "eo":
|
||||
return mapValue(
|
||||
match.ordinalNumber(dateString, {
|
||||
unit: "day",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// Tue
|
||||
case "eee":
|
||||
return (
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, { width: "short", context: "formatting" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
|
||||
// T
|
||||
case "eeeee":
|
||||
return match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
// Tu
|
||||
case "eeeeee":
|
||||
return (
|
||||
match.day(dateString, { width: "short", context: "formatting" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
|
||||
// Tuesday
|
||||
case "eeee":
|
||||
default:
|
||||
return (
|
||||
match.day(dateString, { width: "wide", context: "formatting" }) ||
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.day(dateString, { width: "short", context: "formatting" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 6;
|
||||
}
|
||||
|
||||
set(date, _flags, value, options) {
|
||||
date = setDay(date, value, options);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"y",
|
||||
"R",
|
||||
"u",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"I",
|
||||
"d",
|
||||
"D",
|
||||
"E",
|
||||
"i",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { setWeek } from "../../../setWeek.js";
|
||||
import { startOfWeek } from "../../../startOfWeek.js";
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
// Local week of year
|
||||
export class LocalWeekParser extends Parser {
|
||||
priority = 100;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "w":
|
||||
return parseNumericPattern(numericPatterns.week, dateString);
|
||||
case "wo":
|
||||
return match.ordinalNumber(dateString, { unit: "week" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 53;
|
||||
}
|
||||
|
||||
set(date, _flags, value, options) {
|
||||
return startOfWeek(setWeek(date, value, options), options);
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"y",
|
||||
"R",
|
||||
"u",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"I",
|
||||
"d",
|
||||
"D",
|
||||
"i",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import { getWeekYear } from "../../../getWeekYear.js";
|
||||
|
||||
import { startOfWeek } from "../../../startOfWeek.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, normalizeTwoDigitYear, parseNDigits } from "../utils.js";
|
||||
|
||||
// Local week-numbering year
|
||||
export class LocalWeekYearParser extends Parser {
|
||||
priority = 130;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
const valueCallback = (year) => ({
|
||||
year,
|
||||
isTwoDigitYear: token === "YY",
|
||||
});
|
||||
|
||||
switch (token) {
|
||||
case "Y":
|
||||
return mapValue(parseNDigits(4, dateString), valueCallback);
|
||||
case "Yo":
|
||||
return mapValue(
|
||||
match.ordinalNumber(dateString, {
|
||||
unit: "year",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
default:
|
||||
return mapValue(parseNDigits(token.length, dateString), valueCallback);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value.isTwoDigitYear || value.year > 0;
|
||||
}
|
||||
|
||||
set(date, flags, value, options) {
|
||||
const currentYear = getWeekYear(date, options);
|
||||
|
||||
if (value.isTwoDigitYear) {
|
||||
const normalizedTwoDigitYear = normalizeTwoDigitYear(
|
||||
value.year,
|
||||
currentYear,
|
||||
);
|
||||
date.setFullYear(
|
||||
normalizedTwoDigitYear,
|
||||
0,
|
||||
options.firstWeekContainsDate,
|
||||
);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return startOfWeek(date, options);
|
||||
}
|
||||
|
||||
const year =
|
||||
!("era" in flags) || flags.era === 1 ? value.year : 1 - value.year;
|
||||
date.setFullYear(year, 0, options.firstWeekContainsDate);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return startOfWeek(date, options);
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"y",
|
||||
"R",
|
||||
"u",
|
||||
"Q",
|
||||
"q",
|
||||
"M",
|
||||
"L",
|
||||
"I",
|
||||
"d",
|
||||
"D",
|
||||
"i",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class MinuteParser extends Parser {
|
||||
priority = 60;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "m":
|
||||
return parseNumericPattern(numericPatterns.minute, dateString);
|
||||
case "mo":
|
||||
return match.ordinalNumber(dateString, { unit: "minute" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 59;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMinutes(value, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["t", "T"];
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class MonthParser extends Parser {
|
||||
incompatibleTokens = [
|
||||
"Y",
|
||||
"R",
|
||||
"q",
|
||||
"Q",
|
||||
"L",
|
||||
"w",
|
||||
"I",
|
||||
"D",
|
||||
"i",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
|
||||
priority = 110;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
const valueCallback = (value) => value - 1;
|
||||
|
||||
switch (token) {
|
||||
// 1, 2, ..., 12
|
||||
case "M":
|
||||
return mapValue(
|
||||
parseNumericPattern(numericPatterns.month, dateString),
|
||||
valueCallback,
|
||||
);
|
||||
// 01, 02, ..., 12
|
||||
case "MM":
|
||||
return mapValue(parseNDigits(2, dateString), valueCallback);
|
||||
// 1st, 2nd, ..., 12th
|
||||
case "Mo":
|
||||
return mapValue(
|
||||
match.ordinalNumber(dateString, {
|
||||
unit: "month",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// Jan, Feb, ..., Dec
|
||||
case "MMM":
|
||||
return (
|
||||
match.month(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.month(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
|
||||
// J, F, ..., D
|
||||
case "MMMMM":
|
||||
return match.month(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
// January, February, ..., December
|
||||
case "MMMM":
|
||||
default:
|
||||
return (
|
||||
match.month(dateString, { width: "wide", context: "formatting" }) ||
|
||||
match.month(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.month(dateString, { width: "narrow", context: "formatting" })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 11;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMonth(value, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits } from "../utils.js";
|
||||
|
||||
export class QuarterParser extends Parser {
|
||||
priority = 120;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
// 1, 2, 3, 4
|
||||
case "Q":
|
||||
case "QQ": // 01, 02, 03, 04
|
||||
return parseNDigits(token.length, dateString);
|
||||
// 1st, 2nd, 3rd, 4th
|
||||
case "Qo":
|
||||
return match.ordinalNumber(dateString, { unit: "quarter" });
|
||||
// Q1, Q2, Q3, Q4
|
||||
case "QQQ":
|
||||
return (
|
||||
match.quarter(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.quarter(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
|
||||
// 1, 2, 3, 4 (narrow quarter; could be not numerical)
|
||||
case "QQQQQ":
|
||||
return match.quarter(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
});
|
||||
// 1st quarter, 2nd quarter, ...
|
||||
case "QQQQ":
|
||||
default:
|
||||
return (
|
||||
match.quarter(dateString, {
|
||||
width: "wide",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.quarter(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "formatting",
|
||||
}) ||
|
||||
match.quarter(dateString, {
|
||||
width: "narrow",
|
||||
context: "formatting",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 4;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMonth((value - 1) * 3, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"Y",
|
||||
"R",
|
||||
"q",
|
||||
"M",
|
||||
"L",
|
||||
"w",
|
||||
"I",
|
||||
"d",
|
||||
"D",
|
||||
"i",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class SecondParser extends Parser {
|
||||
priority = 50;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
case "s":
|
||||
return parseNumericPattern(numericPatterns.second, dateString);
|
||||
case "so":
|
||||
return match.ordinalNumber(dateString, { unit: "second" });
|
||||
default:
|
||||
return parseNDigits(token.length, dateString);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 59;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setSeconds(value, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = ["t", "T"];
|
||||
}
|
||||
Generated
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
import { setDay } from "../../../setDay.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, parseNDigits } from "../utils.js";
|
||||
|
||||
// Stand-alone local day of week
|
||||
export class StandAloneLocalDayParser extends Parser {
|
||||
priority = 90;
|
||||
|
||||
parse(dateString, token, match, options) {
|
||||
const valueCallback = (value) => {
|
||||
// We want here floor instead of trunc, so we get -7 for value 0 instead of 0
|
||||
const wholeWeekDays = Math.floor((value - 1) / 7) * 7;
|
||||
return ((value + options.weekStartsOn + 6) % 7) + wholeWeekDays;
|
||||
};
|
||||
|
||||
switch (token) {
|
||||
// 3
|
||||
case "c":
|
||||
case "cc": // 03
|
||||
return mapValue(parseNDigits(token.length, dateString), valueCallback);
|
||||
// 3rd
|
||||
case "co":
|
||||
return mapValue(
|
||||
match.ordinalNumber(dateString, {
|
||||
unit: "day",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// Tue
|
||||
case "ccc":
|
||||
return (
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.day(dateString, { width: "short", context: "standalone" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "standalone" })
|
||||
);
|
||||
|
||||
// T
|
||||
case "ccccc":
|
||||
return match.day(dateString, {
|
||||
width: "narrow",
|
||||
context: "standalone",
|
||||
});
|
||||
// Tu
|
||||
case "cccccc":
|
||||
return (
|
||||
match.day(dateString, { width: "short", context: "standalone" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "standalone" })
|
||||
);
|
||||
|
||||
// Tuesday
|
||||
case "cccc":
|
||||
default:
|
||||
return (
|
||||
match.day(dateString, { width: "wide", context: "standalone" }) ||
|
||||
match.day(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.day(dateString, { width: "short", context: "standalone" }) ||
|
||||
match.day(dateString, { width: "narrow", context: "standalone" })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 6;
|
||||
}
|
||||
|
||||
set(date, _flags, value, options) {
|
||||
date = setDay(date, value, options);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"y",
|
||||
"R",
|
||||
"u",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"I",
|
||||
"d",
|
||||
"D",
|
||||
"E",
|
||||
"i",
|
||||
"e",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { numericPatterns } from "../constants.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, parseNDigits, parseNumericPattern } from "../utils.js";
|
||||
|
||||
export class StandAloneMonthParser extends Parser {
|
||||
priority = 110;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
const valueCallback = (value) => value - 1;
|
||||
|
||||
switch (token) {
|
||||
// 1, 2, ..., 12
|
||||
case "L":
|
||||
return mapValue(
|
||||
parseNumericPattern(numericPatterns.month, dateString),
|
||||
valueCallback,
|
||||
);
|
||||
// 01, 02, ..., 12
|
||||
case "LL":
|
||||
return mapValue(parseNDigits(2, dateString), valueCallback);
|
||||
// 1st, 2nd, ..., 12th
|
||||
case "Lo":
|
||||
return mapValue(
|
||||
match.ordinalNumber(dateString, {
|
||||
unit: "month",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
// Jan, Feb, ..., Dec
|
||||
case "LLL":
|
||||
return (
|
||||
match.month(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.month(dateString, { width: "narrow", context: "standalone" })
|
||||
);
|
||||
|
||||
// J, F, ..., D
|
||||
case "LLLLL":
|
||||
return match.month(dateString, {
|
||||
width: "narrow",
|
||||
context: "standalone",
|
||||
});
|
||||
// January, February, ..., December
|
||||
case "LLLL":
|
||||
default:
|
||||
return (
|
||||
match.month(dateString, { width: "wide", context: "standalone" }) ||
|
||||
match.month(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.month(dateString, { width: "narrow", context: "standalone" })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 0 && value <= 11;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMonth(value, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"Y",
|
||||
"R",
|
||||
"q",
|
||||
"Q",
|
||||
"M",
|
||||
"w",
|
||||
"I",
|
||||
"D",
|
||||
"i",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
Generated
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseNDigits } from "../utils.js";
|
||||
|
||||
export class StandAloneQuarterParser extends Parser {
|
||||
priority = 120;
|
||||
|
||||
parse(dateString, token, match) {
|
||||
switch (token) {
|
||||
// 1, 2, 3, 4
|
||||
case "q":
|
||||
case "qq": // 01, 02, 03, 04
|
||||
return parseNDigits(token.length, dateString);
|
||||
// 1st, 2nd, 3rd, 4th
|
||||
case "qo":
|
||||
return match.ordinalNumber(dateString, { unit: "quarter" });
|
||||
// Q1, Q2, Q3, Q4
|
||||
case "qqq":
|
||||
return (
|
||||
match.quarter(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.quarter(dateString, {
|
||||
width: "narrow",
|
||||
context: "standalone",
|
||||
})
|
||||
);
|
||||
|
||||
// 1, 2, 3, 4 (narrow quarter; could be not numerical)
|
||||
case "qqqqq":
|
||||
return match.quarter(dateString, {
|
||||
width: "narrow",
|
||||
context: "standalone",
|
||||
});
|
||||
// 1st quarter, 2nd quarter, ...
|
||||
case "qqqq":
|
||||
default:
|
||||
return (
|
||||
match.quarter(dateString, {
|
||||
width: "wide",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.quarter(dateString, {
|
||||
width: "abbreviated",
|
||||
context: "standalone",
|
||||
}) ||
|
||||
match.quarter(dateString, {
|
||||
width: "narrow",
|
||||
context: "standalone",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value >= 1 && value <= 4;
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
date.setMonth((value - 1) * 3, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
incompatibleTokens = [
|
||||
"Y",
|
||||
"R",
|
||||
"Q",
|
||||
"M",
|
||||
"L",
|
||||
"w",
|
||||
"I",
|
||||
"d",
|
||||
"D",
|
||||
"i",
|
||||
"e",
|
||||
"c",
|
||||
"t",
|
||||
"T",
|
||||
];
|
||||
}
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
import { constructFrom } from "../../../constructFrom.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseAnyDigitsSigned } from "../utils.js";
|
||||
|
||||
export class TimestampMillisecondsParser extends Parser {
|
||||
priority = 20;
|
||||
|
||||
parse(dateString) {
|
||||
return parseAnyDigitsSigned(dateString);
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
return [constructFrom(date, value), { timestampIsSet: true }];
|
||||
}
|
||||
|
||||
incompatibleTokens = "*";
|
||||
}
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
import { constructFrom } from "../../../constructFrom.js";
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { parseAnyDigitsSigned } from "../utils.js";
|
||||
|
||||
export class TimestampSecondsParser extends Parser {
|
||||
priority = 40;
|
||||
|
||||
parse(dateString) {
|
||||
return parseAnyDigitsSigned(dateString);
|
||||
}
|
||||
|
||||
set(date, _flags, value) {
|
||||
return [constructFrom(date, value * 1000), { timestampIsSet: true }];
|
||||
}
|
||||
|
||||
incompatibleTokens = "*";
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { Parser } from "../Parser.js";
|
||||
|
||||
import { mapValue, normalizeTwoDigitYear, parseNDigits } from "../utils.js";
|
||||
|
||||
// From http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns
|
||||
// | Year | y | yy | yyy | yyyy | yyyyy |
|
||||
// |----------|-------|----|-------|-------|-------|
|
||||
// | AD 1 | 1 | 01 | 001 | 0001 | 00001 |
|
||||
// | AD 12 | 12 | 12 | 012 | 0012 | 00012 |
|
||||
// | AD 123 | 123 | 23 | 123 | 0123 | 00123 |
|
||||
// | AD 1234 | 1234 | 34 | 1234 | 1234 | 01234 |
|
||||
// | AD 12345 | 12345 | 45 | 12345 | 12345 | 12345 |
|
||||
export class YearParser extends Parser {
|
||||
priority = 130;
|
||||
incompatibleTokens = ["Y", "R", "u", "w", "I", "i", "e", "c", "t", "T"];
|
||||
|
||||
parse(dateString, token, match) {
|
||||
const valueCallback = (year) => ({
|
||||
year,
|
||||
isTwoDigitYear: token === "yy",
|
||||
});
|
||||
|
||||
switch (token) {
|
||||
case "y":
|
||||
return mapValue(parseNDigits(4, dateString), valueCallback);
|
||||
case "yo":
|
||||
return mapValue(
|
||||
match.ordinalNumber(dateString, {
|
||||
unit: "year",
|
||||
}),
|
||||
valueCallback,
|
||||
);
|
||||
default:
|
||||
return mapValue(parseNDigits(token.length, dateString), valueCallback);
|
||||
}
|
||||
}
|
||||
|
||||
validate(_date, value) {
|
||||
return value.isTwoDigitYear || value.year > 0;
|
||||
}
|
||||
|
||||
set(date, flags, value) {
|
||||
const currentYear = date.getFullYear();
|
||||
|
||||
if (value.isTwoDigitYear) {
|
||||
const normalizedTwoDigitYear = normalizeTwoDigitYear(
|
||||
value.year,
|
||||
currentYear,
|
||||
);
|
||||
date.setFullYear(normalizedTwoDigitYear, 0, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
const year =
|
||||
!("era" in flags) || flags.era === 1 ? value.year : 1 - value.year;
|
||||
date.setFullYear(year, 0, 1);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
import {
|
||||
millisecondsInHour,
|
||||
millisecondsInMinute,
|
||||
millisecondsInSecond,
|
||||
} from "../../constants.js";
|
||||
|
||||
import { numericPatterns } from "./constants.js";
|
||||
|
||||
export function mapValue(parseFnResult, mapFn) {
|
||||
if (!parseFnResult) {
|
||||
return parseFnResult;
|
||||
}
|
||||
|
||||
return {
|
||||
value: mapFn(parseFnResult.value),
|
||||
rest: parseFnResult.rest,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseNumericPattern(pattern, dateString) {
|
||||
const matchResult = dateString.match(pattern);
|
||||
|
||||
if (!matchResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
value: parseInt(matchResult[0], 10),
|
||||
rest: dateString.slice(matchResult[0].length),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseTimezonePattern(pattern, dateString) {
|
||||
const matchResult = dateString.match(pattern);
|
||||
|
||||
if (!matchResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Input is 'Z'
|
||||
if (matchResult[0] === "Z") {
|
||||
return {
|
||||
value: 0,
|
||||
rest: dateString.slice(1),
|
||||
};
|
||||
}
|
||||
|
||||
const sign = matchResult[1] === "+" ? 1 : -1;
|
||||
const hours = matchResult[2] ? parseInt(matchResult[2], 10) : 0;
|
||||
const minutes = matchResult[3] ? parseInt(matchResult[3], 10) : 0;
|
||||
const seconds = matchResult[5] ? parseInt(matchResult[5], 10) : 0;
|
||||
|
||||
return {
|
||||
value:
|
||||
sign *
|
||||
(hours * millisecondsInHour +
|
||||
minutes * millisecondsInMinute +
|
||||
seconds * millisecondsInSecond),
|
||||
rest: dateString.slice(matchResult[0].length),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseAnyDigitsSigned(dateString) {
|
||||
return parseNumericPattern(numericPatterns.anyDigitsSigned, dateString);
|
||||
}
|
||||
|
||||
export function parseNDigits(n, dateString) {
|
||||
switch (n) {
|
||||
case 1:
|
||||
return parseNumericPattern(numericPatterns.singleDigit, dateString);
|
||||
case 2:
|
||||
return parseNumericPattern(numericPatterns.twoDigits, dateString);
|
||||
case 3:
|
||||
return parseNumericPattern(numericPatterns.threeDigits, dateString);
|
||||
case 4:
|
||||
return parseNumericPattern(numericPatterns.fourDigits, dateString);
|
||||
default:
|
||||
return parseNumericPattern(new RegExp("^\\d{1," + n + "}"), dateString);
|
||||
}
|
||||
}
|
||||
|
||||
export function parseNDigitsSigned(n, dateString) {
|
||||
switch (n) {
|
||||
case 1:
|
||||
return parseNumericPattern(numericPatterns.singleDigitSigned, dateString);
|
||||
case 2:
|
||||
return parseNumericPattern(numericPatterns.twoDigitsSigned, dateString);
|
||||
case 3:
|
||||
return parseNumericPattern(numericPatterns.threeDigitsSigned, dateString);
|
||||
case 4:
|
||||
return parseNumericPattern(numericPatterns.fourDigitsSigned, dateString);
|
||||
default:
|
||||
return parseNumericPattern(new RegExp("^-?\\d{1," + n + "}"), dateString);
|
||||
}
|
||||
}
|
||||
|
||||
export function dayPeriodEnumToHours(dayPeriod) {
|
||||
switch (dayPeriod) {
|
||||
case "morning":
|
||||
return 4;
|
||||
case "evening":
|
||||
return 17;
|
||||
case "pm":
|
||||
case "noon":
|
||||
case "afternoon":
|
||||
return 12;
|
||||
case "am":
|
||||
case "midnight":
|
||||
case "night":
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeTwoDigitYear(twoDigitYear, currentYear) {
|
||||
const isCommonEra = currentYear > 0;
|
||||
// Absolute number of the current year:
|
||||
// 1 -> 1 AC
|
||||
// 0 -> 1 BC
|
||||
// -1 -> 2 BC
|
||||
const absCurrentYear = isCommonEra ? currentYear : 1 - currentYear;
|
||||
|
||||
let result;
|
||||
if (absCurrentYear <= 50) {
|
||||
result = twoDigitYear || 100;
|
||||
} else {
|
||||
const rangeEnd = absCurrentYear + 50;
|
||||
const rangeEndCentury = Math.trunc(rangeEnd / 100) * 100;
|
||||
const isPreviousCentury = twoDigitYear >= rangeEnd % 100;
|
||||
result = twoDigitYear + rangeEndCentury - (isPreviousCentury ? 100 : 0);
|
||||
}
|
||||
|
||||
return isCommonEra ? result : 1 - result;
|
||||
}
|
||||
|
||||
export function isLeapYearIndex(year) {
|
||||
return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
|
||||
}
|
||||
Reference in New Issue
Block a user