diff --git a/src/lib/isISO8601.js b/src/lib/isISO8601.js index 6eea9ae27..5a6780ef8 100644 --- a/src/lib/isISO8601.js +++ b/src/lib/isISO8601.js @@ -2,9 +2,11 @@ import assertString from './util/assertString'; /* eslint-disable max-len */ // from http://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[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\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)?)?)?)?$/; +// Handle hour 24 separately: all lower-order components must be zero. +// The (?!24) guard prevents the optional-hour branch from reading 24 as seconds. +const iso8601 = /^([\+-]?\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\s](?:(?!24)((([01]\d|2[0-3])((:?)[0-5]\d)?)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?|24(?:00(?:00)?|:00(?::00)?)?(?:[.,]0+)?)([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[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)?)?)?)?$/; +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](?:(?!24)((([01]\d|2[0-3])((:?)[0-5]\d)?)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?|24(?:00(?:00)?|:00(?::00)?)?(?:[.,]0+)?)([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; /* eslint-enable max-len */ const isValidDate = (str) => { // str must have passed the ISO8601 check diff --git a/test/validators.test.js b/test/validators.test.js index 98d2a12ff..492f1f48a 100644 --- a/test/validators.test.js +++ b/test/validators.test.js @@ -12420,6 +12420,66 @@ describe('Validators', () => { }); }); + [{}, { strict: true }, { strictSeparator: true }, { strict: true, strictSeparator: true }] + .forEach((options) => { + it(`should validate ISO 8601 end-of-day times with ${JSON.stringify(options)}`, () => { + test({ + validator: 'isISO8601', + args: [options], + valid: [ + '2009-01-01T24', + '2009-01-01T24.0', + '2009-01-01T2400', + '2009-01-01T24:00', + '2009-01-01T240000', + '2009-01-01T24:00:00', + '2009-01-01T2400,000', + '2009-01-01T24:00.000', + '2009-01-01T240000.000', + '2009-01-01T24:00:00,000', + '2009-01-01T24:00:00Z', + '2009-01-01T240000+0530', + '2009-01-01T24:00:00.000-05:30', + '2009-01-01T24:00:00-01', + ], + }); + }); + + it(`should reject invalid ISO 8601 end-of-day times with ${JSON.stringify(options)}`, () => { + test({ + validator: 'isISO8601', + args: [options], + invalid: [ + '2009-01-01T24:0000', + '2009-01-01T2400:00', + '2009-01-01T24:0030', + '2009-01-01T240030', + '2009-01-01T24:01', + '2009-01-01T2401', + '2009-01-01T24:00:01', + '2009-01-01T240001', + '2009-01-01T24.1', + '2009-01-01T24:00.0001', + '2009-01-01T2400,0001', + '2009-01-01T24:00:00.001Z', + '2009-01-01T240000.1+0530', + '2009-01-01T24:00.0:00', + '2009-01-01T24:00:00+24:00', + ], + }); + }); + + it(`should preserve ISO 8601 date-time separator handling with ${JSON.stringify(options)}`, () => { + test({ + validator: 'isISO8601', + args: [options], + [options.strictSeparator ? 'invalid' : 'valid']: [ + '2009-01-01 24:00:00', + ], + }); + }); + }); + it('should validate ISO 8601 dates, with strict = true (regression)', () => { test({ validator: 'isISO8601',