题
我正在尝试编写一个验证日期的正则表达式。正则表达式需要匹配以下内容
- 月/日/年
- 月/日/年
- 单位数月份可以以前导零开头(例如:2008 年 3 月 12 日)
- 单位数日期可以以前导零开头(例如:2008 年 3 月 2 日)
- 不能包含 2 月 30 日或 2 月 31 日(例如:2/31/2008)
到目前为止我已经
^(([1-9]|1[012])[-/.]([1-9]|[12][0-9]|3[01])[-/.](19|20)\d\d)|((1[012]|0[1-9])(3[01]|2\d|1\d|0[1-9])(19|20)\d\d)|((1[012]|0[1-9])[-/.](3[01]|2\d|1\d|0[1-9])[-/.](19|20)\d\d)$
这正确匹配,但它仍然包括 2/30/2008 和 2/31/2008。
有人有更好的建议吗?
编辑: 我发现 答案 在正则表达式库上
^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$
它匹配遵循 MM/DD/YYYY 格式的所有有效月份。
感谢大家的帮助。
解决方案
这不是正则表达式的正确使用。你最好使用
[0-9]{2}/[0-9]{2}/[0-9]{4}
然后检查高级语言的范围。
其他提示
这是匹配所有有效日期(包括闰年)的 Reg ex。接受的格式 mm/dd/yyyy 或 mm-dd-yyyy 或 mm.dd.yyyy 格式
^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$
礼貌 阿西克·艾哈迈德
我来到这里是因为这个问题的标题很广泛,我正在寻找一个可以用来匹配特定日期格式(如OP)的正则表达式。但后来我发现,正如许多答案和评论所全面强调的那样,在提取与质量差或非结构化源数据混合的日期时,存在许多陷阱,使得构建有效模式变得非常棘手。
在我对这些问题的探索中,我提出了一个系统,该系统使您能够通过将四个与分隔符匹配的更简单的子表达式以及顺序中年、月和日字段的有效范围排列在一起来构建正则表达式你需要。
这些都是 :-
定界符
[^\w\d\r\n:]
这将匹配除单词字符、数字字符、回车符、换行符或冒号之外的任何内容。冒号必须存在,以防止匹配看起来像日期的时间(请参阅我的测试数据)
您可以优化模式的这一部分以加快匹配速度,但这是检测大多数有效分隔符的良好基础。
但请注意;它将匹配带有混合分隔符的字符串,例如 2/12-73,但实际上可能不是有效日期。
年份值
(\d{4}|\d{2})
这匹配一组两个或 4 位数字,在大多数情况下这是可以接受的,但如果您处理的是 0-999 年或 9999 年之后的数据,您需要决定如何处理它,因为在大多数情况下 1、3或 >4 位数字的年份是垃圾。
月份值
(0?[1-9]|1[0-2])
匹配 1 到 12 之间的任何数字(带或不带前导零) - 注意: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 这样的日期是 9 月 6 日还是 6 月 9 日。我正在努力想象一种场景,在这种情况下,这不会在某个地方引起问题,这是不好的做法,你不应该这样处理它 - 找到数据所有者并用治理锤子打击他们。
最后,如果您想匹配没有分隔符的 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
可维护的 Perl 5.10 版本
/
(?:
(?<month> (?&mon_29)) [\/] (?<day>(?&day_29))
| (?<month> (?&mon_30)) [\/] (?<day>(?&day_30))
| (?<month> (?&mon_31)) [\/] (?<day>(?&day_31))
)
[\/]
(?<year> [0-9]{4})
(?(DEFINE)
(?<mon_29> 0?2 )
(?<mon_30> 0?[469] | (11) )
(?<mon_31> 0?[13578] | 1[02] )
(?<day_29> 0?[1-9] | [1-2]?[0-9] )
(?<day_30> 0?[1-9] | [1-2]?[0-9] | 30 )
(?<day_31> 0?[1-9] | [1-2]?[0-9] | 3[01] )
)
/x
在此版本中,您可以按名称检索元素。
say "Month=$+{month} Day=$+{day} Year=$+{year}";
(没有尝试限制该年的值。)
要按照以下格式控制日期有效性:
YYYY/MM/DD 或 YYYY-MM-DD
我建议您使用以下正则表达式:
(((19|20)([2468][048]|[13579][26]|0[48])|2000)[/-]02[/-]29|((19|20)[0-9]{2}[/-](0[4678]|1[02])[/-](0[1-9]|[12][0-9]|30)|(19|20)[0-9]{2}[/-](0[1359]|11)[/-](0[1-9]|[12][0-9]|3[01])|(19|20)[0-9]{2}[/-]02[/-](0[1-9]|1[0-9]|2[0-8])))
火柴
2016-02-29 | 2012-04-30 | 2019/09/31
不匹配
2016-02-30 | 2012-04-31 | 2019/09/35
如果您只想允许“/”或“-”分隔符,您可以自定义它。该正则表达式严格控制日期的有效性,并验证 28,30 和 31 天的月份,甚至闰年 29/02 月份。
尝试一下,它工作得很好,可以防止你的代码出现很多错误!
供参考 :我为 SQL 日期时间做了一个变体。你会在那里找到它(寻找我的名字): 用于验证时间戳的正则表达式
欢迎反馈:)
听起来您为此目的过度扩展了正则表达式。我要做的是使用正则表达式来匹配一些日期格式,然后使用单独的函数来验证如此提取的日期字段的值。
Perl 扩展版本
注意使用 /x
修饰符。
/^(
(
( # 31 day months
(0[13578])
| ([13578])
| (1[02])
)
[\/]
(
([1-9])
| ([0-2][0-9])
| (3[01])
)
)
| (
( # 30 day months
(0[469])
| ([469])
| (11)
)
[\/]
(
([1-9])
| ([0-2][0-9])
| (30)
)
)
| ( # 29 day month (Feb)
(2|02)
[\/]
(
([1-9])
| ([0-2][0-9])
)
)
)
[\/]
# year
\d{4}$
| ^\d{4}$ # year only
/x
原来的
^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$
如果您没有使上述建议发挥作用,我会使用它,因为它可以获取我通过 50 个链接运行此表达式的任何日期,并且它可以获取每个页面上的所有日期。
^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$
var dtRegex = new RegExp(/[1-9\-]{4}[0-9\-]{2}[0-9\-]{2}/);
if(dtRegex.test(date) == true){
var evalDate = date.split('-');
if(evalDate[0] != '0000' && evalDate[1] != '00' && evalDate[2] != '00'){
return true;
}
}
此正则表达式使用匹配的分隔符验证 01-01-2000 和 12-31-2099 之间的日期。
^(0[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])\2(19|20)\d\d$
正则表达式并不是为了验证数字范围(当它前面的数字恰好是 2 并且前面的数字恰好小于 6 时,该数字必须是从 1 到 5)。只需寻找正则表达式中数字的放置模式即可。如果您需要验证日期的质量,请将其放入日期对象 js/c#/vb 中,然后查询那里的数字。
Perl 6 版本
rx{
^
$<month> = (\d ** 1..2)
{ $<month> <= 12 or fail }
'/'
$<day> = (\d ** 1..2)
{
given( +$<month> ){
when 1|3|5|7|8|10|12 {
$<day> <= 31 or fail
}
when 4|6|9|11 {
$<day> <= 30 or fail
}
when 2 {
$<day> <= 29 or fail
}
default { fail }
}
}
'/'
$<year> = (\d ** 4)
$
}
使用它来检查输入后,值可以在 $/
或单独作为 $<month>
, $<day>
, $<year>
. 。(这些只是访问值的语法 $/
)
未尝试检查年份,或者检查年份是否与非闰年的 2 月 29 日不匹配。
如果您坚持使用正则表达式来执行此操作,我建议您使用以下内容:
( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
0?2 / (0?1| <...> |28|29) )
/ (19|20)[0-9]{2}
这 可能 使阅读和理解成为可能。
稍微不同的方法可能对您有用,也可能没有用。
我在php.
与此相关的项目绝不会在 2008 年 1 月 1 日之前确定日期。因此,我获取输入的“日期”并使用 strtotime()。如果答案 >= 1199167200 那么我就有了一个对我有用的日期。如果输入的内容看起来不像日期,则返回 -1。如果输入 null,它会返回今天的日期数字,因此您需要首先检查非空条目。
适合我的情况,也许也适合你的情况?