Community Patterns

1

Strong Matcher for ISO 8601 / RFC 3339 Date Times; rejects bad TZ offsets, illegal times (Museum of Bad Data)

Created·2023-05-14 07:19
Flavor·ECMAScript (JavaScript)
Handles many nuanced cases around time zone offsets, leap seconds and leap days. References: List of UTC Offsets RFC 3339, the stricter rules that most systems use in practice ISO 8601, the widely known name for this format Leap Year Leap Second Caveats: Rejects -00:00 timezone offset. rejects all future leap seconds, and only parses ones with a 'Z' offsets. Pattern (?= (?:^ (?: # All non-leap-second YYYY-MM-DD parts: \d\d\d\d-(?:01-9] |10|11|12)-(?:0[1-9]|1[0-9]|2[0-8]) # Days 01-28 | \d\d\d\d-(?:0[13-9] |10|11|12)-(?:29|30) # Days 29+30 | \d\d\d\d-(?:0[13578]|10 |12)-31 # Day 31 | (?:\d\d[2468|\d\d048]|\d\d[13579)-02-29 # leap years not divisible by 100 | (?:0246800|1357900)-02-29 # leap years divisible by 400 ) T (?:(?:00-9]|1[0-9]|2[0-3]):(?:[0-5):(?:0-5)) # time part (?:\.\d\d\d)? # ms part (optional) (?:Z|\+\-|1[012]):00|\+0[34569]:30|\+10:30|-0[39]:30|\+1[34]:00|\+0[58]:45|\+12:45) $) |^(?:1972 |198[1235]|199[2347]|2012|2015 )-06-30T23:59:60Z$ # all june leapsecs |^(?:197[2-9]|1987|1989|199[058] |2005|2008|2016)-12-31T23:59:60Z$ # all december leapsecs ) (?!.*-00:00$) # if given a -00:00 offset reject unconditionally, psychhhh OK! Since only valid times are now possible, we can use a loose pattern match to parse. ^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:\.(\d\d\d))?((Z)|([\+\-])(\d\d):(\d\d))$ Test cases // accept: Exemplars 2008-02-03T04:05:06.007Z 2008-02-03T04:05:06Z 0000-01-01T00:00:00.000Z 9999-12-31T59:59:59.999Z 9999-12-31T59:59:59Z 2008-02-03T04:05:06.007+12:45 2008-02-03T04:05:06+03:30 0000-01-01T00:00:00.007-09:30 0000-02-29T04:05:06.007+14:00 9999-12-31T23:59:59.999+12:00 9999-12-31T23:59:59-12:00 // accept: Leap day, year is multiple of four 0004-02-29T04:05:06Z 0096-02-29T04:05:06Z 1560-02-29T04:05:06Z 2004-02-29T04:05:06Z 2020-02-29T04:05:06Z 2032-02-29T04:05:06Z 9996-02-29T04:05:06Z // accept: Leap day, year is Multiple of 400 0000-02-29T04:05:06Z 1200-02-29T04:05:06Z 1600-02-29T04:05:06Z 2000-02-29T04:05:06Z 3600-02-29T04:05:06Z 8000-02-29T04:05:06Z 9600-02-29T04:05:06Z // accept: Day in range for month 2008-01-30T04:05:06Z 2008-03-30T04:05:06Z 2008-04-30T04:05:06Z 2008-05-30T04:05:06Z 2008-06-30T04:05:06Z 2008-07-30T04:05:06Z 2008-08-30T04:05:06Z 2008-09-30T04:05:06Z 2008-10-30T04:05:06Z 2008-11-30T04:05:06Z 2008-12-30T04:05:06Z 2008-01-31T04:05:06Z 2008-02-31T04:05:06Z 2008-03-31T04:05:06Z 2008-05-31T04:05:06Z 2008-07-31T04:05:06Z 2008-08-31T04:05:06Z 2008-10-31T04:05:06Z 2008-12-31T04:05:06Z // accept: leap second 1972-06-30T23:59:60Z 1981-06-30T23:59:60Z 1982-06-30T23:59:60Z 1983-06-30T23:59:60Z 1985-06-30T23:59:60Z 1992-06-30T23:59:60Z 1993-06-30T23:59:60Z 1994-06-30T23:59:60Z 1997-06-30T23:59:60Z 2012-06-30T23:59:60Z 2015-06-30T23:59:60Z 1972-12-31T23:59:60Z 1973-12-31T23:59:60Z 1974-12-31T23:59:60Z 1975-12-31T23:59:60Z 1976-12-31T23:59:60Z 1977-12-31T23:59:60Z 1978-12-31T23:59:60Z 1979-12-31T23:59:60Z 1987-12-31T23:59:60Z 1989-12-31T23:59:60Z 1990-12-31T23:59:60Z 1995-12-31T23:59:60Z 1998-12-31T23:59:60Z 2005-12-31T23:59:60Z 2008-12-31T23:59:60Z 2016-12-31T23:59:60Z // REJECT: Out of range 10000-02-29T04:05:06Z 2008-00-30T04:05:06Z 2008-13-30T04:05:06Z 2008-02-30T04:05:06Z 2008-04-31T04:05:06Z 2008-06-31T04:05:06Z 2008-09-31T04:05:06Z 2008-11-31T04:05:06Z 2008-12-32T04:05:06Z 2008-12-99T04:05:06Z 2008-12-00T04:05:06Z // REJECT: Hour/min/sec out of range 2008-12-08T60:05:06Z 2008-12-08T04:60:06Z 2008-12-08T04:99:06Z 9999-12-31T59:59:61.999Z 2008-02-03T04:05:61.999Z 2008-02-03T04:05:61Z // REJECT: Negative dates not accepted -2000-02-29T04:05:06Z // REJECT: Malformed 2008-02-0304:05:06Z 20080203T040506Z 2008-02-03T04:05:06007Z 2008-02-03T04:05:06.7Z 2008-02-03T04:05:06.07Z 2008-02-03T04:05:06.0007Z 2008-02-03T04:05:06 2008-02-03T04:05:06.Z // REJECT: no leap second 1978-06-30T23:59:60Z // REJECT: assumes no future leap seconds 2024-12-31T23:59:60Z 2099-12-31T23:59:60Z 9999-12-31T23:59:60Z // REJECT: leap seconds only in UTC format 2005-12-31T23:59:60+00:00 2008-12-31T23:59:60+00:00 // REJECT: Not a leap day, not a multiple of four 2003-02-29T04:05:06Z 2037-02-29T04:05:06Z 2038-02-29T04:05:06Z 2039-02-29T04:05:06Z 9997-02-29T04:05:06Z 9998-02-29T04:05:06Z 9999-02-29T04:05:06Z // REJECT: Not a leap day, mult of 100 0100-02-29T04:05:06Z 1000-02-29T04:05:06Z 2100-02-29T04:05:06Z 2200-02-29T04:05:06Z 3500-02-29T04:05:06Z 3700-02-29T04:05:06Z
Submitted by Philip Flip Kromer (@mrflip)
1

Date - Extract & Validate - Fully tested - Format YYYY-MM-DD (dynamic parts separator / can use a different separator)

Created·2020-11-20 19:31
Flavor·ECMAScript (JavaScript)
A fully tested regex that extracts and validates date parts using named capturing groups. \ Validations: Year must be preceded by nothing or a non-digit character Year must have 4 digits Month must be between 01 and 12 Month must have 2 digits Day must be between 01 and the maximum number of days for the month (e.g. february can't have more than 29 days) Day must have 2 digits Day must be followed by nothing or a non-digit character Separator must be any single character that is not a space or an alphanumeric character Separator must be the same between each date part \ Capturing groups: | # | Name | Description | |:-:|:-------:|-------------------------------------| | 1 | year | 4 digits of the year | | 2 | sep | Date parts separator | | 3 | month | 2 digits of the month | | 4 | day | 2 digits of the date (day of month) | \ Example usage: let match = regex.exec('2020-11-22') console.log('year: %s, month: %s, day: %s', match.groups.year, match.groups.month, match.groups.day) // year: 2020, month: 11, day: 22 \ Compatibility: (updated 2020-11-20) Chrome >= 64 Edge >= 79 Firefox >= 78 IE incompatible (lookbehind assertions & named capture groups not supported) Opera >= 51 Safari incompatible (lookbehind assertions not supported) NodeJS >= 10.0.0 See regex compatibility table. \ Note: does not validate leap years (not really possible in regex)
Submitted by Elie Grenon (DrunkenPoney) <elie.grenon.1@gmail.com>

Community Library Entry

0

Regular Expression
Created·2023-05-14 02:35
Updated·2024-11-07 00:33
Flavor·PCRE2 (PHP)

/
^( # Match only leap years \d\d[2468][048] # multiple of four but not 100 |\d\d[13579][26] # ... |\d\d0[48] # ... |[02468][048]00 # multiple of four hundred |[13579][26]00 # ... )$
/
gmux
Open regex in editor

Description

This will match all four-digit years that are leap years, using the "either (multiple of four but not a multiple of 100) OR (multiple of 400)" rule.

Explained

/
^(
  \d\d[2468][048]|\d\d[13579][26]|\d\d0[48]| # multiple of four but not 100
     [02468][048]00  |[13579][26]00          # multiple of four hundred
)$
/gmx

References

Test Cases

//	accept: multiple of four ()
0004
0008
0012
0016
0020
0024
0028
0032
0036
0040
0044
0048
0052
0056
0060
0064
0068
0072
0076
0080
0084
0088
0092
0096
1560
2004
2008
2012
2016
2020
2024
2028
2032
2036
2040
2044
9996
//	accept: Multiple of 400
0000
0400
0800
1200
1600
2000
2400
2800
3200
3600
4000
4400
4800
5200
5600
6000
6400
6800
7200
7600
8000
8400
8800
9200
9600
//	REJECT: Not a multiple of four
0101
0102
0103
1537
1538
2001
2002
2003
2021
2022
2023
2025
2026
2027
2029
2030
2031
2033
2034
2035
2037
2038
2039
2041
2042
2043
9997
9998
9999
//	REJECT: Multiple of 100 but not 400
0100
0200
0300
0500
0600
0700
0900
1000
1100
1500
2100
2200
2300
2500
2600
2700
2900
3000
3100
3300
3400
3500
3700
//	REJECT: Negative dates not accepted (malformed)
-2000
-0400
-0100
-0004
//	REJECT: Out of range date per RFC 3339
10000
Submitted by Philip Flip Kromer