正则表达式匹配有效日期

我尝试编写一个验证日期的正则表达式

  • M/D/YYYY
  • MM/DD/YYYY
  • 单位数字的月份可以以零开头(例如: 03/12/2008)
  • 单位日可以以前导零开头(例如: 3/02/2008)
  • 不能包括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)$

除了它仍然包括2008年2月30日和2008年2月31日之外,这个匹配是正确的。

有人有更好的建议吗?

编辑: 我在 RegExLib 上找到了 答案

^((((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 格式的所有有效月份。

谢谢大家的帮助。

287531 次浏览

听起来像是为此目的过度扩展了正则表达式。我要做的是使用正则表达式来匹配一些日期格式,然后使用一个单独的函数来验证所提取的日期字段的值。

正则表达式并不意味着验证数字范围(当前面的数字碰巧是2,而前面的数字碰巧小于6时,这个数字必须从1到5)。 只需查找正则表达式中数字的放置模式。如果需要验证某个日期的“ is”质量,请将其放入日期对象 js/c #/vb 中,并在那里询问数字。

这不是正则表达式的合适用法

[0-9]{2}/[0-9]{2}/[0-9]{4}

然后用高级语言检查范围。

我知道这不能回答你的问题,但是你为什么不用一个日期处理例行程序来检查它是否是一个有效的日期?即使使用负向前瞻断言(? !)修改 regexp,也是如此!31/0?2)(即,不匹配31/2或31/02)你仍然会有问题,接受2902在非闰年和约一个单一的分隔日期格式。

这个问题是不容易的,如果你想真正验证一个日期,检查这个 论坛帖子

有关示例或更好的方法,请在 C # 中检查 这个链接

如果您正在使用其他平台/语言,请让我们知道

如果你坚持使用正则表达式,我建议你这样做:

( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
0?2 / (0?1| <...> |28|29) )
/ (19|20)[0-9]{2}

这个 也许吧使阅读和理解成为可能。

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}$

可维护的 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}";

(当局并没有试图限制该年度的数值。)

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日在非闰年。

一种稍微不同的方法,可能对你有用,也可能没用。

我得了肝炎。

这个项目将永远不会有一个日期之前的2008年1月1日。因此,我输入“ date”并使用 strtotime ()。如果答案是 > = 1199167200,那么我有一个对我有用的日期。如果输入了看起来不像日期的内容,则返回 -1。如果输入 null,它会返回今天的日期号,因此您需要首先检查非 null 条目。

适合我的情况,也许也适合你的?

这是与包括闰年在内的所有有效日期相匹配的注册表格。接受 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})$

感谢 Asiq Ahamed

如果你没有得到上述建议的工作,我使用这个,因为它得到任何日期,我运行这个表达式通过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;
}
}

按以下格式控制日期有效期:

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 日期时间做了一个变体。你可以在那里找到它(查找我的名字) : 用于验证时间戳的正则表达式

欢迎提供意见:)

这个 regex 使用匹配的分隔符验证01-01-2000和12-31-2099之间的日期。

^(0[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])\2(19|20)\d\d$

我之所以来到这里,是因为这个问题的标题很宽泛,我正在寻找一个可以用来匹配特定日期格式(比如 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和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。这是一个数据质量问题,正如其他人所说的,正则表达式不应该需要验证数据。

因为你(作为一个开发人员)不能保证源数据的质量,你需要执行和处理额外的验证在你的代码,如果你试图匹配 还有验证数据在 RegEx 会变得非常混乱,变得很难支持没有 非常简明的文档。

垃圾进,垃圾出。

话虽如此,但如果确实存在日期值不同的混合格式,并且必须尽可能多地提取,则可以将两个表达式组合在一起,如下所示;

这个(灾难性的)表达符合 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)

但是你不会知道像1973年6月9日这样的日子是9月6日还是6月9日。我正在努力思考一个场景,在这个场景中,这样做不会在未来某个时候造成问题,这是不好的做法,您不应该像那样处理它——找到数据所有者,然后用治理锤子敲打他们。

最后,如果希望匹配不带分隔符的 YYYYMMDD 字符串,可以去掉一些不确定性,表达式如下所示

\b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b

但是请再次注意,它将匹配格式良好但无效的值,如20010231(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

/(([1-9]{1}|0[1-9]|1[0-2])\/(0[1-9]|[1-9]{1}|[12]\d|3[01])\/[12]\d{3})/

这将验证以下-

  • 单位和2位数字日,范围从1到31。例如,1,01,11,31。
  • 单位和2位数字的月份,范围从1到12。例如1,01,12。
  • 4位数的年份,比如2021年,1980年。