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;
}