【前端】日期正则表达式(转发)
原文地址 : 正则应用之——日期正则表达式 http://blog.csdn.net/akkzhjj/article/details/16867303
1 概述
首先需要说明的一点,无论是Winform,还是Webform,都有很成熟的日历控件,无论从易用性还是可扩展性上看,日期的选择和校验还是用日历控件来实现比较好。
前几天在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 日,每四年累积约一天,把这一天加于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 根据天数是否与年份有关划分为两类
与年份无关的一类中,根据每月天数的不同,又可细分为两类
- 1、3、5、7、8、10、12月为1-31日
- 4、6、9、11月为1-30日
与年份有关的一类中
- 平年2月为1-28日
- 闰年2月为1-29日
- 所有年份的所有月份都包含1-28日
- 所有年份除2月外都包含29和30日
- 所有年份1、3、5、7、8、10、12月都包含31日
- 闰年2月包含29日
3.1.2 根据包含日期不同可划分为四类
3.1.3 分类方法选择
因为日期分类之后的实现,是要通过(exp1|exp2|exp3)这种分支结构来实现的,而分支结构是从左侧分支依次向右开始尝试匹配,当有一个分支匹配成功时,就不再向右尝试,否则尝试所有分支后并报告失败。
分支的多少,每个分支的复杂程度都会影响匹配效率,考虑到被验证日期概率分布,绝大多数都是落到1-28日内,所以采用第二种分类方法,会有效提高匹配效率。
3.2 正则实现
采用3.1.2节的分类方法,就可以针对每一个规则写出对应的正则,以下暂按MM-dd格式进行实现。
先考虑与年份无关的前三条规则,年份可统一写作
(?!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 (int i = 1; i < 10000; i++){if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0){richTextBox2.Text += string.Format("{0:0000}", i) + "\n";}
}
根据闰年的规则,很容易整理出规则,四年一闰;
([0-9]{2}(0[48]|[2468][048]|[13579][26])
百年不闰,四百年再闰。
(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)
四条规则都已实现,且互相间没有影响,合起来就是所有符合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 = 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的格式,可以对以上正则进行扩展
^(?:(?!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等。
另外需求说明的是,虽然日期的规则相对明确,可以采用这种方式裁剪来得到符合要求的日期正则,但是并不推荐这样使用正则,正则的强大在于它的灵活性,可以根据需求,量身打造最合适的正则,如果只是用来套用模板,那正则也就不称其为正则了。
正则的语法规则并不多,而且很容易入门,掌握语法规则,量体裁衣,才是正则之“道”。
6 应用
一、首先看需求
日期的输入:
手动输入,可输入两种格式yyyymmdd或yyyy-mm-dd
二、解决思路
用户手动输入日期,需要验证输入的日期格式
用户可能的输入情况可以分为以下几种:
(1).输入为空或者为空格
(2).输入非日期格式
根据保存到数据库中的日期格式,保存的格式为yyyy-mm-dd,所以用户在输入yyyymmdd后需要进行转换,转换成yyyy-mm-dd。
思路:
验证日期格式,首现想到的是VS的验证控件,但是因为需要验证的控件有几十个,使用验证控件就需要一个个的拉控件,如果后期需要修改也很麻烦,而通过JS实现控制,再通过正则表达式对日期进行验证。
三、JS实现
//验证日期
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-ddif (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" οnblur="date('txtenterschooldate')"/>
转载于:https://www.cnblogs.com/dragonir/p/7492411.html
【前端】日期正则表达式(转发)相关推荐
- 史上最详细的js日期正则表达式分享
最简单的正则 如 : /d{4}-/d{2}-/d{2} 但是实际情况却不是那么简单,,要考虑,有效性和闰年等问题..... 对于日期的有效范围,不同的应用场景会有所不同.MSDN中定义的DateTi ...
- ExtJS 前端 日期数据格式转化
如下图所示, ExtJS 代码: var tb1 = Ext.create('Ext.toolbar.Toolbar', {region: 'north',border: '0 0 0 0',heig ...
- 关于日期正则表达式的思路详解
1 概述 首先需要说明的一点,无论是Winform,还是Webform,都有很成熟的日历控件,无论从易用性还是可扩展性上看,日期的选择和校验还是用日历控件来实现比较好. 前几天在CSDN ...
- 正则应用之——日期正则表达式 1
1 概述 首先需要说明的一点,无论是 Winform ,还是 Webform ,都有很成熟的日历控件,无论从易用性还是可扩展性上看,日期的选择和校验还是用日历控件来实现比较好. 前几 ...
- 关于mybatis返回前端日期格式化问题
前言 使用mybatis难免会碰到返回时间类的问题.这里简单记录下学习总结. 现状 如果什么都不做,那么返回的类型 这个样式肯定不算我们想要的结果,但是先细想一下怎么处理,不至于在前端一堆js,后端遍 ...
- 日期正则表达式yyyyMMdd
日期校验yyyyMMdd, 包括闰月等校验. package com.xgcd;import java.util.regex.Matcher; import java.util.regex.Patte ...
- php正则表达式 匹配日期,正则表达式-正则表达式以匹配有效日期
我登陆这里是因为这个问题的标题很宽泛,我一直在寻找可用于匹配特定日期格式(例如OP)的正则表达式. 但是我随后发现,正如许多答案和评论已全面突出显示的那样,在提取与质量低劣或非结构化源数据混在一起的日 ...
- html各种日期正则表达式,日期正则表达式_常用的日期时间正则表达式
摘要 腾兴网为您分享:常用的日期时间正则表达式,掌上看家,学宝,水印相机,手柄助手等软件知识,以及手机录音剪辑软件,中国北斗卫星导航系统,自动刷新网页工具,盆景艺术在线,风控在线,叮当客,qbasic ...
- linux正则匹配8位数字日期,正则表达式之匹配数字范围,正则表达式匹配数字
正则表达式之匹配数字范围,正则表达式匹配数字 最近有个需求就是根据产品编号批量下架产品,需要下架日期为16-31号之间的产品, 比如编号为B201607280023匹配表达式如下: ^201607(1 ...
- 前端php解密,转发:RSA实现JS前端加密,PHP后端解密
web前端,用户注册与登录,不能直接以明文形式提交用户密码,容易被截获,这时就引入RSA. 前端加密 需引入4个JS扩展文件,jsbn.js.prng4.js.rng.js和rsa.js. RSA L ...
最新文章
- 野火开发版屏幕_鸿蒙OS手机版正式发布!副总裁现场表态:我们已经准备好了...
- 刚刚、几秒前,时间格式化函数
- 在JavaScript函数中定义全局变量
- nginx有10个以上参数rewrite的处理
- boost::allocate_shared相关的测试程序
- Python发送邮件以及自动化测试报告的生成
- 画毛毛虫代码计算机图形学,考试计算机图形学考试计算机图形学.docx
- AWS安装CDH5.3-CentOS6.4中关键操作步骤
- SharePoint 开发TimerJob 介绍
- java冒泡法程序,Java基础之冒泡法的实现
- 继涉黄被约谈 “比心陪练”App因内容涉宣扬暴力再被处罚
- 【Docker】docker设置固定ip地址
- js中数字直接点方法会报错,如1.toString()
- Selenium笔记:PO模型
- php框架 路由_PHP框架开发之Route路由简单实现
- 笔记本重新启动计算机,为什么笔记本电脑突然重新启动_计算机的基本知识_IT /计算机_信息...
- java反编译工具jd-gui下载与使用
- 我们比任何时候都更接近梦想
- 拨开字符编码的迷雾--编译器如何处理文件编码
- 在监控落地的路上,助你一臂之力
热门文章
- html5 获取本机号码,如何获取本机手机号码
- 计算机网络释疑与解答第六版 pdf,计算机网络释疑与习题解答第5版.pdf
- 如何安装ioncube扩展
- 罗技键盘+android风格,Logitech 罗技 K480 蓝牙键盘,IOS、OSX 和安卓三大系统使用体验...
- linux ora-12162,【转】ORA-12162问题的解决及其补充
- WebKit 是一个开源的浏览器引擎
- 事业单位资产管理系统广西某单位案例:实现资产动态全过程管理
- 新内容创业时代,微博的造“星”哲学
- 台式计算机如何双屏显示,电脑怎么分屏?|台式电脑双显示器连接方法
- vega56刷64_Vega56显卡怎么刷Bios VEGA56显卡刷Vega64方法