34

For example: 2015-01-17T18:23:02+00:00

Having some trouble with the regex as certain components of the string to be considered 'valid' are speculated and may not be required.

Also, the fact the string can be formatted as 2015-01-17T18:23:02Z is throwing me slightly.

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
Jack hardcastle
  • 2,748
  • 4
  • 22
  • 40

8 Answers8

76

Based on an earlier answer of mine, you could do this and be pretty darn strict:

^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:Z|[+-][01]\d:[0-5]\d)$

Regular expression visualization

Debuggex Demo

Slightly monstrous but it checks for valid dates including leap-year (Proleptic Gregorian), works for years 1000-9999, checks for invalid times like 25:30 or 21:94 and a maximum UTC offset of +/-19:59 (or a Z).

(right now more than +14:00 or -12:00 doesn't happen, but it might in the future).

For completion: This answer only supports a subset of the ISO8601 standard based on the examples OP gave. Which is the extended notation with seconds in the time section and minutes in the UTC offset. For brevity it does not support basic notation where dashes and colons are omitted, or the omitting of minutes in the UTC offset or the smallest unit. Nor is there support for ordinal dates (day-of-year) or year-week-dayofweek notations.

An extended version of the regex that supports basic notation, ordinal and the omitting of seconds / UTC-offset minutes lives here.

asontu
  • 4,548
  • 1
  • 21
  • 29
  • 3
    Im not sure if nanoseconds/fraction of seconds are needed in iso 8601 offset datetime format, but this regex does not signal, that this datetime is valid: 2017-02-17T10:12:56.000008765+01:00 – hiaclibe Feb 17 '17 at 08:37
  • 3
    What worked for me with fractions of second: ^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+|.{0})(?:Z|[+-][01]\d:[0-5]\d)$ – hiaclibe Feb 17 '17 at 09:10
  • These are valid ISO8601: 20170604T0000Z, 2017060T0000Z – Ole Tange Jun 04 '17 at 21:01
  • 1
    @OleTange true but outside of the scope of this answer. Your 2nd example looks like a typo of the 1st one, so I would discourage anyone from accepting that unless they really know what they're doing. You could extend my answer like *[so](https://www.debuggex.com/r/uUgMt0Ub5qpu1hVc)* but it introduces imperfections. In this version you can have basic date with extended time which is not allowed. Solving that would double the size of the regex and then you haven't accounted for proper `year-week-dayofweek` notations etc. This is a small answer for a limited scope and should be treated as such :) – asontu Jun 19 '17 at 09:01
  • "I would discourage anyone from accepting that unless they really know what they're doing" OP asks for ISO8601 - not ISO8601 with your own modifications. Standards are here to make sure we all use the same format. By inventing your own and knowingly passing it off as ISO8601 seems dishonest to me. At the very least you make it clear which parts of the standard you ignore. – Ole Tange Jun 19 '17 at 09:44
  • @OleTange that's fair enough, clarification added. – asontu Jun 19 '17 at 11:11
17

Based on the previous answer, this regex handles the fraction of seconds.

^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d{1,9})?(?:Z|[+-][01]\d:[0-5]\d)$

Debuggex Demo

Nicolas Henneaux
  • 11,507
  • 11
  • 57
  • 82
4

In this link we see the different ISO 8601 formats based on different values we would like to include.

For the example in the question, 2015-01-17T18:23:02+00:00, below regex should work.

[0-9]{4}-[0-9]{2}-[0-9]{2}T([0-9]{2}:){2}[0-9]{2}[+|-][0-9]{2}:[0-9]{2}

Here [+|-] is for possible time zone offsets.

Community
  • 1
  • 1
Shree Harsha S
  • 665
  • 7
  • 14
1

If you just want to match time zones use this:

^[+|-][0-1][0-9]:[0-5][0-9]$

The max is + or - 19:59 which is sufficient

Nick Gallimore
  • 1,222
  • 13
  • 31
1
^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]{3})?(Z)?$

DateTime. Also covers optional Milliseconds.

Parth Chokshi
  • 642
  • 4
  • 16
  • Please, don't post twice the [same answer](https://stackoverflow.com/a/57959149/372239) – Toto Sep 16 '19 at 15:02
1

if using regex is not mandatory, I think the best way to detect it in js is by doing this :

function isIso8601(value) {
    return new Date(value).toJSON() === value;
}
isIso8601('2019-08-21T16:35:05.073Z'); // true
isIso8601('2013-99-99T04:13:00+00:00'); // false
Abdo Driowya
  • 129
  • 3
  • 11
0

to match with date and timezones

2019-07-01T24:15:00+19:00

\d{4}-[01]{1}\d{1}-[0-3]{1}\d{1}T[0-2]{1}\d{1}:[0-6]{1}\d{1}:[0-6]{1}\d{1}[+|-][0-1][0-9]:[0-5][0-9]$

test it here https://regexr.com/4gmi2

Ranidu
  • 511
  • 5
  • 7
0

After struggling for a few hours I got this regex working:

^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:Z|[+-](\b(0[1-9]|1[0-7])\b:[0-5][0-9]|18:00))$

The only issue I've found so far has been that it allows for February 29th on non-leap years. Also, it only generates 4-digit years, but that is easier to change.

I've used this page to generate strings matching said regex and then tested them in this online Java compiler by trying

System.out.println(OffsetDateTime.parse(toParse[i]));
Lucas
  • 523
  • 2
  • 10
  • 20
Ale T.
  • 1
  • 1