使用正则表达式验证日期格式dd/mm/YYYY、dd-mm-YYYY、dd.mm.YYYY、dd mmm YYYY、dd-mmm-YYYY、dd/mmm/YYYY、dd.mmm.YYYY,并支持闰年。

260

我需要使用正则表达式验证日期字符串的格式为dd/mm/yyyy

这个正则表达式可以验证dd/mm/yyyy格式,但无法验证无效的日期,比如31/02/4500

^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$

有哪些有效的正则表达式可以验证带闰年支持的dd/mm/yyyy格式?


3
我认为,如果您设置一个准确的期望值,可能会有所帮助,因为此正则表达式实际上并没有正确验证闰年;例如,2013年没有2月29日,但是此正则表达式声称这是有效的:http://regexr.com?346fp - TML
11
为什么要用正则表达式?还有更简单(且更准确)的方法... - Dan Puzey
3
正则表达式用于匹配模式,而不是检查数字值。使用正则表达式找到一个可能的字符串,然后在您所使用的托管语言(如PHP等)中检查其数值。 - Andy Lester
3
此答案已添加到 Stack Overflow 正则表达式 FAQ 中的“常见验证任务”下。 - aliteralmind
4
你错了。正则表达式是验证的最佳工具,因为HTML5输入有一个名为“pattern”的属性,它接受一个正则表达式,浏览器会自动根据正则表达式进行验证,而无需使用任何JavaScript。只需在pattern属性中设置一个正则表达式即可。 - awe
显示剩余6条评论
28个回答

440
您贴的正则表达式未正确验证闰年,但该帖子中有一个可以验证闰年的正则表达式。我对其进行了修改,使其支持dd/mm/yyyydd-mm-yyyydd.mm.yyyy格式。 ^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$ 我在Arun提供的链接和这里测试过,看起来可以工作。
2019年2月14日的编辑:我删除了正则表达式中的逗号,这允许了像29-0,-11这样的日期。

我想在日期验证之前添加2个英文字母,以便我的正确输出为“AB 12/12/1999”,有人可以帮我编写正则表达式吗?我的当前正则表达式是: /a-zA-Z - /.- /.\d\d/但它允许年份超过4个字符。 - Salman Nausher
1
它几乎完美地运行!如果你想要日期以dd/mm/yyyy的格式显示,就需要将"d{2}"替换为"d{4}"。否则,它将适用于dd/mm/yy的格式。 - ladytoky0

304

根据我的需求,我已经扩展了@Ofir Luzon提供的正则表达式,以适应格式为dd-mmm-YYYY、dd/mmm/YYYY、dd.mmm.YYYY。有相同需求的其他人可以参考此内容。

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

在这里测试了一些测试用例 https://regexr.com/39tr1

要更好地理解这个正则表达式,请参考以下图片:

输入图像描述

编辑

扩展为yyyy/mm/dd、yyyy-mm-dd或yyyy.mm.dd 一些测试用例https://regex101.com/r/3TZfyU/1

^(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(\/|-|\.)(?:0?[13578]|1[02])\1(?:31))|(?:(\/|-|\.)(?:0?[13-9]|1[0-2])\2(?:29|30)))$|
^(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\/|-|\.)0?2\3(?:29)$|
^(?:(?:1[6-9]|[2-9]\d)?\d{2})(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:0?[1-9]|1\d|2[0-8])$

84

注意:

您的正则表达式无法识别“是4的倍数但不是100的倍数,或是400的倍数”的年份。通过这个测试的年份不是闰年。例如:1900、2100、2200、2300、2500等。换句话说,它将所有格式为\d\d00的年份归为同一类闰年,这是不正确的。- MuchToLearn

因此它只能正常工作在[1901-2099]范围内。(轻松了)

dd/MM/yyyy:

检查是否是闰年。 有效年份为1900到9999年。仅支持格式为dd/MM/yyyy的日期。

(^(((0[1-9]|1[0-9]|2[0-8])[\/](0[1-9]|1[012]))|((29|30|31)[\/](0[13578]|1[02]))|((29|30)[\/](0[4,6,9]|11)))[\/](19|[2-9][0-9])\d\d$)|(^29[\/]02[\/](19|[2-9][0-9])(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)$)

为了加快速度,您可以使组成为非分组。http://regexr.com/3esom^(?:(?:(?:(?:0[1-9]|1[0-9]|2[0-8])/)|(?:(?:29|30|31)/)|(?:(?:29|30)/))/\d\d)|(?:29[/]02/(?: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))$ - Matt Vukomanovic

37

2
这个正则表达式无法验证像 30-02-2001 这样的日期... 不管怎样,谢谢回答 :) - Nalaka526
@Nalaka526,这个正则表达式对于基本的日期验证已经足够好了。如果你想要可靠地检查一个日期的有效性,你总是可以解析它。 - Peter Chaula
我猜 [- /.] 应该是指 [- /\.],其中点是实际字符,而不是“任何字符”。 - Random
我使用Dart&Flutter尝试了这个功能,当我使用datetimepicker选择日期并将其打印时,发现它可以正常工作。非常感谢您,先生。 - Abdullah T

17

对于那些看到这些内容感到完全困惑的人,这里是我脚本的一部分摘录。不幸的是,它只匹配日期时间输入中的有效数字,并且31日二月会被标记为有效,但正如许多人所说,正则表达式并不是执行此测试的最佳工具。

要匹配格式为'yyyy-MM-dd hh:mm'的日期(或以任何顺序),

var dateerrors = false;
var yearReg = '(201[4-9]|202[0-9])';            ///< Allows a number between 2014 and 2029
var monthReg = '(0[1-9]|1[0-2])';               ///< Allows a number between 00 and 12
var dayReg = '(0[1-9]|1[0-9]|2[0-9]|3[0-1])';   ///< Allows a number between 00 and 31
var hourReg = '([0-1][0-9]|2[0-3])';            ///< Allows a number between 00 and 24
var minReg = '([0-5][0-9])';                    ///< Allows a number between 00 and 59
var reg = new RegExp('^' + yearReg + '-' + monthReg + '-' + dayReg + ' ' + hourReg + ':' + minReg + '$', 'g');
$('input').filter(function () {return this.id.match(/myhtml_element_with_id_\d+_datetime/);}).each(function (e) {
    if (e > 0) {
        // Don't test the first input. This will use the default
        var val = $(this).val();
        if (val && !val.trim().match(reg)) {
            dateerrors = true;
            return false;
        }
    }
});
if (dateerrors) {
    alert('You must enter a validate date in the format "yyyy-mm-dd HH:MM", e.g. 2019-12-31 19:30');
    return false;
}

上面的脚本首先创建了一个正则表达式对象。然后找到所有id与某个模式匹配的输入,并循环遍历这些输入。我不会测试找到的第一个输入(if (e > 0))。
一点解释:
var reg = new RegExp('^' + yearReg + '-' + monthReg + '-' + dayReg + ' ' + hourReg + ':' + minReg + '$', 'g');

^表示匹配开始,而$表示匹配结束。

return this.id.match(/myhtml_element_with_id_\d+_datetime/);

\d+ 意味着匹配一个或多个整数,所以 myhtml_element_with_id_56_datetimemyhtml_element_with_id_2_datetime 都会匹配成功,但是 myhtml_element_with_id_5a_datetime 不会匹配成功。


14

我怀疑在不知道用户所处的区域何时从儒略历转换为公历之前,以下内容应该是尽可能准确的。

它接受“-”、“/”或没有分隔符作为年、月和日之间的分隔符,不论顺序如何。

MMddyyyy:

^(((0[13-9]|1[012])[-/]?(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])[-/]?31|02[-/]?(0[1-9]|1[0-9]|2[0-8]))[-/]?[0-9]{4}|02[-/]?29[-/]?([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00))$

ddMMyyyy:

^(((0[1-9]|[12][0-9]|30)[-/]?(0[13-9]|1[012])|31[-/]?(0[13578]|1[02])|(0[1-9]|1[0-9]|2[0-8])[-/]?02)[-/]?[0-9]{4}|29[-/]?02[-/]?([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00))$

yyyyMMdd:

^([0-9]{4}[-/]?((0[13-9]|1[012])[-/]?(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])[-/]?31|02[-/]?(0[1-9]|1[0-9]|2[0-8]))|([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00)[-/]?02[-/]?29)$

除了顺序外,这些都符合每四年闰年的儒略日历,直到1700年,格列高利历不再与儒略日历相同。它有两个问题:

  1. 它接受0000年,在许多但不是全部标准中不存在。请注意,ISO 8601接受0000年(相当于公元前1年)。
  2. 它不跳过在格列高利历开始使用时丢失的10-13天。这因地区而异。例如,罗马天主教会跳过了10天,即1582年10月5日至10月14日,但希腊(最后一个转换的国家)跳过了1923年2月16日至28日的13天,并考虑到1700年、1800年和1900年的闰年。

已测试了自0001年至9999年的Java日历实现,唯一的差异是上述情况下的1582年10天。


YYYYMMDD正则表达式错误地匹配了[01|02|03|05|06|07|09|10|11|13|14|15]00-02-29。修正后的正则表达式:^([0-9]{4}[-/]?((0[13-9]|1[012])[-/]?(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])[-/]?31|02[-/]?(0[1-9]|1[0-9]|2[0-8]))|([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048])00)[-/]?02[-/]?29)$。使用Python测试:https://repl.it/repls/DependentBestChapters - apnkpr
请注意,沙特阿拉伯最近(一年前)从伊斯兰历转换,但希腊仍然是最后一个从儒略历转换为公历的国家。 - Raymo111

9
year  = ((20[012]\d|19\d\d)|(1\d|2[0123])) 
month = ((0[0-9])|(1[012]))
day   = ((0[1-9])|([12][0-9])|(3[01]))

year-month-day = (((20[012]\d|19\d\d)|(1\d|2[0123]))-((0[0-9])|(1[012]))-((0[1-9])|([12][0-9])|(3[01])))
day-month-year = (((0[1-9])|([12][0-9])|(3[01]))-((0[0-9])|(1[012]))-((20[012]\d|19\d\d)|(1\d|2[0123])))
year/month/day = (((20[012]\d|19\d\d)|(1\d|2[0123]))\/((0[0-9])|(1[012]))\/((0[1-9])|([12][0-9])|(3[01])))
month/day/year = (((0[0-9])|(1[012]))\/((0[1-9])|([12][0-9])|(3[01]))\/((20[012]\d|19\d\d)|(1\d|2[0123])))
day/month/year = (((0[1-9])|([12][0-9])|(3[01]))\/((0[0-9])|(1[012]))\/((20[012]\d|19\d\d)|(1\d|2[0123])))
day.month.year = (((0[1-9])|([12][0-9])|(3[01]))\.((0[0-9])|(1[012]))\.((20[012]\d|19\d\d)|(1\d|2[0123])))
year.month.day = (((20[012]\d|19\d\d)|(1\d|2[0123]))\.((0[0-9])|(1[012]))\.((0[1-9])|([12][0-9])|(3[01])))


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

它的工作是为了

yyyy-mm-dd
dd-mm-yyyy
yyyy/mm/dd
mm/dd/yyyy
dd/mm/yyyy
dd.mm.yyyy
yyyy.mm.dd

yy-mm-dd
dd-mm-yy
yyyy/mm/dd
mm/dd/yy
dd/mm/yy
dd.mm.yy
yy.mm.dd

但不适用于日期为 d 或月份为 m 的情况,例如 d.m.yyyy

所有示例 - 点击此处


1
请问如何实现 day = d 或 month = m 的情况? - RoyRao
嗨@RoyRao,我遇到了同样的问题。这就是为什么我写了不起作用的东西。我通过在正则表达式之前进行替换来解决了这个问题。 如果你找到了更优雅的解决方案,请告诉我。 - Алексей Коробов

8

以下是另一种正则表达式,用于匹配以下任意日期格式并允许省略前导零:

正则表达式: ^[0-3]?[0-9].[0-3]?[0-9].(?:[0-9]{2})?[0-9]{2}$

匹配结果:

1/1/11 或 1.1.11 或 1-1-11 : true 01/01/11 或 01.01.11 或 01-01-11 : true 01/01/2011 或 01.01.2011 或 01-01-2011 : true 01/1/2011 或 01.1.2011 或 01-1-2011 : true 1/11/2011 或 1.11.2011 或 1-11-2011 : true 1/11/11 或 1.11.11 或 1-11-11 : true 11/1/11 或 11.1.11 或 11-1-11 : true

正则表达式可视化

Debuggex 演示


4
如果你的输入是 13/13/2000,且“匹配”结果为“true”,则此方法将不起作用。 - Andrei Krasutski
3
39/39/39并不是一个日期。请停止为这个答案投票。 - Warren Sergent
32.13.1993 是有效的。 - Ľuboš Csonka

7

这里我写了一个针对日期格式 dd/mm/yyyy 的正则表达式,分隔符可以是其中的任意一个:-.,/ 年份范围为 0000-9999

它处理了闰年,并设计用于支持向前查找、捕获组和反向引用的正则表达式语言,而不是d/m/yyyy 这样的格式。如果需要,可以将其他分隔符添加到[-.,/]中。

^(?=\d{2}([-.,\/])\d{2}\1\d{4}$)(?:0[1-9]|1\d|[2][0-8]|29(?!.02.(?!(?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)\d{2}(?:[02468][048]|[13579][26])))|30(?!.02)|31(?=.(?:0[13578]|10|12))).(?:0[1-9]|1[012]).\d{4}$

在regex101上测试;作为Java字符串:

"^(?=\\d{2}([-.,\\/])\\d{2}\\1\\d{4}$)(?:0[1-9]|1\\d|[2][0-8]|29(?!.02.(?!(?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)\\d{2}(?:[02468][048]|[13579][26])))|30(?!.02)|31(?=.(?:0[13578]|10|12))).(?:0[1-9]|1[012]).\\d{4}$"

解释:

(?x) # modifier x: free spacing mode (for comments)
     # verify date dd/mm/yyyy; possible separators: -.,/
     # valid year range: 0000-9999

^    # start anchor

# precheck xx-xx-xxxx,... add new separators here
(?=\d{2}([-.,\/])\d{2}\1\d{4}$)

(?:  # day-check: non caturing group

  # days 01-28
  0[1-9]|1\d|[2][0-8]| 

  # february 29d check for leap year: all 4y / 00 years: only each 400
  # 0400,0800,1200,1600,2000,...
  29
  (?!.02. # not if feb: if not ...
    (?!
      # 00 years: exclude !0 %400 years
      (?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)

      # 00,04,08,12,... 
      \d{2}(?:[02468][048]|[13579][26])
    )
  )|

  # d30 negative lookahead: february cannot have 30 days
  30(?!.02)|

  # d31 positive lookahead: month up to 31 days
  31(?=.(?:0[13578]|10|12))

) # eof day-check

# month 01-12
.(?:0[1-9]|1[012])

# year 0000-9999
.\d{4}

$ # end anchor

此外,请参阅SO正则表达式常见问题解答;如果失败,请告诉我。

6

在这里找到了这个正则表达式链接

^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$

这个验证格式是mm/dd/yyyy,并且正确地验证了有效日期(但不包括m/d/yyyy)。

一些测试


1
只适用于2014年之前,必须更改|20(0[0-9]|1[0-4])))为|20(0[0-9]|1[0-9])))以支持到2019年。 - Tony Dong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接