我最近一直在寻找一个正则表达式来做一些客户端日期检查,但我找不到能满足以下条件的表达式:
(这些限制超出了我的范围,并且根据客户要求,尽管我努力说服他们这不是最好的路线)
当前代码:
$('input').keyup(function()
{
var regex = /^(?:(0[1-9]|1[012])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})$/;
$(this).toggleClass('invalid',!regex.test($(this).val()));
});
更新
我应该注意,这主要是为了看看这样的正则表达式是否可行(因为在这个问题上使用正则表达式不是我的选择)。我知道用于验证日期的其他(和更好的)选项,但是如前所述 - 这是通过正则表达式查看是否可能。
答案 0 :(得分:25)
正如其他地方所提到的,正则表达式几乎不是你想要的。但是,话虽如此,如果你真的想要一个正则表达式,这就是它的构建方式:
31个月
(0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2}
30天的月份
(0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2}
2月1 - 8日始终有效
(02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2}
2月29日在闰年也有效
(02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)
这意味着如果你把它们放在一起就是这个:
((0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})|((0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2})|((02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))
这个版本有点短,但有点难以理解。
((0[13578]|1[02])[\/.]31[\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\/.](29|30)[\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))
这些脚本很长且不可维护。应该清楚的是,这不是一个好主意,但它是可能的。
注意事项:
[\/.]
作为分隔符(8个地方)答案 1 :(得分:13)
我建议您放弃尝试使用正则表达式。将日期解析为其组成部分(月,日,年)要好得多,然后使用数字比较来确保它在适当的范围内。
更好的是,看看Javascript Date.parse函数是否能达到你想要的效果。
使用正则表达式解析日期是可能的,但令人沮丧。很难做到正确,非正则表达式向导难以理解表达式(这意味着很难证明事情是正确的),并且与其他选项相比,慢。
答案 2 :(得分:7)
我就是这样做的:
function validate( input ) {
var date = new Date( input );
input = input.split( '/' );
return date.getMonth() + 1 === +input[0] &&
date.getDate() === +input[1] &&
date.getFullYear() === +input[2];
}
用法:
validate( '2/1/1983' ) // true
validate( '2/29/1983' ) // false
validate( '2/29/1984' ) // true (1984 is a leap year)
答案 3 :(得分:2)
显然,正则表达式不是理想的表达方式。此外,使用YYYY-MM-DD
(ISO 8601)格式更安全,而不是MM/DD/YYYY
。
那就是说,这是1800年1月1日至12月31日期间最短的全功能正则表达式:
^(((0[1-9]|1[012])\/(?!00|29)([012]\d)|(0[13-9]|1[012])\/(29|30)|(0[13578]|1[02])\/31)\/(18|19|20)\d{2}|02\/29\/((18|19|20)(0[48]|[2468][048]|[13579][26])|2000))$
长度:162个字符。
故障:
^ # start
(
( # non-leap months & days
(0[1-9]|1[012])/(?!00|29)([012]\\d) # all months, days 01-28, uses negative lookahead
|
(0[13-9]|1[012])/(29|30) # all months except feb, days 29,30
|
(0[13578]|1[02])/31 # all 31 day months, day 31 only
)
/
(18|19|20)\\d{2} # all years
|
02/29 # leap day
/
(
(18|19|20)(0[48]|[2468][048]|[13579][26]) # leap years not divisible by 100
|
2000 # leap years divisible by 100
)
)
$ # end
Here's a fiddle测试从00/00/1800到99/99/2099的所有用例。
此外,为了更有趣,here's another fiddle生成最糟糕的正则表达式仍然有效,1205306个字符长。它看起来像这样:
^(01/01/1800|01/02/1800|01/03/1800|...|12/29/2099|12/30/2099|12/31/2099)$
答案 4 :(得分:1)
YYYY-MM-DD格式的正则表达式
((18|19|20)[0-9]{2}[\-.](0[13578]|1[02])[\-.](0[1-9]|[12][0-9]|3[01]))|(18|19|20)[0-9]{2}[\-.](0[469]|11)[\-.](0[1-9]|[12][0-9]|30)|(18|19|20)[0-9]{2}[\-.](02)[\-.](0[1-9]|1[0-9]|2[0-8])|(((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)[\-.](02)[\-.]29
答案 5 :(得分:0)
使用时刻(不是正则表达式)我已经完成了以下操作:
假设您将ISO日期作为字符串值:
var isoDate = '2016-11-10';
var parsedIsoDate = moment(isoDate, ['YYYY-MM-DD'], true).format('YYYY-MM-DD');
if (parsedIsoDate !== isoDate) {
// Invalid date.
}
答案 6 :(得分:0)
^(((?:(?:1[6-9]|[2-9]\d)?\d{2})(-)(?:(?:(?:0?[13578]|1[02])(-)31)|(?:(?:0?[1,3-9]|1[0-2])(-)(?:29|30))))|(((?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))(-)(?:0?2(-)29))|((?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(-)(?:(?:0?[1-9])|(?:1[0-2]))(-)(?:0[1-9]|1\d|2[0-8]))))$
请尝试以上Reg表达式。我尝试了多种组合,发现它们正在发挥作用。
请检查这是否适合您。
接受格式:YYYY-MM-DD
从1600年开始接受
答案 7 :(得分:0)
根据您的要求找到正则表达式
处镜像和调试RegEx^(?:( ?: 31(/)(?: 0 [13578] | 1 [02]))\ 1 |(?:( ?: 29 | 30)(/)(?: 0 [13 -9] | 1 [0-2])\ 2))(?:( ?: 18 | 19 | 20)\ d {2})$ | ^(?: 29(/)02 \ 3(?:( ?:(?:( ?: 18 | 19 | 20))(?: 0 [48] | [2468] [048] | [13579] [26]))))$ | ^(?: 0?[1 -9] | 1 \ d | 2 [0-8])(/)(?:( ?: 0 [1-9])|(?:1 [0-2]))\ 4(?:(? :18 | 19 | 20)\ d {2})$
测试:
答案 8 :(得分:-1)
这是我用于客户端日期验证的RegEx。它的范围从1000到2999,验证闰年和可选的时间部分。不是很华丽:)
var r = /^(0[1-9]|1\d|2[0-8]|29(?=-\d\d-(?!1[01345789]00|2[1235679]00)\d\d(?:[02468][048]|[13579][26]))|30(?!-02)|31(?=-0[13578]|-1[02]))-(0[1-9]|1[0-2])-([12]\d{3})(\s([01]\d|2[0-3]):([0-5]\d):([0-5]\d))?$/gm;
r.test('20-02-2013 10:01:07'); // true
r.test('29-02-1700'); // false
r.test('29-02-1604 14:01:45'); // true
r.test('29-02-1900 20:10:50'); // false
r.test('31-12-2000'); // true
r.test('31-11-2008 05:05:05'); // false
r.test('29-02-2004 05:01:23'); // true
r.test('24-06-2014 24:10:05'); // false
答案 9 :(得分:-1)
我试图验证YYYY-MM-DD,其中YYYY可以是两位数,MM和DD可以是一位。这就是我提出的。它将所有世纪视为闰年。
((\d\d)?\d\d-((0?(1|3|5|7|8)|10|12)-(31|30|[21]\d|0?[1-9])|(0?(4|6|9)|11)-(31|30|[21]\d|0?[1-9])|0?2-((2[0-8]|1\d)|0?[1-9]))|(\d\d)?((0|2|4|6|8)(0|4|8)|(1|3|5|7|9)(2|6))-0?2-29)
答案 10 :(得分:-1)
为运动添加我的答案 - 否则我完全赞同@Jim。
这将匹配闰年,包括数字少于或大于4的那些。
^\d*((((^|0|[2468])[048])|[13579][26])00$)|((0[48]|(^0*|[2468])[048]|[13579][26]))$
Ruby中的一个迷你测试用例(^
替换为\A
,$
替换为\Z
,因为Ruby):
r = /\A\d*((((\A|0|[2468])[048])|[13579][26])00\Z)|((0[48]|(\A0*|[2468])[048]|[13579][26]))\Z/
100000.times do |year|
leap = year % 4 == 0 && ((year % 100 != 0) || (year % 400 == 0))
leap_regex = !year.to_s[r].nil?
if leap != leap_regex
print 'Assertion broken:', year, leap, leap_regex, "\n"
end
end
答案 11 :(得分:-2)
((0 [13578] | 1 [02])31 / [0-9] {2} [/]。)|((01 | 0 [3-9] | 1 [1-2]) /./.[0-9]{2})|((0[1-9]|1[0-2])/./.[0-9]{2})|((02)[/ ] 29 /.)
短版本的答案不适用于10/29和10/30任何一年长版本在下面工作是一个简单的java脚本程序我写的测试
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class RegxDateTest {
public static void main(String[] args) {
// String to be scanned to find the pattern.
String line = "This order was placed for QT3000! OK?";
String pattern ="((0[13578]|1[02])[\\/.]31[\\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\\/.](29|30)[\\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\\/.](0[1-9]|1[0-9]|2[0-8])[\\/.](18|19|20)[0-9]{2})|((02)[\\/.]29[\\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))";
// Create a Pattern object
Pattern r = Pattern.compile(pattern);
LocalDate startDate = new LocalDate("1950-01-01");
LocalDate endDate = new LocalDate("2020-01-01");
for (LocalDate date = startDate; date.isBefore(endDate); date = date.plusDays(1))
{
if (date.toString("MM/dd/yyyy").matches(pattern)) {
// System.out.println("This date does match: " + date.toString("MM/dd/yyyy") );
}else{
System.out.println("This date does not match: " + date.toString("MM/dd/yyyy") );
}
}
String baddate1="02/29/2016";
if (baddate1.matches(pattern)) {
System.out.println("This date does match: " + baddate1 );
}else{
System.out.println("This date does not match: " + baddate1 );
}
System.out.println("alldone: " );
}
}