正则expression式匹配有效的date

我试图写一个正则expression式来validation一个date。 正则expression式需要匹配以下内容

  • 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)$ 

这匹配正确除了它仍然包括2/30/2008&2/31/2008。

有没有人有更好的build议?

编辑:我find了 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格式的有效月份。

感谢大家的帮助。

这不正确的使用正则expression式。 你最好使用

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

然后使用更高级别的语言来检查范围。

这里是Reg的所有有效date,包括闰年。 格式可接受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艾哈迈德

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

(没有尝试限制年份的值。)

我在这里登陆,因为这个问题的标题是广泛的,我正在寻找一个正则expression式,我可以使用匹配在一个特定的date格式(如OP)。 但是我发现,正如很多答案和评论已经全面强调的那样,当提取混合了质量差或非结构化的源数据的date时,有许多缺陷使得构build有效模式非常棘手。

在我探讨这些问题的过程中,我想出了一个系统,使您能够通过将四个简单的子expression式与分隔符相匹配,以及顺序中的年,月和日字段的有效范围排列在一起来构build正则expression式你需要。

这些是 :-

定界符

 [^\w\d\r\n:] 

这将匹配任何不是单词字符,数字字符,回车符,新行或冒号。 冒号必须在那里,以防止匹配时间看起来像date(请参阅我的testing数据)

您可以优化这部分模式以加速匹配,但这是检测大多数有效分隔符的良好基础。

请注意, 它将匹配一个string与混合分隔符,如2 / 12-73,实际上可能不是一个有效的date。

年份价值

 (\d{4}|\d{2}) 

这匹配一组两位或四位数字,在大多数情况下,这是可以接受的,但是如果您处理的是年份0-999或超过9999年的数据,您需要决定如何处理,因为在大多数情况下,1,3或> 4位数字是垃圾。

月份值

 (0?[1-9]|1[0-2]) 

匹配1到12之间的任何数字,带或不带前导零 – 注意:0和00不匹配。

date值

 (0?[1-9]|[12]\d|30|31) 

匹配1到31之间的任何数字,带或不带前导零 – 注意:0和00不匹配。

此expression式匹配date,月份,年份格式的date

 (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}) 

但它也会匹配一些年份,月份date。 还应该与边界运营商进行预订,以确保select整个datestring,并防止从不完整的数据中提取有效的子date,即没有20/12/194匹配的边界标签20/12/19和101/12/1974比赛作为01/12/1974

将下一个expression式的结果与上面的expression式的结果与无意义部分中的testing数据进行比较(见下文)

 \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 

在这个正则expression式中没有validation,所以一个格式良好但无效的date,如31/02/2001将匹配。 这是一个数据质量问题,正如其他人所说,你的正则expression式不应该需要validation数据。

由于您(作为开发人员)无法保证源代码数据的质量,您需要在代码中执行并处理额外的validation,所以如果您尝试在RegEx中匹配validation数据,则会变得非常混乱,支持没有非常简洁的文件。

垃圾进垃圾出。

话虽如此,如果你有混合格式的date值变化,你必须尽可能多地提取; 你可以像这样把几个expression式结合在一起;

这个(灾难性的)expression式匹配DMY和YMDdate

 (\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日那样的date是9月6日还是6月9日,那么你将无法判断。 我正在努力想出一个不会造成问题的场景,这是不好的做法,你不应该这样处理 – find数据所有者,并用治理锤击中它们。

最后,如果你想匹配没有分隔符的YYYYMMDDstring,你可以采取一些不确定性,expression式看起来像这样

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

但是请注意,它将匹配格式良好但无效的值,如20010231(二月三十一日!):)

testing数据

在这个线程中的解决scheme的实验中,我结束了一个testing数据集,其中包括各种有效和无效的date和一些棘手的情况下,你可能会或可能不希望匹配,即时间可以匹配的date和date多行。

我希望这对某人有用。

 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 

要按照以下格式控制date有效性:

YYYY / MM / DD或YYYY-MM-DD

我会build议你使用下面的正则expression式:

 (((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 | | 2019年9月31日

非匹配

2016-02-30 | | 2019/09/35

如果您只允许“/”或“ – ”分隔符,您可以自定义它。 此RegEx严格控制date的有效性,并validation28,30和31天的月份,甚至29/02月份的闰年。

试试吧,它工作得很好,并防止你的代码从很多的错误!

仅供参考:我为SQL datetime做了一个变种。 你会发现它(查找我的名字): 正则expression式来validation时间戳

反馈意见:)

听起来就像你正在为此目的过度使用正则expression式。 我会做的是使用正则expression式匹配几个date格式,然后使用单独的函数来validation如此提取的date字段的值。

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

如果你没有得到上述build议的工作,我用这个,因为它得到任何date,我通过50个链接运行这个expression式,并得到每个页面上的所有date。

 ^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; } } 

这个正则expression式使用匹配的分隔符validation01-01-2000和12-31-2099之间的date。

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

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

在使用这个来检查input之后,这些值可以用$/或者单独作为$<month>$<day>$<year> 。 (这些只是在$/访问值的语法)

没有试图检查年份,也没有与非闰年的二月二十九日相匹配。

正则expression式不是用来validation数字范围(当前面的数字恰好是2,而前面的数字恰好小于6时,这个数字必须是1到5)。 只要在正则expression式中寻找数字的位置模式。 如果你需要validation一个date的质量,把它放在一个date对象js / c#/ vb中,并且在那里编号。

我知道这不能回答你的问题,但你为什么不使用date处理程序来检查它是否是有效的date? 即使你用(?!31/0?2)(即不匹配31/2或31/02)的负向前瞻断言来修改正则expression式,你仍然会遇到非闰年接受29 02的问题和大约一个分隔符date格式。

这个问题并不容易,如果你想真正validation一个date,请检查这个论坛主题 。

有关示例或更好的方法,请在C#中查看此链接

如果您正在使用其他平台/语言,请告诉我们

如果你要坚持用正则expression式来做这件事,我会推荐如下的东西:

 ( (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日之前没有一个date。所以,我把这个'date'input并使用strtotime()。 如果答案是> = 1199167200那么我有一个对我有用的date。 如果input的东西看起来不像date,则返回-1。 如果inputnull,它会返回今天的date号码,所以你首先需要检查一个非空的条目。

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