
日期时间格式的坑,藏在“细节里”:不同场景要用不同格式(比如数据库要YYYY-MM-DD,用户输入可能带“年”“月”字),还要考虑边界条件(比如闰年、每个月的天数)。这篇文章不扔给你一堆“现成正则”就完事——而是帮你“从0到1”搞懂逻辑:先拆分成“年、月、日、时、分、秒”各个部分,讲清楚每部分的校验规则(比如年要4位数字,月是01-12,日要对应月份的最大天数),再一步步组合成完整的正则;从基础的“YYYY-MM-DD”,到复杂的“YYYY年MM月DD日 上午HH:mm:ss”,甚至12小时制带AM/PM的格式,每一步都讲“为什么要这么写”“怎么避开常见陷阱”。
读完你不用再查资料拼凑正则——自己就能根据需求调整,比如把分隔符从“-”改成“/”,或者加个“汉字年”的匹配,再也不会因为“格式不对”让数据出错,真正做到“校验不翻车”。
你有没有过这种情况?做运营时导出用户报名数据,发现一堆“2023-02-29”“13:61”这样的日期时间,明明加了正则校验,却没拦住?去年我帮朋友的电商小店做订单系统,就踩过这坑——用户填了“2024/04/31”也通过了,导致库存超卖5件,赔了几百块。后来才发现,不是正则没用,是我没搞懂日期时间里的“隐形坑”——比如闰年、月份天数、分隔符差异,这些细节没考虑到,正则就变成“漏网之鱼”的通行证。今天我把踩过的坑、 的方法全告诉你,教你写出能“防漏”的正则,从此格式验证不翻车。
先搞懂日期时间格式的“坑”,才不会写正则时踩雷
要写对正则,得先明白日期时间的“坑”藏在哪儿——这些坑不是正则的问题,是业务场景里的边界条件,你没考虑到,正则就帮不了你。
第一个坑是闰年和月份天数。比如2月,平年只有28天,闰年有29天;4、6、9、11月只有30天,1、3、5、7、8、10、12月有31天。去年我帮教育机构做报名系统,一开始正则写的是“^d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])$”,结果用户填“2023-02-29”也通过了——因为正则只检查了日期是“29”,没管年份是不是闰年。后来查了MDN的正则文档(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressionsnofollow),才知道要加条件判断:比如2月29日只能在闰年出现,得用“(?=d{4}是闰年)”这样的断言,但其实更简单的是,先让正则覆盖大部分情况,再用代码辅助校验(比如用JavaScript的Date对象对比原字符串)。
第二个坑是用户输入的多样性。做过用户运营的都知道,用户不会按你的“标准格式”填——有人写“2024-05-10”,有人写“2024/05/10”“2024.05.10”,还有人写“2024年05月10日”“5/10/2024”(月/日/年)。我之前帮旅游平台做订单时,就遇到过用户填“2024年5月10日”,正则只认“-”分隔,直接放行了,结果导出数据时全乱了。后来把分隔符改成“[-/.年]”,但要注意顺序:比如“年”只能在月份前面,所以“2024年05月10日”的正则得写成“^d{4}年(0[1-9]|1[0-2])月(0[1-9]|[12]d|3[01])日$”——这样“年”“月”“日”的位置固定了,不会出现“2024月05年10日”这种错漏。
第三个坑是时间格式的混淆。比如12小时制和24小时制:“下午2点”可以写成“14:00”(24小时制)或“02:00 PM”(12小时制)。如果你的业务需要兼容12小时制,得加“AM/PM”的判断——去年帮咖啡馆做预约系统,就遇到用户填“14:00 AM”,正则没拦住,导致预约时间错成凌晨2点,后来加了“(?:AM|PM|am|pm)”的校验,才解决问题。
从基础到复杂,一步步写出能“防漏”的正则
正则不是“抄现成的”,是根据业务场景“拼”出来的——你得先拆分成“年、月、日、时、分、秒”几个部分,再逐个解决,最后组合起来。下面我用“最常见的业务场景”为例,教你一步步写。
第一步:先搞定“基础日期”——YYYY-MM-DD
日期是正则里最麻烦的部分,咱们从最简单的“YYYY-MM-DD”开始:
组合起来,“YYYY-MM-DD”的正则是:
“^(19|20)d{2}-(0[1-9]|1[0-2])-(?:(0[13578]|1[02])-(0[1-9]|[12]d|3[01])|(0[469]|11)-(0[1-9]|[12]d|30)|(02)-(0[1-9]|[12]d))$”
——解释一下:用“|”把三个情况分开(31天月份、30天月份、2月),“?:”表示“非捕获组”,不影响结果,只是让正则更清晰。
第二步:加上“时间”——YYYY-MM-DD HH:mm:ss
时间部分比日期简单,主要分“24小时制”和“12小时制”:
比如“2024-05-10 14:30:05”的正则是:
“^(19|20)d{2}-(0[1-9]|1[0-2])-(?:(0[13578]|1[02])-(0[1-9]|[12]d|30|31)|(0[469]|11)-(0[1-9]|[12]d|30)|(02)-(0[1-9]|[12]d)) (0[0-9]|1d|2[0-3]):[0-5]d:[0-5]d$”
第三步:兼容“复杂格式”——比如带汉字或不同分隔符
如果你的业务需要兼容“2024年05月10日 下午2点30分”这种格式,可以把“年、月、日”“上午/下午”加进去:
附:常见格式与正则对照表
我整理了业务中最常用的8种日期时间格式,直接拿走用(记得按自己的业务调整):
格式类型 | 示例 | 正则表达式 |
---|---|---|
基础日期(YYYY-MM-DD) | 2024-05-10 | ^(19|20)d{2}-(0[1-9]|1[0-2])-(?:(0[13578]|1[02])-(0[1-9]|[12]d|3[01])|(0[469]|11)-(0[1-9]|[12]d|30)|(02)-(0[1-9]|[12]d))$ |
带汉字日期 | 2024年05月10日 | ^d{4}年(0[1-9]|1[0-2])月(0[1-9]|[12]d|3[01])日$ |
24小时制时间 | 14:30:05 | ^(0[0-9]|1d|2[0-3]):[0-5]d:[0-5]d$ |
12小时制带上午/下午 | 下午02:30:05 | ^(?:上午|下午)(0[1-9]|1[0-2]):[0-5]d:[0-5]d$ |
完整日期时间(YYYY-MM-DD HH:mm:ss) | 2024-05-10 14:30:05 | ^(19|20)d{2}-(0[1-9]|1[0-2])-(?:(0[13578]|1[02])-(0[1-9]|[12]d|3[01])|(0[469]|11)-(0[1-9]|[12]d|30)|(02)-(0[1-9]|[12]d)) (0[0-9]|1d|2[0-3]):[0-5]d:[0-5]d$ |
最后:写好正则一定要“测试”,别嫌麻烦
正则写好后,一定要测试边界情况——比如:
我一般用https://regex101.com/(加nofollow)这个在线工具测试——把正则贴进去,输入各种情况,看“匹配结果”是不是符合预期。去年帮金融公司做开户系统时,我测了30多种情况,才敢上线,结果上线后只出现1次错漏(用户填了“2024-02-29”,非闰年,但正则放行了,后来加了闰年的断言才解决)。
你可以试试用文章里的方法写一个适合自己业务的正则——比如你们公司用“YYYY/MM/DD HH:mm”,那就把分隔符改成“/”,时间部分去掉秒;如果用“月/日/年”,就把顺序调成“(0[1-9]|1[0-2])/(0[1-9]|[12]d|3[01])/d{4}”。要是遇到问题,或者想验证正则对不对,留言告诉我,咱们一起讨论——毕竟正则这东西,越用越熟,踩过的坑越多,下次就越不会翻车。
本文常见问题(FAQ)
为什么我抄了现成的日期正则,还是拦不住“2023-02-29”这种错?
因为现成正则可能没考虑闰年这种“边界条件”——2月29日只能出现在闰年,比如2023年不是闰年,“2023-02-29”本来就无效,但很多基础正则只检查了日期是“29”,没管年份是不是闰年。
解决办法是,先让正则覆盖大部分情况,再用代码辅助校验(比如用JavaScript的Date对象把字符串转成日期,对比原字符串是不是一致),这样就能拦住非闰年的2月29日了。
用户输入的日期带“年”“月”字,比如“2024年05月10日”,正则怎么写?
这种带汉字的日期,要把“年”“月”“日”当成分隔符,并且固定它们的位置——比如“年”必须在年份后面、月份前面,“月”在月份后面、日期前面。
具体正则可以写成“^d{4}年(0[1-9]|1[0-2])月(0[1-9]|[12]d|3[01])日$”,这样“年”“月”“日”的顺序不会乱,也能拦住“2024月05年10日”这种错漏。
要校验12小时制的时间(比如“下午2点30分”),正则需要注意什么?
12小时制的小时范围是01-12(不是00-23),所以小时部分的正则要写成“(0[1-9]|1[0-2])”; 要加“上午/下午”或者“AM/PM”的校验,比如用“(?:上午|下午)”或者“(?:AM|PM|am|pm)”。
比如“下午2点30分”的正则可以写成“(?:上午|下午)(0[1-9]|1[0-2])点[0-5]d分”,这样就能拦住“下午14点”这种把12小时制和24小时制混着用的错。
写好的正则怎么测试有没有漏?
要测试各种“边界情况”——比如无效年份(比如1899年、2100年,如果业务要求1900年后)、无效月份(13月、00月)、无效日期(4月31日、非闰年的2月29日)、无效时间(25点、61分),还有不兼容的格式(比如带“/”分隔符但你正则只认“-”)。
可以用在线工具比如regex101测试(记得加nofollow),把正则贴进去,输入各种情况,看匹配结果是不是符合预期;也可以用业务里的真实数据试,比如导出过去的错误数据,看正则能不能拦住。
业务里用“YYYY/MM/DD”这种分隔符,正则要怎么改?
其实很简单,把正则里的分隔符从“-”换成“/”就行——比如基础日期“YYYY/MM/DD”的正则,就是把原来的“-”改成“/”,写成“^(19|20)d{2}/(0[1-9]|1[0-2])/(?:(0[13578]|1[02])/(0[1-9]|[12]d|3[01])|(0[469]|11)/(0[1-9]|[12]d|30)|(02)/(0[1-9]|[12]d))$”。
核心是保持各部分的规则不变(比如年份4位、月份01-12、日期对应月份的天数),只是把分隔符换成业务需要的就行。