16

# 挑战

1. 只会验证，即比赛中，每一个可能的日期在Proleptic公历（这也适用于所有日期在1582年首次执行前）
2. 与任何无效日期都不匹配。

## 输入值

``````(?<year>[+-]?\d{4,})-(?<month>\d\d)-(?<day>\d\d)
(?<year>[+-]?\d{4,})-(?<doy>\d{3})
``````

``````(?<year>-?\d{4,})-W(?<week>\d\d)-(?<dow>\d)
``````

``````[+-]?\d{4,}-((0\d|1[0-2])-([0-2]\d|3[01]) ↩
|([0-2]\d\d|3[0-5]\d|36[0-6]) ↩
|(W([0-4]\d|5[0-3])-[1-7]))
``````

# 测试用例

## 有效测试

``````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
200000-02-29
2016-366
2000-366
0000-366
-2016-366
-2000-366
2015-081
2015-W33-1
2015-W53-7
2015-08-10
``````

## 格式无效

``````-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
``````

## 无效的日期

``````2015        # a valid ISO format, but we require day-precision
2015-08     # 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
``````

2

orlp

1
@orlp如果未指定，则选择不受限制。我故意写“ regex”或“ RX”，因此可以使用允许递归的方言（例如CFG，而不是RG）。
Crissov 2015年

orlp

Alex A.

1
Regex继承自Perl 6中的Method，因此它本身也是可执行代码的一种形式。

4

# PCRE（也是Perl），778个字节

``````/^([+-]?\d*((([02468][048]|[13579][26]|\d\d(?!00))([02468][048]|[13579][26]))|\d{4}(?!-02-29|-366))-((?!02-3|(0[469]|11)-31|000)((0[1-9]|1[012])-(0[1-9]|[12]\d|30|31)|([012]\d\d|3([0-5]\d|6[0-6])))|(W(?!00)([0-4]\d|51|52)-[1-7]))|((\+?\d*([02468][048]|[13579][26])|-\d*([02468][159]|[13579][37]))(04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99)|(\+?\d*([02468][159]|[13579][37])|-\d*([02468][26]|[13579][048]))(05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95)|(\+?\d*([02468][26]|[13579][048])|-\d*([02468][37]|[13579][159]))(01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96)|\+?\d*(([02468][37]|[13579][159])(03|14|20|25|31|36|42|53|59|64|70|76|81|87|92|[049]8))|-\d*(([02468][048]|[13579][26])([059]2|08|13|19|24|30|36|41|47|58|64|69|75|80|86|97)))-W53-[1-7])\$/
``````

``````/^  # Start of pattern (no leading space)
(
# YEAR
# Optional sign and digits if more than 4 in year
[+-]?\d*(
# Years 00??, 04??, 08?? ... 92??, 96?? OR dd not followed by 00
# followed by 00, 04, 08 ... 92, 96 OR
(([02468][048]|[13579][26]|\d\d(?!00))([02468][048]|[13579][26])) |
# any year not followed by 29 February or day 366
\d{4}(?!-02-29|-366)
# dash
) -
# MONTH AND DAY, or DAY OF YEAR, or WEEK OF YEAR AND DAY if less than 53 weeks
(
# Not (30 or 31 February OR 31 April, June, September or December OR day 0)
(?!02-3|(0[469]|11)-31|000)
(
# Month         dash         day         OR
(0[1-9]|1[012]) - (0[1-9]|[12]\d|30|31) |
# 001-299 OR 300-359 OR 360-366
([012]\d\d | 3([0-5]\d | 6[0-6]))
# OR
) |
(
# W    01-52    dash    1-7
W(?!00)([0-4]\d|51|52)-[1-7]
)
# OR
) |
# WEEK OF YEAR AND DAY only if week is 53
(
# Optional plus and extra year digits
\+?\d*(
# Years +0303 - +9998
([02468][37]|[13579][159])(03|14|20|25|31|36|42|53|59|64|70|76|81|87|92|[049]8)
) |
# Minus and extra year digits
-\d*(
# Years -0002 - -9697
([02468][048]|[13579][26])([059]2|08|13|19|24|30|36|41|47|58|64|69|75|80|86|97)
) |
# Years +0004 - +9699, -0104 - -9799
(\+?\d*([02468][048]|[13579][26])|-\d*([02468][159]|[13579][37]))
(04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99) |
# Years +0105 - +9795, -0205 - -9895
(\+?\d*([02468][159]|[13579][37])|-\d*([02468][26]|[13579][048]))
(05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95) |
# Years +0201 - +9896, -0301 - -9996
(\+?\d*([02468][26]|[13579][048])|-\d*([02468][37]|[13579][159]))
(01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96)
# dash W 53 dash 1-7
)-W53-[1-7]
# End of pattern (no trailing space)
)\$/x
``````

Crissov

1
@Crissov每个`(?!…)`表达式只保存几个字节。我通过将每年的正/负周模式/周中的日模式组合为三个，减少了很多字节。最后一个彼此不对应。因此，我得到了8个长子图案，最后降至5个。此外，由于`|20|25|`它的长度与`|2[05]|`我选择的可读性相同。
CJ Dennis'3

Crissov

Crissov

`W(?!00)([0-4]\d|51|52)-[1-7]`必须等于`W(?!00)([0-4]\d|5[0-2])-[1-7]`。这会增加一个字符的长度。779

9

# PCRE： 603940947949 956个字节

``````^\s*[+-]?(\d{4,10}-((00[1-9]|0[1-9]\d|[12]\d\d|3[0-5]\d|36[0-5])|(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|W(0[1-9]|[1-4]\d|5[0-2])-[1-7]))|((\d{2,8}([13579][26]|[2468][048]|0[48])|(\d{0,6}([13579][26]|[02468][048])00))-(366|02-29))|(\+?\d{0,6}(([02468][048]|[13579][26])([26]0|71|[38]2|[49]3|[05]4|15|[27]6|37|[48]8|[09]9)|([02468][159]|[13579][37])(50|[16]1|[27]2|33|[48]4|[09]5|[15]6|67|[27]8|[38]9)|([02468][26]|[13579][048])([48]0|[09]1|[15]2|63|[27]4|[38]5|[49]6|[05]7|[16]8|29)|([02468][37]|[13579][159])([27]0|[38]1|[49]2|[05]3|[16]4|25|[37]6|87|[049]8|[5]9))|-\d{0,6}(([02468][048]|[13579][26])(0[28]|1[39]|24|3[06]|4[17]|5[28]|6[49]|75|8[06]|9[27])|([02468][159]|[13579][37])(0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39])|([02468][26]|[13579][048])(0[51]|16|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95)|([02468][37]|[13579][159])(0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16])))-W53-[1-7]\s*\$
``````

## 被4除数

4的倍数以简单的模式重复：

• 00，04，08，12，16，
20，24，28，32，36，
40，44，48，52，56，
60，64，68，72，76，
80，84，88，92，96， …

``````(?<divisible-by-four>[13579][26]|[02468][048])
(?<not-divisible-by-four>[13579][048]|[02468][26]|\d[13579])
``````

## 年份

``````(?<leap-year>[+-]?(\d{2,8}([13579][26]|[2468][048]|0[48])|(\d{0,6}([13579][26]|[02468][048])00))
(?<year>[+-]?\d{4,10})
``````

``````(?<leap-year>([+-]?(\d\d([13579][26]|[2468][048]|0[48])|(([13579][26]|[02468][048])00)))|([+-](\d{3,8}([13579][26]|[2468][048]|0[48])|(\d{1,6}([13579][26]|[02468][048])00))))
(?<year>([+-]?\d{4})|([+-]\d{5,10}))
``````

## 一年中的一天

``````(?<ordinal-day>-(00[1-9]|0[1-9]\d|[12]\d\d|3[0-5]\d|36[0-5]))
(?<ordinal-leap-day>-366)
``````

## 一年中的某天

``````(?<month-day>-(0[1-9]|1[0-2])-(0[1-9]|1\d|2[0-8]))
(?<short-month-day>-(0[13-9]|1[0-2])-(29|30))
(?<long-month-day>-(0[13578]|1[02])-31)
(?<month-leap-day>-02-29)
``````

`00`早期版本未涵盖月份和日期。

## 一年中的星期几

``````(?<week-day>-W(0[1-9]|[1-4]\d|5[0-2])-[1-7])
``````

• 004、009、015、020、026、032、037、043、048、054、060、065、071、076、082、088、093、099，
• 105、111、116、122、128、133、139、144、150、156、161、167、172、178、184、189、195，
• 201、207、212、218、224、229、235、240、246、252、257、263、268、274、280、285、291、296，
• 303、308、314、320、325、331、336、342、348、353、359、364、370、376、381、387、392、398。

1. `04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99`
2. `05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95`
3. `01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96`
4. `03|08|14|20|25|31|36|42|48|53|59|64|70|76|81|87|92|98`

• 按第一位分组。
1. `0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39]`
2. `05|1[16]|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95`
3. `0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16]`
4. `0[38]|14|2[05]|3[16]|4[28]|5[39]|64|7[06]|8[17]|9[28]`
• 按第二位分组。
1. `[26]0|71|[38]2|[49]3|[05]4|15|[27]6|37|[48]8|[09]9`
2. `50|[16]1|[27]2|33|[48]4|[09]5|[15]6|67|[27]8|[38]9`
3. `[48]0|[09]1|[15]2|63|[27]4|[38]5|[49]6|[05]7|[16]8|29`
4. `[27]0|[38]1|[49]2|[05]3|[16]4|25|[37]6|87|[049]8|[5]9`

• 1世纪： `[02468][048]|[13579][26]`
• 2世纪： `[02468][159]|[13579][37]`
• 3世纪： `[02468][26]|[13579][048]`
• 4世纪： `[02468][37]|[13579][159]`

1. `02|08|13|19|24|30|36|41|47|52|58|64|69|75|80|86|92|97`
2. `04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99`
3. `05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95`
4. `01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96`

1. `0[28]|1[39]|24|3[06]|4[17]|5[28]|6[49]|75|8[06]|9[27]`
2. `0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39]`
3. `0[51]|16|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95`
4. `0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16]`

## 全部放在一起

### 任何一年

``````[+-]?\d{4,10}-((00[1-9]|0[1-9]\d|[12]\d\d|3[0-5]\d|36[0-5])|(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|W(0[1-9]|[1-4]\d|5[0-2])-[1-7])
``````

### 日年增加

``````[+-]?(\d{2,8}([13579][26]|[2468][048]|0[48])|(\d{0,6}([13579][26]|[02468][048])00))-(366|02-29)
``````

### ap周年增加

``````+?\d{0,6}(([02468][048]|[13579][26])([26]0|71|[38]2|[49]3|[05]4|15|[27]6|37|[48]8|[09]9)|([02468][159]|[13579][37])(50|[16]1|[27]2|33|[48]4|[09]5|[15]6|67|[27]8|[38]9)|([02468][26]|[13579][048])([48]0|[09]1|[15]2|63|[27]4|[38]5|[49]6|[05]7|[16]8|29)|([02468][37]|[13579][159])([27]0|[38]1|[49]2|[05]3|[16]4|25|[37]6|87|[049]8|[5]9))-W53-[1-7]
-\d{0,6}(([02468][048]|[13579][26])(0[28]|1[39]|24|3[06]|4[17]|5[28]|6[49]|75|8[06]|9[27])|([02468][159]|[13579][37])(0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39])|([02468][26]|[13579][048])(0[51]|16|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95)|([02468][37]|[13579][159])(0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16]))-W53-[1-7]
``````

CJ丹尼斯

@CJDennis是的，我现在将添加两个字符。
Crissov

Crissov