正则表达式日期:正则应用的 日期正则表达式

1概述
首先需要介绍说明无论是Winform还是Webform都有很成熟日历Control控件无论从易用性还是可扩展性上看日期选择和校验还是用日历Control控件来实现比较好
前几天在CSDN多个版块看到需要日期正则帖子所以整理了这篇文章和大家起讨论交流如有遗漏或地方还请大家指正
日期正则般是对格式有要求且数据不是直接由用户输入时使用因应用场景区别写出正则也区别复杂程度也自然区别正则书写需要根据具体情况具体分析个基本原则就是:只写合适不写复杂
对于日期提取只要能和非日期区分开写最简单正则即可
复制代码 代码如下:

\d{4}-\d{2}-\d{2}


如果可以在源串中唯定位yyyy-MM-dd格式日期则可用做提取
对于验证如果仅仅是验证组成及格式是没有多大意义还要加入对规则校验由于闰年存在使得日期校验正则变得比较复杂
先来考察下日期有效范围以及什么是闰年
2 日期规则
2.1 日期有效范围
对于日期有效范围区别应用场景会有所区别
MSDN中定义DateTime对象有效范围是:0001-01-01 00:00:00到9999-12-31 23:59:59
UNIX时间戳0按照ISO 8601规范标准为 :1970-01-01T00:00:00Z
而实际应用中日期范围基本上不会超出DateTime所规定范围所以正则验证取其中常用日期范围即可
2.2 什么是闰年
(以下摘自百度百科)
闰年(leap year)是为了弥补因人为历法规定造成年度天数和地球实际公转周期时间差而设立补上时间差年份为闰年
地球绕日运行周期为365天5小时48分46秒(合365.24219天)回归年(tropical year)公历平年只有365日比回归年短约0.2422 日每 4年累积约把这天加于2月末(即2月29日)使当年时间长度变为366日年就为闰年
需要注意是,现在公历是根据罗马人“儒略历”改编而得由于当时没有了解到每年要多算出0.0078天问题从公元前46年到16世纪共累计多出了10天为此当时教皇格雷果里十 3世将1582年10月5日人为规定为10月15日并开始了新闰年规定即规定公历年份是整百数必须是400倍数才是闰年不是400倍数就是平年比如1700年、1800年和1900年为平年2000年为闰年此后平均每年长度为365.2425天约4年出现1天偏差按照每 4年个闰年计算平均每年就要多算出0.0078天经过 4百年就会多出大约3天来因此每 4百年中要减少 3个闰年闰年计算归结起来就是通常说: 4年闰;百年不闰 4百年再闰
2.3 日期格式
根据区别语言文化日期会有所区别通常有以下几种格式:
yyyyMMdd
yyyy-MM-dd
yyyy/MM/dd
yyyy.MM.dd
3 日期正则表达式构建 3.1 规则分析
写复杂正则个常用思路方法就是先把不相关需求拆分开分别写出对应正则然后组合检查下相互关联关系以及影响基本上就可以得出对应正则
按闰年定义可知日期可以有几种分类思路方法
3.1.1 根据天数是否和年份有关划分为两类
和年份无关类中根据每月天数区别又可细分为两类
Ø 1、3、5、7、8、10、12月为1-31日
Ø 4、6、9、11月为1-30日
和年份有关类中
Ø 平年2月为1-28日
Ø 闰年2月为1-29日
3.1.2 根据包含日期区别可划分为 4类
Ø 所有年份所有月份都包含1-28日
Ø 所有年份除2月外都包含29和30日
Ø 所有年份1、3、5、7、8、10、12月都包含31日
Ø 闰年2月包含29日
3.1.3 分类思路方法选择
日期分类的后实现是要通过(exp1|exp2|exp3)这种分支结构来实现而分支结构是从左侧分支依次向右开始尝试匹配当有个分支匹配成功时就不再向右尝试否则尝试所有分支后并报告失败
分支多少每个分支复杂程度都会影响匹配效率考虑到被验证日期概率分布绝大多数都是落到1-28日内所以采用第 2种分类思路方法会有效提高匹配效率
3.2 正则实现
采用3.1.2节分类思路方法就可以针对每个规则写出对应正则以下暂按MM-dd格式进行实现
先考虑和年份无关前 3条规则年份可统写作
复制代码 代码如下:

(?!0000)[0-9]{4}


下面仅考虑月和日正则
Ø 包括平年在内所有年份月份都包含1-28日
复制代码 代码如下:

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


Ø 包括平年在内所有年份除2月外都包含29和30日
复制代码 代码如下:

(0[13-9]|1[0-2])-(29|30)


Ø 包括平年在内所有年份1、3、5、7、8、10、12月都包含31日
复制代码 代码如下:

(0[13578]|1[02])-31)


合起来就是除闰年2月29日外其它所有日期
复制代码 代码如下:

(?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)


接下来考虑闰年实现
Ø 闰年2月包含29日
这里月和日是固定就是02-29只有年是变化
可通过以下代码输出所有闰年年份考察规则
复制代码 代码如下:

for ( i = 1; i < 10000; i)
{
((i % 4 0 && i % 100 != 0) || i % 400 0)
{
richTextBox2.Text .Format("{0:0000}", i) + "\n";
}
}


根据闰年规则很容易整理出规则 4年闰;
复制代码 代码如下:

([0-9]{2}(0[48]|[2468][048]|[13579][26])


百年不闰 4百年再闰
复制代码 代码如下:

(0[48]|[2468][048]|[13579][26])00


合起来就是所有闰年2月29日
复制代码 代码如下:

([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)


4条规则都已实现且互相间没有影响合起来就是所有符合DateTime范围日期正则
复制代码 代码如下:

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


考虑到这个正则表达式仅仅是用作验证所以捕获组没有意义只会占用资源影响匹配效率所以可以使用非捕获组来进行优化
复制代码 代码如下:

^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$


以上正则年份0001-9999格式yyyy-MM-dd可以通过以下代码验证正则有效性和性能
复制代码 代码如下:

DateTime dt = DateTime(1, 1, 1);
DateTime endDay = DateTime(9999, 12, 31);
Stopwatch sw = Stopwatch;
sw.Start;
Regex dateRegex = Regex(@"^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$");
//Regex dateRegex = Regex(@"^((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)$");
Console.WriteLine("开始日期: " + dt.("yyyy-MM-dd"));
while (dt <= endDay)
{
(!dateRegex.IsMatch(dt.("yyyy-MM-dd")))
{
Console.WriteLine(dt.("yyyy-MM-dd") + " false");
}
(dt endDay)
{
;
}
dt = dt.AddDays(1);
}
Console.WriteLine("结束日期: " + dt.("yyyy-MM-dd"));
sw.Stop;
Console.WriteLine("测试用时: " + sw.ElapsedMilliseconds + "ms");
Console.WriteLine("测试完成!");
Console.ReadLine;


4 日期正则表达式扩展
4.1 “年月日”形式扩展
以上实现是yyyy-MM-dd格式日期验证考虑到连区别以及月和日可能为M和d即yyyy-M-d格式可以对以上正则进行扩展
复制代码 代码如下:

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


使用反向引用进行简化年份0001-9999格式yyyy-MM-dd或yyyy-M-d可以没有或是“-”、“/”、“.”的
复制代码 代码如下:

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


这就是“年月日”这种形式最全个正则了区别含义部分以区别颜色标识可以根据自己需要进行栽剪
4.2 其它形式扩展
了解了以上正则各部分代表含义互相间关系后就很容易扩展成其它格式日期正则如dd/MM/yyyy这种“日月年”格式日期
复制代码 代码如下:

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


这种格式需要注意就是不能用反向引用来进行优了等可根据自己需求栽剪
4.3 添加时间扩展
时间规格很明确也很简单基本上就HH:mm:ss和H:m:s两种形式
复制代码 代码如下:

([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]


合入到日期正则中yyyy-MM-dd HH:mm:ss
复制代码 代码如下:

^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$


4.4 年份定制
以上所有涉及到平年年份里使用是0001-9999当然年份也可以根据闰年规则定制
如年份1600-9999格式yyyy-MM-dd或yyyy-M-d可以没有或是“-”、“/”、“.”的
复制代码 代码如下:

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


5特别介绍说明
以上正则采用是最基本正则语法规则绝大多数采用传统NFA引擎语言都可以支持包括JavaScript、Java、.NET等
另外需求介绍说明虽然日期规则相对明确可以采用这种方式裁剪来得到符合要求日期正则但是并不推荐这样使用正则正则强大在于它灵活性可以根据需求量身打造最合适正则如果只是用来套用模板那正则也就不称其为正则了
正则语法规则并不多而且很容易入门掌握语法规则量体裁衣才是正则的“道”
Tags:  正则表达式 正则表达式应用 js正则表达式日期 正则表达式日期

延伸阅读

最新评论

发表评论