IT俱乐部 正则表达式 日期校验 / 时间校验正则表达式深入解析(超实用!)

日期校验 / 时间校验正则表达式深入解析(超实用!)

0 结论

以下为常用的日期格式校验表达式

0.1 日期格式校验

以下日期校验可满足“四年一闰,百年不闰,四百年再闰”

  • yyyyMMdd
^(?:(?!0000)[0-9]{4}(?:(?:0[13578]|1[02])(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)(?:0[1-9]|[12][0-9]|30)|02(?:0[1-9]|1[0-9]|2[0-8]))|(?:(((d{2})(0[48]|[2468][048]|[13579][26])|(([02468][048])|([13579][26]))00))0229))$
  • yyyy-MM-dd
^(?:(?!0000)[0-9]{4}-(?:(?:0[13578]|1[02])(?:-0[1-9]|-[12][0-9]|-3[01])|(?:0[469]|11)(?:-0[1-9]|-[12][0-9]|-30)|02(?:-0[1-9]|-1[0-9]|-2[0-8]))|(?:(((d{2})(0[48]|[2468][048]|[13579][26])|(([02468][048])|([13579][26]))00))-02-29))$

0.2 时间格式校验

  • hhmmss

12 小时制,范围为 000000 - 115959

^(?!120000)(?:0[0-9]|1[12])(?:[0-5][0-9]){2}$

24 小时制,范围为 000000 - 235959

^(?!240000)(?:[01][0-9]|2[1-4])(?:[0-5][0-9]){2}$
  • hh:mm:ss

12 小时制,范围为 00:00:00 - 11:59:59

^(?!12:00:00)(?:0[0-9]|1[12])(?::[0-5][0-9]){2}$

24 小时制,范围为 00:00:00 - 23:59:59

^(?!24:00:00)(?:[01][0-9]|2[1-4])(?::[0-5][0-9]){2}$

0.3 使用方式

String uncheckedStr = "2023-12-12";
final String REGEX_PATTERN = "^(?:(?!0000)[0-9]{4}-(?:(?:0[13578]|1[02])(?:-0[1-9]|-[12][0-9]|-3[01])|(?:0[469]|11)(?:-0[1-9]|-[12][0-9]|-30)|02(?:-0[1-9]|-1[0-9]|-2[0-8]))|(?:(((d{2})(0[48]|[2468][048]|[13579][26])|(([02468][048])|([13579][26]))00))-02-29))$";
if (!Pattern.matches(REGEX_PATTERN, uncheckedStr)) {
    throw new BusinessException(ErrorCodeEnum.INVALID_TIME_PATTERN);
}

1 日期校验正则表达式解析

以 yyyyMMdd 为例解析日期校验正则表达式组装过程

  • 匹配前四位年份,匹配范围为 0001-9999 ,使用 (?!) 排除 0000 ,获得 yyyy 部分的校验正则表达式
(?!0000)[0-9]{4}
  • 由于月份与日期关联存在多种可能,因此采用模式匹配
1. 31 天的月份 01 03 05 07 08 10 12
(?:0[13578]|1[02])          匹配月份
(?:0[1-9]|[12][0-9]|3[01])  匹配日期 01 - 31 

2. 30 天的月份 04 06 09 11
(?:0[469]|11)               匹配月份
(?:0[1-9]|[12][0-9]|30)     匹配日期 01 - 30

3. 02 月存在闰月的问题,且条件较为复杂,因此先匹配 01 - 28 的日期范围
02                          匹配月份
(?:0[1-9]|1[0-9]|2[0-8])    匹配日期 01 - 28
  • 使用 | 组装匹配模式,多个模式使用 (?:MODELA|MODELB|MODELC) 的方式进行组合,此处组合匹配模式后获得 mmdd 部分的校验正则表达式
匹配 MMdd 的表达式
(?:(?:0[13578]|1[02])(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)(?:0[1-9]|[12][0-9]|30)|02(?:0[1-9]|1[0-9]|2[0-8]))
   |--            31 天的月份日期              --|--          30 天的月份日期         --|--     28 天的月份日期    --|
  • 拼接第 1 步和第 3 步的匹配模式以组装基本的 yyyyMMdd 模式,该模式下不考虑闰年,因此将第 2 步的二月的日期匹配范围改到了 29,该模式无法辨认 1900-02-29 的非闰年情形
不考虑闰年的简易模式(二月的日期匹配范围改到了29,以下模式能正常使用)
^(?!0000)[0-9]{4}(?:(?:0[13578]|1[02])(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)(?:0[1-9]|[12][0-9]|30)|02(?:0[1-9]|1[0-9]|2[0-9]))$
  • 进一步处理闰年年份
1. 非百年的年份处理较为简单,枚举 4 的倍数且不为 100 的倍数的年份
((d{2})(0[48]|[2468][048]|[13579][26])

2. 百年年份保留 400 的倍数
(([02468][048])|([13579][26]))00)
  • 拼接模式以匹配闰年日期 yyyy0229
(((d{2})(0[48]|[2468][048]|[13579][26])|(([02468][048])|([13579][26]))00))0229
|--          非百年                    --|--        百年(保留400年)      --|
  • 结合第 3 步和第 6 步的结果组装最终的匹配模式,该模式可以满足 四年一闰,百年不闰,四百年再闰 的需求
^(?:(?!0000)[0-9]{4}(?:(?:0[13578]|1[02])(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)(?:0[1-9]|[12][0-9]|30)|02(?:0[1-9]|1[0-9]|2[0-8]))|(?:(((d{2})(0[48]|[2468][048]|[13579][26])|(([02468][048])|([13579][26]))00))0229))$

附:下面是常用的一些时间日期格式验证Javascript代码

// JavaScript Document
var success = 'success';         //校验成功
var failure = 'failure';         //校验失败
var dateStrNull = 'dateStrNull'; //日期字符串为空
var formatError = "formatError"; //不符合格式
var lengthLt8 = "lengthLt8";     //长度小于8
var monthLT1  = 'monthLT1';    //月份不能小于1
var monthGT12 = 'monthGT12';   //月份大于12
var dayGT28 = 'dayGT28';       //日期大于28
var dayGT29 = 'dayGT29';       //日期大于29
var dayGT30 = 'dayGT30';       //日期大于30
var dayGT31 = 'dayGT31';       //日期大于31
var dayLT1 = 'dayLT1';         //日期小于1
var hour24System = '24';       //采用24小时制
var hour12System = '12';       //采用12小时制
var hourGT23 = 'hourGT23';     //小时大于23
var hourGT11 = 'hourGT11';     //小时大于11
var minuteGT59 = 'minuteGT59'; //分钟大于59
var secondGT59 = 'secondGT59'; //秒钟大于59
var dateCompare = 'date';   //比较年月日
var dateTimeCompare = 'dateTime'; //比较年月日或者时分秒或者所有
var timeCompare = 'time';   //比较年时分秒
var dateTimeGTNow = 'dateTimeGTNow';  //大于当前年月日或者时分秒或者所有
var dateTimeLTNow = 'dateTimeLTNow';  //小于当前年月日或者时分秒或者所有
var equality = 'equality';       //时间相等
 
//时间格式的正则表达式
var YYYYMMDDReg = /[d]{4}[/-]{1}[d]{1,2}[/-]{1}[d]{1,2}/g; 
var YYYYMMDDHHmmReg = /[d]{4}[/-]{1}[d]{1,2}[/-]{1}[d]{1,2}s[d]{1,2}[:][d]{1,2}/g;
var YYYYMMDDHHmmssReg = /[d]{4}[/-]{1}[d]{1,2}[/-]{1}[d]{1,2}s[d]{1,2}[:][d]{1,2}[:][d]{1,2}/g;
var HHmmssReg = /[d]{1,2}[:][d]{1,2}[:][d]{1,2}/g;
var HHmmReg = /[d]{1,2}[:][d]{1,2}/g;
 
/**
 * 校验是否为YYYY-MM-DD 日期格式
 * @param dateStr 时间字符串
 * @returns
 */
function validateYYYYMMDD(dateStr){
	//如果日期字符串为空
	if(strIsNull(dateStr)){
		return dateStrNull;
	}
	
	//如果小于8位
	if(dateStr.length =0 || oldStr.indexOf('/')>=0){
		return oldStr;
	}
	
	//如果小于6或者大于8
	if(oldStr.length != 8 ){
		return oldStr;
	}
	
	var idx1 = 4;
	var idx2 = 6;
	
	if(oldStr.length == 6){
		idx2 = 5;
	}else if(oldStr.length == 7){
		idx2 = 6;
	}
	
	var newStr = oldStr.substring(0,4) + "-" + oldStr.substring(4,idx2) + "-" + oldStr.substring(idx2);
	return newStr;
}
 
/**
 * 校验是否大于当前时间(自动检测传入的时间格式)
 * @param dateStr
 * @returns
 */
function isMorethanNow(dateStr){
	var result = 'equals';
	//如果是YYYY-MM-DD
	if(dateStr.replace(YYYYMMDDReg,'')==''){
		result = compareNow(dateStr,dateCompare);
		
	//如果是YYYY-MM-DD HH:mm:ss
	}else if(dateStr.replace(YYYYMMDDHHmmssReg,'')==''){
		result = compareNow(dateStr,dateTimeCompare);
		
	//如果是YYYY-MM-DD HH:mm
	}else if(dateStr.replace(YYYYMMDDHHmmReg,'')==''){
		result = compareNow(dateStr+":59",dateTimeCompare);
		
	//如果是HH:mm:ss
	}else if(dateStr.replace(HHmmssReg,'')==''){
		result = compareNow(dateStr,timeCompare);
	
	//如果是HH:mm
	}else if(dateStr.replace(HHmmReg,'')==''){
		result = compareNow(dateStr+":59",timeCompare);
	}
	//如果大于
	if(result == dateTimeGTNow){
		return 'true';
	}else if(result == dateTimeLTNow){
		return 'false';
	}else{
		return 'equals';
	}
}
 
/**
 * 比较当前的日期或者时间(日期,时间)
 * @param dateStr
 * @param compareType 比较方式 date,time,dateTime (默认比较年月日)
 */
function compareNow(dateStr,compareType){
	//如果比较方式为空
	if(strIsNull(compareType)){
		compareType = dateCompare;
	}
 
	//获取当前日期
	var now = new Date();
	//比较日期
	if(compareType == dateCompare){
		var nowDate = new Date(now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate());
		var fd_date = new Date(dateStr.replace(/-/g,'/'));
		
		if(fd_date>nowDate){
			return dateTimeGTNow;
		}else if(fd_datenow){
			return dateTimeGTNow;
		}else if(fd_datenowTimeStr){
			return dateTimeGTNow;
		}else if(dateStrNoSpace12){
		return monthGT12;
	}else if(month_num31){
		return dayGT31;
		
	//如果月份是4,6,9,11
	}else if((month_num==4||month_num==6||month_num==9||month_num==11)
			&&day_num>30){
		return dayGT30;
		
	//如果月份是2
	}else if(month_num==2){
		//如果为闰年
		if(isLeapYear(year_num)&&day_num>29){
			return dayGT29;
		}else if(day_num>28){
			return dayGT28;
		}
	}
	return success;
}
 
/**
 * 校验是否为闰年
 * @param year_num
 * @returns
 */
function isLeapYear(year_num){
 
	/*年份为4的倍数不一定是闰年
	 * (1)年份不是100的倍数但是4的倍数都是闰年 
	 * (2)年份是400的倍数的年份是闰年 (如1900年不是闰年,而2000年是闰年)
	 */
	return year_num%4==0&&(year_num%100!=0||year_num%400==0);
}
 
/**
 * 校验小时
 * @param hourSys 小时制
 * @param hour_num
 * @returns
 */
function validateHour(hour_num,hourSys){
	//24小时制
	if(hourSys == hour24System&&hour_num > 23){
		return hourGT23;
	
	//12小时制
	}else if(hourSys == hour12System&&hour_num > 11){
		return hourGT11;
	}
	return success;
}
 
/**
 * 校验分钟
 * @param minute_num
 * @returns
 */
function validateMinute(minute_num){
	//如果小时大于24
	if(minute_num>59){
		return minuteGT59;
	}
	return success;
}
 
/**
 * 校验秒钟
 * @param second_num
 * @returns
 */
function validateSecond(second_num){
	//如果小时大于24
	if(second_num>59){
		return secondGT59;
	}
	return success;
}
 
/**
 * 根据字符串获取整数值
 * @param str
 * @returns
 */
function getIntValue(str){
	if(strIsNull(str)){
		return 0;
	}
	
	return str.replace(/^0+/g,'');
}
 
/**
 * 字符串是否为空
 * @param dateStr
 */
function strIsNull(str){
	if(str!=null
			&&str.replace(/(^s*)|(s*$)/g,'').length>0){
		return false;
	}
	return true;
}
 
/**
 * 得到错误的提示信息
 * @param errCode 错误码
 * @param controlName 控件名
 * @param format 格式 
 */
function getErrorMsg(errCode,controlName,format){
	if(errCode == dateStrNull){
		return controlName+'不能为空';
	}else if(errCode == formatError){
		return controlName+'请按日期格式''+format+''输入';
	}else if(errCode == lengthLt8){
		return controlName+'请按日期格式'YYYYMMDD'输入';
	}else if(errCode == monthLT1){
		return controlName+'填写的月份有误,月份不能小于1';
	}else if(errCode == monthGT12){
		return controlName+'填写的月份有误,月份不能大于12';
	}else if(errCode == dayLT1){
		return controlName+'填写的日期有误,日期不能小于1';
	}else if(errCode == dayGT28){
		return controlName+'填写的日期有误,平年的2月份日期不能大于28';
	}else if(errCode == dayGT29){
		return controlName+'填写的日期有误,闰年的2月份日期不能大于29';
	}else if(errCode == dayGT30){
		return controlName+'填写的日期有误,月份为4,6,9,11时,日期不能大于30';
	}else if(errCode == dayGT31){
		return controlName+'填写的日期有误,月份为1,3,5,7,8,10,12时,日期不能大于31';
	}else if(errCode == hourGT23){
		return controlName+'填写的小时有误,24小时制的小时数不能超过23';
	}else if(errCode == hourGT11){
		return controlName+'填写的小时有误,12小时制的小时数不能超过11';
	}else if(errCode == minuteGT59){
		return controlName+'填写的分有误,分钟数不能超过59';
	}else if(errCode == secondGT59){
		return controlName+'填写的秒有误,秒钟数不能超过59';
	}else if(errCode == dateGTNow){
		return controlName+'填写日期有误,不能大于当前日期';
	}else if(errCode == dateLTNow){
		return controlName+'填写日期有误,不能小于当前日期';
	}else if(errCode == timeGTNow){
		return controlName+'填写时间有误,不能大于当前时间';
	}else if(errCode == timeLTNow){
		return controlName+'填写时间有误,不能小于当前时间';
	}else if(errCode == failure ){
		return controlName+'填写有误';
	}
}
 

总结 

到此这篇关于日期校验/时间校验正则表达式的文章就介绍到这了,更多相关日期时间校验正则表达式内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/navsub/regex/11066.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部