In ValiDate ISO 8601 by RX, the challenge was to use only standard regular expressions to validate standard date formats and values (the former is a common job for RX, the latter was unusual). The winning answer used 778 bytes. This challenge is to beat that using any language of your choosing, but without special date functions or classes.
Find the shortest code that
- validates every possible date in the Proleptic Gregorian calendar (which also applies to all dates before its first adoption in 1582),
- does not match any invalid date and
- does not use any predefined functions, methods, classes, modules or similar for handling dates (and times), i.e. relies on string and numeric operations.
Output is truthy or falsey. It’s not necessary to output or convert the date.
Input is a single string in any of 3 expanded ISO 8601 date formats – no times.
The first two are
±YYYY-MM-DD (year, month, day) and
±YYYY-DDD (year, day). Both need special-casing for the leap day. They are naively matched separately by these extended RXs:
The third input format is
±YYYY-wWW-D (year, week, day). It is the complicated one because of the complex leap week pattern.
A leap year in the Proleptic Gregorian calendar contains the leap day
…-02-29 and thus it is 366 days long, hence
…-366 exists. This happens in any year whose (possibly negative) ordinal number is divisible by 4, but not by 100 unless it’s also divisible by 400.
Year zero exists in this calendar and it is a leap year.
A long year in the ISO week calendar contains a 53rd week
…-W53-…, which one could term a “leap week”. This happens in all years where 1 January is a Thursday and additionally in all leap years where it’s a Wednesday.
2001-01-01 are Mondays. It turns out to occur every 5 or 6 years usually, in a seemingly irregular pattern.
A year has at least 4 digits. Years with more than 10 digits do not have to be supported, because that’s close enough to the age of the universe (ca. 14 billion years). The leading plus sign is optional, although the actual standard suggests it should be required for years with more than 4 digits.
Partial or truncated dates, i.e. with less than day-precision, must not be accepted.
- are required in all cases.
(These preconditions make it possible for leading
+ to be always optional.)
This is code-golf. The shortest code in bytes wins. Earlier answer wins a tie.
2015-08-10 2015-10-08 12015-08-10 -2015-08-10 +2015-08-10 0015-08-10 1582-10-10 2015-02-28 2016-02-29 2000-02-29 0000-02-29 -2000-02-29 -2016-02-29 +2016-02-29 200000-02-29 -200000-02-29 +200000-02-29 2016-366 2000-366 0000-366 -2000-366 -2016-366 +2016-366 2015-081 2015-W33-1 2015-W53-7 +2015-W53-7 +2015-W33-1 -2015-W33-1 2015-08-10
The last one is optionally valid, i.e. leading and trailing spaces in input strings may be trimmed.
-0000-08-10 # that's an arbitrary decision 15-08-10 # year is at least 4 digits long 2015-8-10 # month (and day) is exactly two digits long, i.e. leading zero is required 015-08-10 # year is at least 4 digits long 20150810 # though a valid ISO format, we require separators; could also be interpreted as a 8-digit year 2015 08 10 # separator must be hyphen-minus 2015.08.10 # separator must be hyphen-minus 2015–08–10 # separator must be hyphen-minus 2015-0810 201508-10 # could be October in the year 201508 2015 - 08 - 10 # no internal spaces allowed 2015-w33-1 # letter ‘W’ must be uppercase 2015W33-1 # it would be unambiguous to omit the separator in front of a letter, but not in the standard 2015W331 # though a valid ISO format we require separators 2015-W331 2015-W33 # a valid ISO date, but we require day-precision 2015W33 # though a valid ISO format we require separators and day-precision 2015-08 # a valid ISO format, but we require day-precision 201508 # a valid but ambiguous ISO format 2015 # a valid ISO format, but we require day-precision
2015-00-10 # month range is 1–12 2015-13-10 # month range is 1–12 2015-08-00 # day range is 1–28 through 31 2015-08-32 # max. day range is 1–31 2015-04-31 # day range for April is 1–30 2015-02-30 # day range for February is 1–28 or 29 2015-02-29 # day range for common February is 1–28 2100-02-29 # most century years are non-leap -2100-02-29 # most century years are non-leap 2015-000 # day range is 1–365 or 366 2015-366 # day range is 1–365 in common years 2016-367 # day range is 1–366 in leap years 2100-366 # most century years are non-leap -2100-366 # most century years are non-leap 2015-W00-1 # week range is 1–52 or 53 2015-W54-1 # week range is 1–53 in long years 2016-W53-1 # week range is 1–52 in short years 2015-W33-0 # day range is 1–7 2015-W33-8 # day range is 1–7