因为这个问题的标题比较广泛,我在寻找一个正则表达式来匹配特定的日期格式(就像OP一样)时才来到这里。但是我发现,正如许多答案和评论全面强调的那样,当提取与质量较差或非结构化源数据混合的日期时,构建有效模式非常棘手。
在探索这个问题时,我想出了一个系统,使您可以通过将四个简单的子表达式按照所需顺序排列在一起来构建正则表达式,以匹配分隔符以及年、月和日字段的有效范围。
它们是:
分隔符
[^\w\d\r\n:]
这将匹配任何不是单词字符、数字字符、回车、换行或冒号的内容。冒号必须存在,以避免匹配看起来像日期的时间(参见我的测试数据)。
您可以优化此部分模式以加快匹配速度,但这是一个检测大多数有效分隔符的良好基础。
但请注意; 它将匹配具有混合分隔符的字符串,例如 2/12-73,这可能实际上不是有效日期。
年份值
(\d{4}|\d{2})
这匹配两个或四个数字组成的一组,大多数情况下这是可以接受的,但如果您处理的数据来自公元0年至999年或超过9999年,您需要决定如何处理,因为大多数情况下1、3或>4位数字年份都是无用的。
月份值
(0?[1-9]|1[0-2])
匹配1到12之间的任何数字,可以有前导0 - 注意:0和00不匹配。
日期数值
(0?[1-9]|[12]\d|30|31)
匹配1到31之间的任何数字,可以有或没有前导零 - 注意:不匹配0和00。
此表达式匹配以日期、月份和年份格式化的日期
(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})
但它也会匹配一些年月日格式。应该用边界操作符包围它以确保选择整个日期字符串并防止从未经过良好形式化处理的数据中提取有效子日期,即没有边界标签的数据,例如 20/12/194 匹配为 20/12/19,101/12/1974 匹配为 01/12/1974。
将下面无意义部分的测试数据与上面的表达式结果进行比较
\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b
此正则表达式没有进行验证,因此一个格式正确但无效的日期(例如31/02/2001)也会被匹配。这是一个数据质量问题,正如其他人所说,您的正则表达式不应该需要验证数据。
由于(作为开发者)您无法保证源数据的质量,因此您需要在代码中执行和处理额外的验证。如果您尝试在正则表达式中匹配
并且验证数据,它将变得非常混乱,并且难以在没有
非常精确文档的情况下支持。
垃圾进,垃圾出。
话虽如此,如果您确实拥有日期值不同的混合格式,并且必须尽可能地提取,则可以像下面这样组合几个表达式;
此(灾难性的)表达式匹配DMY和YMD日期。(\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b)|(\b(0?[1-9]|1[0-2])[^\w\d\r\n:](0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](\d{4}|\d{2})\b)
但是你无法确定像6/9/1973这样的日期是九月六日还是六月九日。我很难想象在何种情况下不会因此导致问题,这是不良行为,你不应该这样处理——找到数据所有者并用治理锤打击他们。
最后,如果你想匹配没有分隔符的YYYYMMDD字符串,你可以减少一些不确定性,表达式看起来像这样。
\b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b
需要注意的是,它将匹配形式良好但无效的值,例如20010231(2月31日!):)
测试数据
在尝试本主题中的解决方案时,我得到了一个包含各种有效和无效日期以及一些棘手情况的测试数据集,您可能希望或不希望匹配,例如可能与日期匹配的时间和跨多行的日期。
我希望这对某人有用。
Valid Dates in various formats
Day, month, year
2/11/73
02/11/1973
2/1/73
02/01/73
31/1/1973
02/1/1973
31.1.2011
31-1-2001
29/2/1973
29/02/1976
03/06/2010
12/6/90
month, day, year
02/24/1975
06/19/66
03.31.1991
2.29.2003
02-29-55
03-13-55
03-13-1955
12\24\1974
12\30\1974
1\31\1974
03/31/2001
01/21/2001
12/13/2001
Match both DMY and MDY
12/12/1978
6/6/78
06/6/1978
6/06/1978
using whitespace as a delimiter
13 11 2001
11 13 2001
11 13 01
13 11 01
1 1 01
1 1 2001
Year Month Day order
76/02/02
1976/02/29
1976/2/13
76/09/31
YYYYMMDD sortable format
19741213
19750101
Valid dates before Epoch
12/1/10
12/01/660
12/01/00
12/01/0000
Valid date after 2038
01/01/2039
01/01/39
Valid date beyond the year 9999
01/01/10000
Dates with leading or trailing characters
12/31/21/
31/12/1921AD
31/12/1921.10:55
12/10/2016 8:26:00.39
wfuwdf12/11/74iuhwf
fwefew13/11/1974
01/12/1974vdwdfwe
01/01/99werwer
12321301/01/99
Times that look like dates
12:13:56
13:12:01
1:12:01PM
1:12:01 AM
Dates that runs across two lines
1/12/19
74
01/12/19
74/13/1946
31/12/20
08:13
Invalid, corrupted or nonsense dates
0/1/2001
1/0/2001
00/01/2100
01/0/2001
0101/2001
01/131/2001
31/31/2001
101/12/1974
56/56/56
00/00/0000
0/0/1999
12/01/0
12/10/-100
74/2/29
12/32/45
20/12/194
2/12-73