diff --git a/README.md b/README.md index 79b6426b4..5d50e5f36 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Validator | Description **isISIN(str)** | check if the string is an [ISIN][ISIN] (stock/security identifier). **isISO6346(str)** | check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification. **isISO6391(str)** | check if the string is a valid [ISO 639-1][ISO 639-1] language code. -**isISO8601(str [, options])** | check if the string is a valid [ISO 8601][ISO 8601] date.
`options` is an object which defaults to `{ strict: false, strictSeparator: false }`. If `strict` is true, date strings with invalid dates like `2009-02-29` will be invalid. If `strictSeparator` is true, date strings with date and time separated by anything other than a T will be invalid. +**isISO8601(str [, options])** | check if the string is a valid [ISO 8601][ISO 8601] date.
`options` is an object which defaults to `{ strict: false, strictSeparator: false, extendedYearFormat: false }`. If `strict` is true, date strings with invalid dates like `2009-02-29` will be invalid. If `strictSeparator` is true, date strings with date and time separated by anything other than a T will be invalid. If `extendedYearFormat` is true, date strings with years consisting of more than 4 digits and preceded by the '+' or '-' symbol are also valid. **isISO31661Alpha2(str)** | check if the string is a valid [ISO 3166-1 alpha-2][ISO 3166-1 alpha-2] officially assigned country code. **isISO31661Alpha3(str)** | check if the string is a valid [ISO 3166-1 alpha-3][ISO 3166-1 alpha-3] officially assigned country code. **isISO31661Numeric(str)** | check if the string is a valid [ISO 3166-1 numeric][ISO 3166-1 numeric] officially assigned country code. diff --git a/src/lib/isISO8601.js b/src/lib/isISO8601.js index 1f797347d..bafcc5924 100644 --- a/src/lib/isISO8601.js +++ b/src/lib/isISO8601.js @@ -1,17 +1,21 @@ import assertString from './util/assertString'; /* eslint-disable max-len */ -// from http://goo.gl/0ejHHW +// from https://goo.gl/0ejHHW const iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([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)?)?)?)?$/; // 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-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-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)?)?)?)?$/; +// same as above, except with extended year format +const iso8601ExtendedYearFormat = /^([\+-]\d{4,}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([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)?)?)?)?$/; +const iso8601ExtendedYearFormatStrictSeparator = /^([\+-]\d{4,}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-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 */ const isValidDate = (str) => { // str must have passed the ISO8601 check // this check is meant to catch invalid dates // like 2009-02-31 // first check for ordinal dates - const ordinalMatch = str.match(/^(\d{4})-?(\d{3})([ T]{1}\.*|$)/); + const ordinalMatch = str.match(/^(\d{4,})-?(\d{3})([ T]{1}\.*|$)/); if (ordinalMatch) { const oYear = Number(ordinalMatch[1]); const oDay = Number(ordinalMatch[2]); @@ -19,7 +23,7 @@ const isValidDate = (str) => { if ((oYear % 4 === 0 && oYear % 100 !== 0) || oYear % 400 === 0) return oDay <= 366; return oDay <= 365; } - const match = str.match(/(\d{4})-?(\d{0,2})-?(\d*)/).map(Number); + const match = str.match(/(\d{4,})-?(\d{0,2})-?(\d*)/).map(Number); const year = match[1]; const month = match[2]; const day = match[3]; @@ -38,7 +42,12 @@ const isValidDate = (str) => { export default function isISO8601(str, options = {}) { assertString(str); - const check = options.strictSeparator ? iso8601StrictSeparator.test(str) : iso8601.test(str); + let check = options.strictSeparator ? iso8601StrictSeparator.test(str) : iso8601.test(str); + if (!check && options.extendedYearFormat) { + check = options.strictSeparator + ? iso8601ExtendedYearFormatStrictSeparator.test(str) + : iso8601ExtendedYearFormat.test(str); + } if (check && options.strict) return isValidDate(str); return check; }