Regex and ISO8601 formatted DateTime

JavascriptRegexDatetimeIso8601

Javascript Problem Overview


I have a DateTime string ISO8601 formated

2012-10-06T04:13:00+00:00

and the following Regex which does not match this string

#(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})\+(\d{2})\:(\d{2})#

I can't figure out why it does not match.

I escaped metacharacters, for me it seems to be OK.

http://jsfiddle.net/5n5vk/2/

EDIT :

The right way: http://jsfiddle.net/5n5vk/3/

Javascript Solutions


Solution 1 - Javascript

Incomplete Regex

It's incomplete as it matches invalid date such as 2013-99-99T04:13:00+00:00.

Better solution

The regex below won't match this kind of invalid date (cf. ISO 8601 Date Validation That Doesn’t Suck). You can test with the following code :

re = /^([\+-]?\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-2])(-?[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)?)?)?)?$/
var testDates = {
    'date' : "2012-10-06T04:13:00+00:00",
    'validDate' : "0785-10-10T04:13:00+00:00",
    'invalidDate' : "2013-99-99T04:13:00+00:00",
    '1234Date': '1234'
}
for (var d in testDates) {
    if (re.test(testDates[d])) { console.info('[valid]: '+testDates[d]); }
    else { console.error('[invalid]: '+testDates[d]); }
}

Solution 2 - Javascript

I found the RegExp that also tries to validate the date a bit overkill for me. I Just wanted to know if a string contains an ISO 8601 date string. I'll check if the date is actually valid after I have converted it to a Date object.

Here are 2 versions of the RegExp. This first checks if the string is a valid ISO 8601 date string. The other tests for a full date string including the hours/minutes/seconds (Commonly used in API's)

/**
 * RegExp to test a string for a ISO 8601 Date spec
 *  YYYY
 *  YYYY-MM
 *  YYYY-MM-DD
 *  YYYY-MM-DDThh:mmTZD
 *  YYYY-MM-DDThh:mm:ssTZD
 *  YYYY-MM-DDThh:mm:ss.sTZD
 * @see: https://www.w3.org/TR/NOTE-datetime
 * @type {RegExp}
 */
var ISO_8601 = /^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/i



/**
 * RegExp to test a string for a full ISO 8601 Date
 * Does not do any sort of date validation, only checks if the string is according to the ISO 8601 spec.
 *  YYYY-MM-DDThh:mm:ss
 *  YYYY-MM-DDThh:mm:ssTZD
 *  YYYY-MM-DDThh:mm:ss.sTZD
 * @see: https://www.w3.org/TR/NOTE-datetime
 * @type {RegExp}
 */
var ISO_8601_FULL = /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/i


// Usage:

ISO_8601_FULL.test( "2016-05-24T15:54:14.876Z" )  // true
ISO_8601_FULL.test( "2002-12-31T23:00:00+01:00" ) // true
ISO_8601_FULL.test( "2016-02-01" )                // false
ISO_8601_FULL.test( "2016" )                      // false

ISO_8601.test( "2016-02-01" )                     // true
ISO_8601.test( "2016" )                           // true
ISO_8601.test( "2002-12-31T23:00:00+01:00" )      // true

Solution 3 - Javascript

Don't quote the regex when specifying a regex in js. Forward slash is enough.

alert($('#datepicker').val());

if($('#datepicker').val().match(
    /(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})[+-](\d{2})\:(\d{2})/
)) {
    alert('ok');
} else {
    alert('not ok');
}​

Solution 4 - Javascript

JavaScript date.toISOString() regex

This only attempts to solve the basic pattern of 2017-06-17T00:00:00.000Z that you expect from Javascript doing it.

const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;

One of the most annoying things about JSON is one cannot simply pass a date through and expect it to convert properly. Since most people use JavaScript, this is probably practical.

Here's a demo snippet if you have to pass to mongo and need to convert.

if (isoPattern.test(json.startDate))
  json.startDate = new Date(json.startDate);

I argue this is a better approach as you can be assured the date will parse, then you can check desired range, all being pretty straight forward and easy to maintain as regex is great but to a point.

Solution 5 - Javascript

To add to all these good answers, I found this one to be working quite good for just ISO dates (no time)

(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))

(v=pass x=does-not-pass)

2016-12-30 v
2016-13-31 x
2016-01-32 x
2016-02-29 v
2016-02-30 x
2017-02-29 v -> that's a false positive
1889-01-01 x -> you can add accepted centuries in the list: (?:18|19|20)
2099-01-01 v

Solution 6 - Javascript

How about only testing if you can create a Date object of the string, if that is the purpose of the test?

new Date("2016-05-24T15:54:14.876Z").toString() === 'Invalid Date' // false
new Date("Invalid date").toString() === 'Invalid Date' // true

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTwystOView Question on Stackoverflow
Solution 1 - JavascriptÉdouard LopezView Answer on Stackoverflow
Solution 2 - JavascriptSnailCrusherView Answer on Stackoverflow
Solution 3 - JavascriptPeter KuharView Answer on Stackoverflow
Solution 4 - JavascriptKing FridayView Answer on Stackoverflow
Solution 5 - JavascriptstallingOneView Answer on Stackoverflow
Solution 6 - JavascriptpatriquesView Answer on Stackoverflow