- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章关于日期正则表达式的思路详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
1 概述 。
首先需要说明的一点,无论是Winform,还是Webform,都有很成熟的日历控件,无论从易用性还是可扩展性上看,日期的选择和校验还是用日历控件来实现比较好.
前几天在CSDN多个版块看到需要日期正则的帖子,所以整理了这篇文章,和大家一起讨论交流,如有遗漏或错误的地方,还请大家指正.
日期正则一般是对格式有要求,且数据不是直接由用户输入时使用。因应用场景的不同,写出的正则也不同,复杂程度也自然不同。正则的书写需要根据具体情况具体分析,一个基本原则就是:只写合适的,不写复杂的.
对于日期提取,只要能与非日期区分开,写最简单的正则即可,如 。
1
|
\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 日,每四年累积约一天,把这一天加于2月末(即2月29日),使当年时间长度变为366日,这一年就为闰年.
需要注意的是,现在的公历是根据罗马人的“儒略历”改编而得。由于当时没有了解到每年要多算出0.0078天的问题,从公元前46年,到16世纪,一共累计多出了10天。为此,当时的教皇格雷果里十三世,将1582年10月5日人为规定为10月15日。并开始了新闰年规定。即规定公历年份是整百数的,必须是400的倍数才是闰年,不是400的倍数的就是平年。比如,1700年、1800年和1900年为平年,2000年为闰年。此后,平均每年长度为365.2425天,约4年出现1天的偏差。按照每四年一个闰年计算,平均每年就要多算出0.0078天,经过四百年就会多出大约3天来,因此,每四百年中要减少三个闰年。闰年的计算,归结起来就是通常说的:四年一闰;百年不闰,四百年再闰.
2.3 日期的格式 。
根据不同的语言文化,日期的连字符会有所不同,通常有以下几种格式:
yyyyMMdd 。
yyyy-MM-dd 。
yyyy/MM/dd 。
yyyy.MM.dd 。
3 日期正则表达式构建 。
3.1 规则分析 。
写复杂正则的一个常用方法,就是先把不相关的需求拆分开,分别写出对应的正则,然后组合,检查一下相互的关联关系以及影响,基本上就可以得出对应的正则.
按闰年的定义可知,日期可以有几种分类方法.
3.1.1 根据天数是否与年份有关划分为两类 。
与年份无关的一类中,根据每月天数的不同,又可细分为两类 。
与年份有关的一类中 。
3.1.2 根据包含日期不同可划分为四类 。
3.1.3 分类方法选择 。
因为日期分类之后的实现,是要通过(exp1|exp2|exp3)这种分支结构来实现的,而分支结构是从左侧分支依次向右开始尝试匹配,当有一个分支匹配成功时,就不再向右尝试,否则尝试所有分支后并报告失败.
分支的多少,每个分支的复杂程度都会影响匹配效率,考虑到被验证日期概率分布,绝大多数都是落到1-28日内,所以采用第二种分类方法,会有效提高匹配效率.
3.2 正则实现 。
采用3.1.2节的分类方法,就可以针对每一个规则写出对应的正则,以下暂按MM-dd格式进行实现.
先考虑与年份无关的前三条规则,年份可统一写作 。
1
|
(?!0000)[0-9]{4}
|
下面仅考虑月和日的正则 。
包括平年在内的所有年份的月份都包含1-28日 。
1
|
(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])
|
包括平年在内的所有年份除2月外都包含29和30日 。
1
|
(0[13-9]|1[0-2])-(29|30)
|
包括平年在内的所有年份1、3、5、7、8、10、12月都包含31日 。
1
|
(0[13578]|1[02])-31)
|
合起来就是除闰年的2月29日外的其它所有日期 。
1
|
(?!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,只有年是变化的.
可通过以下代码输出所有的闰年年份,考察规则 。
1
2
3
4
5
|
for
(int i = 1; i < 10000; i++){
if
((i % 4 == 0 && i % 100 != 0) || i % 400 == 0){
richTextBox2.Text += string.Format(
"{0:0000}"
, i) +
"\n"
;
}
}
|
根据闰年的规则,很容易整理出规则,四年一闰; 。
1
|
([0-9]{2}(0[48]|[2468][048]|[13579][26])
|
百年不闰,四百年再闰.
1
|
(0[48]|[2468][048]|[13579][26])00
|
合起来就是所有闰年的2月29日 。
1
|
([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)
|
四条规则都已实现,且互相间没有影响,合起来就是所有符合DateTime范围的日期的正则 。
1
|
^((?!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)$
|
考虑到这个正则表达式仅仅是用作验证,所以捕获组没有意义,只会占用资源,影响匹配效率,所以可以使用非捕获组来进行优化.
1
|
^(?:(?!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。可以通过以下代码验证正则的有效性和性能 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
DateTime dt = new DateTime(1, 1, 1);
DateTime endDay = new DateTime(9999, 12, 31);
Stopwatch sw = new Stopwatch();
sw.Start();
Regex dateRegex = new 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 = new 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.ToString(
"yyyy-MM-dd"
));
while
(dt < endDay){
if
(!dateRegex.IsMatch(dt.ToString(
"yyyy-MM-dd"
))){
Console.WriteLine(dt.ToString(
"yyyy-MM-dd"
) +
" false"
);
}
dt = dt.AddDays(1);
}
if
(!dateRegex.IsMatch(dt.ToString(
"yyyy-MM-dd"
))){
Console.WriteLine(dt.ToString(
"yyyy-MM-dd"
) +
" false"
);
}
Console.WriteLine(
"结束日期: "
+ dt.ToString(
"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的格式,可以对以上正则进行扩展 。
1
|
^(?:(?!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,连字符可以没有或是“-”、“/”、“.”之一.
1
|
^(?:(?!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这种“日月年”格式的日期.
1
|
^(?:(?:(?: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两种形式.
1
|
([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]
|
合入到日期的正则中,yyyy-MM-dd HH:mm:ss 。
1
|
^(?:(?!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
|
^(?:(?: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等.
另外需求说明的是,虽然日期的规则相对明确,可以采用这种方式裁剪来得到符合要求的日期正则,但是并不推荐这样使用正则,正则的强大在于它的灵活性,可以根据需求,量身打造最合适的正则,如果只是用来套用模板,那正则也就不称其为正则了.
正则的语法规则并不多,而且很容易入门,掌握语法规则,量体裁衣,才是正则之“道”.
6 应用 。
1、首先看需求 。
日期的输入:
手动输入,可输入两种格式yyyymmdd或yyyy-mm-dd 。
2、解决思路 。
用户手动输入日期,需要验证输入的日期格式 。
用户可能的输入情况可以分为以下几种:
(1).输入为空或者为空格 。
(2).输入非日期格式 。
根据保存到数据库中的日期格式,保存的格式为yyyy-mm-dd,所以用户在输入yyyymmdd后需要进行转换,转换成yyyy-mm-dd.
思路:
验证日期格式,首现想到的是VS的验证控件,但是因为需要验证的控件有几十个,使用验证控件就需要一个个的拉控件,如果后期需要修改也很麻烦,而通过JS实现控制,再通过正则表达式对日期进行验证.
3、JS实现 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
//验证日期
function
date(id) {
var
idvalue = document.getElementById(id).value;
//通过查找元素
var
tmpStr =
""
;
var
strReturn =
""
;
//调用trim()去掉空格,因为js不支持trim()
var
iIdNo = trim(idvalue);
//正则表达式,判断日期格式,包括日期的界限,日期的格式,平年和闰年
var
v = idvalue.match(/^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$/);
//输入为空时跳过检测
if
(iIdNo.length == 0) {
return
false
;
}
//自动更改日期格式为yyyy-mm-dd
if
(iIdNo.length == 8) {
tmpStr = iIdNo.substring(0, 8);
tmpStr = tmpStr.substring(0, 4) +
"-"
+ tmpStr.substring(4, 6) +
"-"
+ tmpStr.substring(6, 8)
document.getElementById(id).value = tmpStr;
document.getElementById(id).focus();
}
//验证,判断日期格式
if
((iIdNo.length != 8) && !v) {
strReturn =
"日期格式错误,提示:19990101或1999-01-01"
;
alert(strReturn);
document.getElementById(id).select();
return
false
;
}
}
//运用正则表达式去除字符串两端空格(因为js不支持trim())
function
trim(str) {
return
str.replace(/(^\s*)|(\s*$)/g,
""
);
}
//前台调用(获得焦点触发)
<input class=
"txtenterschooldate"
size=
"14"
type=
"text"
id=
"txtenterschooldate"
name=
"txtenterschooldate"
onblur=
"date('txtenterschooldate')"
/>
|
总结 。
以上所述是小编给大家介绍的关于日期正则表达式的思路详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我网站的支持! 。
原文链接:http://www.cnblogs.com/dragonir/archive/2017/09/07/7492411.html 。
最后此篇关于关于日期正则表达式的思路详解的文章就讲到这里了,如果你想了解更多关于关于日期正则表达式的思路详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
大家好,我是汤师爷~ 什么是订单履约系统? 订单履约是从消费者下单支付到收到商品的全流程管理过程,包括订单接收、订单派单、库存分配、仓储管理和物流配送等环节,核心目标是确保商品准时、准确地送达消费
大家好,我是汤师爷~ 今天聊聊促销系统整体规划。 各类促销活动的系统流程,可以抽象为3大阶段: B端促销活动管理:商家运营人员在后台系统中配置和管理促销活动,包括设定活动基本信息、使用规则
全称“Java Virtual Machine statistics monitoring tool”(statistics 统计;monitoring 监控;tool 工具) 用于监控虚拟机的各种运
主要是讲下Mongodb的索引的查看、创建、删除、类型说明,还有就是Explain执行计划的解释说明。 可以转载,但请注明出处。  
1>单线程或者单进程 相当于短链接,当accept之后,就开始数据的接收和数据的发送,不接受新的连接,即一个server,一个client 不存在并发。 2>循环服务器和并发服务器
详解 linux中的关机和重启命令 一 shutdown命令 shutdown [选项] 时间 选项: ?
首先,将json串转为一个JObject对象: ? 1
matplotlib官网 matplotlib库默认英文字体 添加黑体(‘SimHei')为绘图字体 代码: plt.rcParams['font.sans-serif']=['SimHei'
在并发编程中,synchronized关键字是常出现的角色。之前我们都称呼synchronized关键字为重量锁,但是在jdk1.6中对synchronized进行了优化,引入了偏向锁、轻量锁。本篇
一般我们的项目中会使用1到2个数据库连接配置,同程艺龙的数据库连接配置被收拢到统一的配置中心,由DBA统一配置和维护,业务方通过某个字符串配置拿到的是Connection对象。  
实例如下: ? 1
1. MemoryCahe NetCore中的缓存和System.Runtime.Caching很相似,但是在功能上做了增强,缓存的key支持object类型;提供了泛型支持;可以读缓存和单个缓存
argument是javascript中函数的一个特殊参数,例如下文,利用argument访问函数参数,判断函数是否执行 复制代码 代码如下: <script
一不小心装了一个Redis服务,开了一个全网的默认端口,一开始以为这台服务器没有公网ip,结果发现之后悔之莫及啊 某天发现cpu load高的出奇,发现一个minerd进程 占了大量cpu,googl
今天写这个是为了 提醒自己 编程过程 不仅要有逻辑 思想 还有要规范 代码 这样可读性 1、PHP 编程规范与编码习惯最主要的有以下几点: 1 文件说明 2 funct
摘要:虚拟机安装时一般都采用最小化安装,默认没有lspci工具。一台测试虚拟网卡性能的虚拟机,需要lspci工具来查看网卡的类型。本文描述了在一个虚拟机中安装lspci工具的具体步骤。 由于要测试
1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统
目录 算术运算符 基本四则运算符 增量赋值运算符 自增/自减运算符 关系运算符 逻
如下所示: ? 1
MapperScannerConfigurer之sqlSessionFactory注入方式讲解 首先,Mybatis中的有一段配置非常方便,省去我们去写DaoImpl(Dao层实现类)的时间,这个
我是一名优秀的程序员,十分优秀!