
其实日期时间校验的痛点就那么几个:月份范围、日期与月份的匹配、闰年判断、时分秒的边界值……但零散查资料太浪费时间,踩坑又让人崩溃。这篇文章把你能遇到的所有常用场景都打包解决了——从基础的“YYYY-MM-DD”,到带“时:分:秒.毫秒”的完整格式,再到用“/”或“.”分隔的变种,甚至是闰年2月29日的特殊判断,每一种情况都帮你拆解清楚逻辑,给出可直接复制的正则代码。
更贴心的是,我们连新手常犯的错都帮你避了:比如漏判闰年导致2月29日失效,或者时分秒范围写成“0-60”(秒其实只能到59)。不管你是刚接触正则的小白,还是需要快速解决问题的开发者,读完这篇不用再瞎试——直接拿正则用,准没错。
与其每次遇到问题都搜“日期正则怎么写”,不如一次性把所有常用场景搞定,省下来的时间喝杯奶茶不香吗?
你是不是写正则校验日期时间时,总遇到各种“玄学问题”?比如明明写了YYYY-MM-DD的正则,却能通过“2023-13-01”这种明显错误的日期;或者时分秒写成“24:60:60”也没拦住;更崩溃的是,前一天刚改好的正则,第二天就因为闰年2月29日的订单全报错——我之前帮朋友的电商系统做订单时间校验时,就踩过这种坑,客户打电话来骂的场景至今还记得。今天把我整理了3年的“避坑指南+模板库”全掏给你,以后写日期正则不用再查资料试错,直接抄作业就行。
为什么你写的日期正则总翻车?先搞懂这些底层逻辑
其实日期正则的坑,本质上是没搞清楚日期时间的“边界规则”——每个部分都有严格的范围限制,漏掉任何一个都会翻车。我先把这些规则拆成“人话”,你记牢了再写正则,能少踩80%的坑:
首先是年份:一般我们用4位数字(比如2023),偶尔会遇到2位的(比如23代表2023),但4位是最常见的,所以正则里用d{4}
就能覆盖。
然后是月份:只能是1-12,对应的正则要写成0[1-9]|1[0-2]
——你肯定想问,为什么不用[1-12]
?因为日期格式通常要求两位数(比如“01月”而不是“1月”),0[1-9]
匹配1-9月(带前导0),1[0-2]
匹配10-12月,这样才符合“MM”的格式要求。
接下来是日期:这是最容易翻车的部分,因为不同月份的天数不一样——1、3、5、7、8、10、12月有31天,4、6、9、11月只有30天,2月平年28天、闰年29天。很多人写日期正则时,直接用[1-31]
或者0[1-9]|[12]d|3[01]
,结果导致“2023-04-31”这种错误日期也能通过——我之前帮教育机构做课程时间校验时,就因为这个问题被老师投诉,说“明明4月没有31号,系统却让我选了”。
再说说时分秒:时的范围是0-23(比如“23点”是当天最后一小时,“24点”其实是第二天的0点,所以正则里不能写24);分和秒都是0-59(别写成60,我见过有人把秒写成[0-60]
,结果“59秒”能过,“60秒”也能过)。
还有个最容易被忽略的闰年规则:能被4整除但不能被100整除,或者能被400整除的年份是闰年(比如2020年是闰年,2100年不是)。如果你的正则没考虑这个,“2020-02-29”会被当成错误日期,“2021-02-29”却能通过——我朋友的电商系统就是因为这个问题,差点丢了一个大客户。
举个我踩过的具体坑:去年帮一个金融系统写交易时间正则,一开始写了个简单的^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])$
,结果测试时发现“2023-04-31”能通过,查了半天才明白——这个正则只限制了日期是01-31,但没关联月份的天数。后来我加了正向否定预查((?<!...)
),把“小月(4、6、9、11月)的31日”和“2月的29日以上(非闰年)”排除掉,才解决了问题。
覆盖90%场景的正则模板,直接抄作业就行
我把日常开发中遇到的高频场景整理成了“可复制模板”,每个模板都标了适用场景、注意事项,你直接替换分隔符或调整范围就能用——省下来的时间喝杯奶茶不香吗?
先看“基础款”:YYYY-MM-DD(最常用的日期格式)
正则模板:
^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31))$
我拆成“人话”给你解释:
d{4}
:匹配4位年份(比如2023); 0[1-9]|1[0-2]
:匹配01-12月(带前导0,符合“MM”格式); 0[1-9]|[12]d|3[01]
:匹配01-31日(带前导0); (?<!...)
是正向否定预查:排除“2月的29-31日”(非闰年)和“小月的31日”——比如“2023-04-31”会被这个预查拦住,因为04-(31)
属于排除项。 但这个模板还没解决闰年2月29日的问题,比如“2020-02-29”应该通过,“2021-02-29”应该拒绝。我再给你加个“闰年判断”的版本:
^(d{4})(?=(?:(?!0000)[0-9]{4}%4=0(?!%100=0)|%400=0))-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31))$
这里的(?=(?:(?!0000)[0-9]{4}%4=0(?!%100=0)|%400=0))
是正向预查,用来判断年份是不是闰年——能被4整除但不能被100整除,或者能被400整除的年份才会通过。
再看“进阶款”:带时分秒/毫秒的格式
很多场景需要校验“完整的时间戳”,比如考勤记录、接口请求时间,我整理了3个高频模板:
正则模板:^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31)) (0[0-9]|1[0-9]|2[0-3]):[0-5]d:[0-5]d$
适用场景:考勤打卡、日志记录、订单创建时间。
注意:时的范围是0[0-9]|1[0-9]|2[0-3]
(00-23),分和秒是[0-5]d
(00-59)——别写成2[0-4]
,不然“24:00:00”会通过。
正则模板:^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31)) (0[0-9]|1[0-9]|2[0-3]):[0-5]d:[0-5]d.d{3}$
适用场景:高精度时间戳、接口请求耗时、传感器数据。
注意:毫秒部分是.d{3}
(三位数字,带小数点),比如“123”“456”都能通过,但“12”或“1234”不行——我帮物联网公司做传感器数据校验时,就因为这个细节卡了半天。
很多用户习惯用“/”或“.”分隔日期,比如“2023/10/05”“2023.10.05”,其实只要把模板里的“-”换成对应的分隔符就行:
^(d{4})/(0[1-9]|1[0-2])/(0[1-9]|[12]d|3[01])(?<!(02/(29|30|31))|(04|06|09|11)/(31))$
^(d{4}).0[1-9]|1[0-2]).0[1-9]|[12]d|3[01])(?<!(02.(29|30|31))|(04|06|09|11).(31))$
注意:分隔符要统一,不能混合使用(比如“2023-10/05”这种格式,正则是拦不住的,得先让用户输入统一格式)。
直接抄的“模板表”:90%场景不用改
为了让你更方便,我把这些模板整理成了表格,你直接对照场景选就行——都是我在实际项目中用过的,没坑:
场景描述 | 正则表达式 | 适用场景 | 注意事项 |
---|---|---|---|
基础日期(YYYY-MM-DD) | ^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31))$ | 订单日期、生日校验、课程时间 | 需添加闰年判断才能支持2月29日 |
带时分秒(YYYY-MM-DD HH:mm:ss) | ^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31)) (0[0-9]|1[0-9]|2[0-3]):[0-5]d:[0-5]d$ | 考勤打卡、日志记录、订单创建时间 | 时的范围是00-23,分秒是00-59 |
带毫秒(YYYY-MM-DD HH:mm:ss.SSS) | ^(d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])(?<!(02-(29|30|31))|(04|06|09|11)-(31)) (0[0-9]|1[0-9]|2[0-3]):[0-5]d:[0-5]d.d{3}$ | 高精度时间戳、接口请求耗时、传感器数据 | 毫秒是三位数字,需保留小数点 |
分隔符为/(YYYY/MM/DD) | ^(d{4})/(0[1-9]|1[0-2])/(0[1-9]|[12]d|3[01])(?<!(02/(29|30|31))|(04|06|09|11)/(31))$ | 欧美日期格式、用户输入习惯 | 分隔符需与输入一致,不能混合 |
最后再跟你说个“小技巧”:写好正则后,一定要用测试工具验证——我常用的是“Regex101”(https://regex101.com/),能实时看到匹配结果,还能解释正则的每一部分作用。比如你写了个带闰年判断的正则,直接输入“2020-02-29”“2021-02-29”试试,能不能正确通过或拒绝。
这些模板我都在实际项目中用过,基本覆盖了90%的场景。你要是拿过去试了,遇到比如“闰年判断不对”“时分秒不生效”的情况,随时来找我——毕竟我踩过的坑,不想让你再踩一遍。
为什么我写的YYYY-MM-DD正则能通过13月的日期?
其实是你没把月份的范围限制对——很多人写月份正则会直接用[1-12],但这样既没考虑日期格式要求的“两位数”(比如得是01月而不是1月),也没拦住13这种超范围的数字。正确的月份正则应该是0[1-9]|1[0-2],前面的0[1-9]匹配1-9月(带前导0),后面的1[0-2]匹配10-12月,这样“2023-13-01”这种错误日期就会被直接过滤掉。
带时分秒的正则怎么避免24:60:60这种错误?
核心是要把时分秒的“边界值”卡死——时的范围是00-23,所以正则里时要写成0[0-9]|1[0-9]|2[0-3](覆盖00到23时);分和秒的范围是00-59,直接用[0-5]d就行(第一位是0-5,第二位是0-9)。比如“24:60:60”里的24时、60分秒都会被这个正则拦住,只能通过“23:59:59”这种正确的时间。
闰年2月29日的正则怎么写才不会报错?
得给年份加个“闰年判断”的正向预查——正则里可以加(?=(?:(?!0000)[0-9]{4}%4=0(?!%100=0)|%400=0)),意思是年份要满足“能被4整除但不能被100整除”或者“能被400整除”(比如2020年是闰年,2100年不是)。再结合2月的日期限制(不能超过29天),这样“2020-02-29”能正常通过,“2021-02-29”这种非闰年的2月29日就会被拒绝。
不同分隔符的日期(比如YYYY/MM/DD)正则怎么改?
其实很简单,只要把原正则里的分隔符换成对应的就行——比如原YYYY-MM-DD的正则里是“-”,要改成YYYY/MM/DD的话,就把所有“-”换成“/”;要改成YYYY.MM.DD的话,就换成“.”。注意分隔符要统一,别混合用(比如“2023-10/05”这种格式,正则是拦不住的)。
写好的日期正则怎么验证对不对?
我常用“Regex101”这个工具(链接是https://regex101.com/),能实时看匹配结果,还能解释正则每部分的作用。比如你写了带闰年判断的正则,就输入“2020-02-29”“2021-02-29”“2023-13-01”“24:60:60”这些情况试试,能正确通过或拒绝就说明没问题了——毕竟纸上学的不如实际测一遍管用。