javascript regex iso datetime

84,907

Solution 1

For the strict, full datetime, including milliseconds, per the W3C's take on the spec.:

//-- Complete precision:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/

//-- No milliseconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/

//-- No Seconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/

//-- Putting it all together:
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/

.
Additional variations allowed by the actual ISO 8601:2004(E) doc:

/********************************************
**    No time-zone varients:
*/
//-- Complete precision:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+/

//-- No milliseconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d/

//-- No Seconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d/

//-- Putting it all together:
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+)|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d)|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d)/

WARNING: This all gets messy fast, and it still allows certain nonsense such as a 14th month. Additionally, ISO 8601:2004(E) allows a several other variants.

.
"2010-06-15T00:00:00" isn't legal, because it doesn't have the time-zone designation.

Solution 2

For matching just ISO date, like 2017-09-22, you can use this regexp:

^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$

It will match any numeric year, any month specified by two digits in range 00-12 and any date specified by two digits in range 00-31

Solution 3

I reworked the top answer into something a bit more concise. Instead of writing out each of the three optional patterns, the elements are nested as optional statements.

/[+-]?\d{4}(-[01]\d(-[0-3]\d(T[0-2]\d:[0-5]\d:?([0-5]\d(\.\d+)?)?[+-][0-2]\d:[0-5]\dZ?)?)?)?/

I'm curious if there are downsides to this approach?

You can find tests for my suggested answer here: http://regexr.com/3e0lh

Solution 4

I have made this regex and solves the validation for dates as they come out of Javascript's .toISOString() method.

^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\.[0-9]{3}Z$

Contemplated:

  • Proper symbols ('-', 'T', ':', '.', 'Z') in proper places.
  • Consistency with months of 29, 30 or 31 days.
  • Hours from 00 to 23.
  • Minutes and seconds from 00 to 59.
  • Milliseconds from 000 to 999.

Not contemplated:

  • Leap years.

Example date: 2019-11-15T13:34:22.178Z

Example to run directly in Chrome console: /^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\.[0-9]{3}Z$/.test("2019-11-15T13:34:22.178Z");

Regex flow diagram (Regexper): regex flow diagram

Solution 5

Here is a regular expression to check ISO 8601 date format including leap years and short-long months. To run this, you'll need to "ignore white-space". A compacted version without white-space is on regexlib: http://regexlib.com/REDetails.aspx?regexp_id=3344

There's more to ISO 8601 - this regex only cares for dates, but you can easily extend it to support time validation which is not that tricky.

Update: This works now with javascript (without lookbehinds)

  ^(?:
      (?=
            [02468][048]00
            |[13579][26]00
            |[0-9][0-9]0[48]
            |[0-9][0-9][2468][048]
            |[0-9][0-9][13579][26]              
      )

      \d{4}

      (?:

        (-|)

        (?:

            (?:
                00[1-9]
                |0[1-9][0-9]
                |[1-2][0-9][0-9]
                |3[0-5][0-9]
                |36[0-6]
            )
            |
                (?:01|03|05|07|08|10|12)
                (?:
                  \1
                  (?:0[1-9]|[12][0-9]|3[01])
                )?            
            |
                (?:04|06|09|11)
                (?:
                  \1
                  (?:0[1-9]|[12][0-9]|30)
                )?            
            |
                02
                (?:
                  \1
                  (?:0[1-9]|[12][0-9])
                )?

            |
                W(?:0[1-9]|[1-4][0-9]|5[0-3])
                (?:
                  \1
                  [1-7]
                )?

        )            
      )?
  )$
  |
  ^(?:
      (?!
            [02468][048]00
            |[13579][26]00
            |[0-9][0-9]0[48]
            |[0-9][0-9][2468][048]
            |[0-9][0-9][13579][26]              
      )

      \d{4}

      (?:

        (-|)

        (?:

            (?:
                00[1-9]
                |0[1-9][0-9]
                |[1-2][0-9][0-9]
                |3[0-5][0-9]
                |36[0-5]
            )
            |
                (?:01|03|05|07|08|10|12)
                (?:
                  \2
                  (?:0[1-9]|[12][0-9]|3[01])
                )?

            |
                (?:04|06|09|11)
                (?:
                  \2
                  (?:0[1-9]|[12][0-9]|30)
                )?
            |
                (?:02)
                (?:
                  \2
                  (?:0[1-9]|1[0-9]|2[0-8])
                )?
            |
                W(?:0[1-9]|[1-4][0-9]|5[0-3])
                (?:
                  \2
                  [1-7]
                )?
       ) 
    )?
)$

To cater for time, add something like this to the mixture (from: http://underground.infovark.com/2008/07/22/iso-date-validation-regex/ ):

([T\s](([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)?(\15([0-5]\d))?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?
Share:
84,907

Related videos on Youtube

Scott Klarenbach
Author by

Scott Klarenbach

Updated on July 05, 2022

Comments

  • Scott Klarenbach
    Scott Klarenbach almost 2 years

    does anyone have a good regex pattern for matching iso datetimes?

    ie: 2010-06-15T00:00:00

    • mykhal
      mykhal almost 14 years
      i use /^(\d{4})-0?(\d+)-0?(\d+)[T ]0?(\d+):0?(\d+):0?(\d+)$/, (which however is not the most strict one) .. conversion to the Date is a different story :)
  • Scott S. McCoy
    Scott S. McCoy almost 14 years
    ISO-8601 says that if the timezone is omitted it's assumed to be UTC, this includes the 'Z'.
  • Brock Adams
    Brock Adams almost 14 years
    @Scott S. McCoy: I went off the W3C's interpretation (w3.org/TR/NOTE-datetime) like a good web-developer should. ;-) I also don't consider Wikipedia to be a definitive source. But, you are right. According to the actual spec (dotat.at/tmp/ISO_8601-2004_E.pdf), the timezone offset is optional.
  • Scott S. McCoy
    Scott S. McCoy almost 14 years
    I wholeheartedly agree that Wikipedia is not a definitive source. But for some topics it makes a reasonable reference, and the article on ISO-8601 has palatable examples and digestible explanations. :-D
  • Admin
    Admin about 11 years
    Negative years are allowed so there should be a optional '-' character before years, see w3.org/TR/xmlschema-2/#dateTime : The ·lexical space· of dateTime consists of finite-length sequences of characters of the form: '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
  • Admin
    Admin about 11 years
    ISO 8601 states that the 'T' may be omitted under some circumstances. Also, RFC 3389 extends ISO 8601 tools.ietf.org/html/rfc3339#page-12 , which allows for a space character in place of the 'T'.
  • sidonaldson
    sidonaldson about 9 years
    Wow. This answer is referenced in the angular source code! @BrockAdams
  • Brock Adams
    Brock Adams about 9 years
    @sidonaldson, Thanks for the notice! I'm rather conflicted though, as I suspect that regex is not the optimal way to validate something as messy as date formats. (But this Q asked for regex and presumably the OP can live with the computational cost and small accuracy holes -- while we wouldn't want that in a library, language, or app engine.)
  • mortb
    mortb about 9 years
    Confirmed, that approach doesn't work. The problem is with IE. 2014-06-44 becomes 2014-08-13 as IE treats the overflowing date (>30) as a date the next month. I've tried in IE8 and IE11. Works in Chrome though. Very annoying.
  • Thanish
    Thanish over 8 years
    Thanks for the regex. To tweak it a bit, we can use (?: ) instead of ( ) to avoid capturing a group. e.g. \d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2‌​]\d:[0-5]\d|Z) for the first regex.
  • Brock Adams
    Brock Adams over 8 years
    @Thanish, yes you can. The capture group normally doesn't hurt anything, though, and can optionally help you determine which style was matched, if you need to for some reason.
  • Luis
    Luis about 8 years
    Regex escaped (for the first regex): expect(value).toMatch('\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5‌​]\\d:[0-5]\\d\\.\\d+‌​(?:[+-][0-2]\\d:[0-5‌​]\\d|Z)');
  • mcfedr
    mcfedr about 8 years
    This will fail for leap seconds, i.e, that occasional 61st second.
  • Frank
    Frank over 7 years
    This won't work with: 2016-09-05T15:22:26.286Z Making [+-][0-2]\d:[0-5]\d optional makes it work: [+-]?\d{4}(-[01]\d(-[0-3]\d(T[0-2]\d:[0-5]\d:?([0-5]\d(\.\d+‌​)?)?([+-][0-2]\d:[0-‌​5]\d)?Z?)?)?)?
  • ferpel
    ferpel over 4 years
    this regex allows invalid dates as: 19-02-29T20:59:39.217Z where the year doesn't respect ISO format yyyy you can try this in Chrome console to check it: /(((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])‌​))-02-29)|(((19|2[0-‌​9])[0-9]{2})-02-(0[1‌​-9]|1[0-9]|2[0-8]))|‌​(((19|2[0-9])[0-9]{2‌​})-(0[13578]|10|12)-‌​(0[1-9]|[12][0-9]|3[‌​01]))|(((19|2[0-9])[‌​0-9]{2})-(0[469]|11)‌​-(0[1-9]|[12][0-9]|3‌​0)))T([01][0-9]|[2][‌​0-3]):[0-5][0-9]:[0-‌​5][0-9]\.[0-9]{3}Z/.‌​test("19-02-29T20:59‌​:39.217Z");
  • ferpel
    ferpel over 4 years
    this allows strange things to be valid as: 28T09:5220.5690463826472487295963210360122513980205942+01:53 try this in Chrome console to check it: /[+-]?\d{4}(-[01]\d(-[0-3]\d(T[0-2]\d:[0-5]\d:?([0-5]\d(\.\d‌​+)?)?[+-][0-2]\d:[0-‌​5]\dZ?)?)?)?/.test("‌​28T09:5220.569046382‌​64724872959632103601‌​22513980205942+01:53‌​");
  • ferpel
    ferpel over 4 years
    this allows strange things to be valid as: 1881-16-27T11:06:38.9986346665253151+01:34 try this in Chrome console to check it: /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2‌​]\d:[0-5]\d|Z))|(\d{‌​4}-[01]\d-[0-3]\dT[0‌​-2]\d:[0-5]\d:[0-5]\‌​d([+-][0-2]\d:[0-5]\‌​d|Z))|(\d{4}-[01]\d-‌​[0-3]\dT[0-2]\d:[0-5‌​]\d([+-][0-2]\d:[0-5‌​]\d|Z))/.test("1881-‌​16-27T11:06:38.99863‌​46665253151+01:34");
  • Toto
    Toto over 4 years
    Why a such complex regex if it doesn't validate leap years? neither leap second.
  • ferpel
    ferpel over 4 years
    @Toto Because is the best between the ones posted previously. It respects each item I have pointed in the "contemplates" section. Try the regex in this answer and the other posted previously using this site, and you will see what I'm talking about. (I have also commented the problems in each of the other regex posted showing why them have flaws)
  • Toto
    Toto over 4 years
    Sorry but it's not better because it doesn't handle leap years.
  • ferpel
    ferpel over 4 years
    It is better because the others does not make what they promise. Please, try the other regex from this different answers with the page I have suggested in the previous comment and you will see what I am trying to explain.
  • Yigit Erol
    Yigit Erol over 3 years
    How is 00 valid for either month or day?
  • 1nstinct
    1nstinct over 3 years
    Moment.js doesn't like tailing 'Z'. It says 'it is not in a recognized RFC2822 or ISO format'
  • Raddish IoW
    Raddish IoW about 3 years
    This caused my auth token to be caught because it had a matching part matched by the section \d{4}.