javascript正则表达式验证日期(区别平年和闰年)
以前我们写正则表达式验证日期都只会考虑到平年而不会考虑到闰年,今天我们详细的介绍了关于在写正则时关于闰年和平年的区分写法。
DateTime 值类型表示值范围在公元(基督纪元)0001 年 1 月 1 日午夜 12:00:00 到公元 (C.E.) 9999 年 12 月 31 日晚上 11:59:59 之间的日期和时间。
我们进入正题。
首先需要验证年份,显然,年份范围为 0001 - 9999,匹配YYYY的正则表达式为:
[0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}
其中 [0-9] 也可以表示为 /d,但 /d 不如 [0-9] 直观,因此下面我将一直采用 [0-9]
用正则表达式验证日期的难点有二:一是大小月份的天数不同,二是闰年的考虑。
对于第一个难点,我们首先不考虑闰年,假设2月份都是28天,这样,月份和日期可以分成三种情况:
下面仅考虑月和日的正则
1.包括平年在内的所有年份的月份都包含1-28日
(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])
2.包括平年在内的所有年份除2月外都包含29和30日
(0[13-9]|1[0-2])-(29|30)
3.包括平年在内的所有年份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)
接着我们来解决第二个难点:闰年的考虑。根据闰年的定义,我们可以将闰年分为两类:
1、能被4整除但不能被100整除的年份。寻找后两位的变化规律,可以很快得到下面的正则匹配:
([0-9]{2})(0[48]|[2468][048]|[13579][26])
2、能被400整除的年份。能被400整除的数肯定能被100整除,因此后两位肯定是00,我们只要保证前两位能被4整除即可,相应的正则表达式为:
(0[48]|[2468][048]|[3579][26])00
2.最强验证日期的正则表达式,添加了闰年的验证
这个日期正则表达式支持
YYYY-MM-DD
YYYY/MM/DD
YYYY_MM_DD
YYYY.MM.DD的形式
match:2008-2-29 2008/02/29
not match : 2008-2-30 2007-2-29
1:四年一闰
([0-9]{2}(0[48]|[2468][048]|[13579][26])
2:百年不闰,四百年再闰
(0[48]|[2468][048]|[13579][26])00
3:合起来就是所有闰年的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)$
总合上面我们写个完美的日期验证函数,代码如下:
- function CheckDate(strDate){
- //var strDate = document.getElementById("date_hour").value;
- var reg=/^(/d{4})(/d{2})(/d{2})$/;
- if(!reg.test(strDate)){
- alert("日期格式不正确!/n正确格式为:20040101");
- return false;
- }
- //var ss=strDate.split("/");
- //var year=ss[0];
- //var month=ss[1];
- //var date=ss[2];
- var year=strDate.substring(0,4);
- var month=strDate.substring(4,6);
- var date=strDate.substring(6,8);
- //alert(year+month+date);
- if(!checkYear(year)){return false;}
- if(!checkMonth(month)){return false;}
- if(!checkDate(year,month,date)){return false;}
- return true;
- }
- function checkYear(year){
- if(isNaN(parseInt(year))){
- alert("年份输入有误,请重新输入!");
- return false;
- }
- else if(parseInt(year)<1950 || parseInt(year) >2050)
- {
- alert("年份应该在1950-2050之间!");
- return false;
- }
- else return true;
- }
- function checkMonth(month){
- if(isNaN(parseInt(month,10))){alert("月份输入有误,请重新输入!"); return false;}
- else if(parseInt(month,10)<1 || parseInt(month,10) >12)
- { alert("月份应该在1-12之间!");
- return false;}
- else return true;
- }
- function checkDate(year,month,date){
- var daysOfMonth=CalDays(parseInt(year),parseInt(month));
- if(isNaN(parseInt(date))){alert("日期输入有误,请重新输入!"); return false;}
- else if(parseInt(date)<1||parseInt(date)>daysOfMonth){ alert("日期应该在1-"+daysOfMonth+"之间!"); return false;}
- else return true;
- }
- function CalDays(year,month){
- var date= new Date(year,month,0);
- return date.getDate();
- }
- function isLeapYear(year){
- if((year %4==0 && year %100!=0) || (year %400==0)) return true;
- else return false;
- }
总结:
能被400整除的年份。能被400整除的数肯定能被100整除,因此后两位肯定是00,我们只要保证前两位能被4整除即可,相应的正则表达式为,关于公历闰年是这样规定的:地球绕太阳公转一周叫做一回归年,一回归年长365日5时48分 46秒。因此,公历规定有平年和闰年,平年一年有365日,比回归年短0.2422日,四年共短0.9688日,故每四年增加一日,这一年有366日,就 是闰年。但四年增加一日比四个回归年又多0.0312日,400年后将多3.12日,故在400年中少设3个闰年,也就是在400年中只设97个闰年,这 样公历年的平均长度与回归年就相近似了。由此规定:年份是整百数的必须是400的倍数才是闰年,例如1900年、2100年就不是闰年。