Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/lib/isISO8601.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ const iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]
// same as above, except with a strict 'T' separator between date and time
const iso8601StrictSeparator = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W(0[1-9]|[1-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[0-6])))([T]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;
/* eslint-enable max-len */
// Returns true iff ISO week-numbering year `year` has a 53rd week, i.e. 31
// December of that year falls within ISO week 53 rather than week 1 of the
// following year. Only week 53 is year-dependent -- weeks 01-52 always exist
// -- and the main iso8601 regex already constrains the week field to 01-53
// syntactically, so this is the only additional check strict mode needs.
const hasISOWeek53 = (year) => {
const dec31 = new Date(Date.UTC(year, 11, 31));
const dayNum = dec31.getUTCDay() || 7; // Sunday (0) -> 7, so Mon=1..Sun=7
dec31.setUTCDate(dec31.getUTCDate() + (4 - dayNum)); // Thursday of the same ISO week
const yearStart = new Date(Date.UTC(dec31.getUTCFullYear(), 0, 1));
const week = Math.ceil((((dec31 - yearStart) / 86400000) + 1) / 7);
return week === 53;
};

const isValidDate = (str) => {
// str must have passed the ISO8601 check
// this check is meant to catch invalid dates
Expand All @@ -19,6 +33,13 @@ const isValidDate = (str) => {
if ((oYear % 4 === 0 && oYear % 100 !== 0) || oYear % 400 === 0) return oDay <= 366;
return oDay <= 365;
}
// then check for week dates (YYYY-Www or YYYY-Www-D, basic or extended)
const weekMatch = str.match(/^([\+-]?\d{4})-?W(\d{2})/);
if (weekMatch) {
const wYear = Number(weekMatch[1]);
const week = Number(weekMatch[2]);
return week <= 52 || hasISOWeek53(wYear);
}
const match = str.match(/(\d{4})-?(\d{0,2})-?(\d*)/).map(Number);
const year = match[1];
const month = match[2];
Expand Down
18 changes: 18 additions & 0 deletions test/validators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12443,12 +12443,30 @@ describe('Validators', () => {
'2009-222',
'2020-366',
'2400-366',
// week 53 exists in these ISO week-numbering years (long years)
'2015-W53-1',
'2015-W53',
'2020-W53-7',
'2026-W53',
// weeks 01-52 exist in every ISO week-numbering year, long or short
'2020-W10',
'2020-W10-3',
],
invalid: [
'2010-02-30',
'2009-02-29',
'2009-366',
'2019-02-31',
// week 53 does not exist in these ISO week-numbering years (short
// years) -- regression test for #2859: previously strict mode never
// validated week dates at all, so these were wrongly accepted.
'2019-W53-1',
'2019-W53',
'2021-W53-7',
'2022-W53',
// 31 December 2017 is a Sunday, so the ISO remap of getUTCDay() 0 -> 7
// runs here; 2017 is still a short year, so week 53 does not exist.
'2017-W53',
],
});
});
Expand Down
Loading