正则表达式 | 闰年及更多

IT技术 javascript regex datetime
2021-02-04 09:51:01

我最近一直在寻找一个正则表达式来进行一些客户端日期检查,但我一直无法找到满足以下条件的正则表达式:

  • 范围从 1800 到现在
  • 使用闰年执行正确的日期检查
  • MM/DD/YYYY 表格
  • 无效的日期检查

(这些限制超出了我的范围,并且是客户的要求,尽管我努力说服他们这不是最佳途径)

当前代码:

$('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()));    
});

更新:

我应该注意,这主要是为了看看是否可以使用这样的正则表达式(因为在这件事上使用正则表达式不是我的选择)。我知道验证日期的其他(和更好的)选项,但是如前所述 - 这是为了查看是否可以通过正则表达式。

6个回答

正如其他地方提到的,正则表达式几乎肯定不是你想要的。但是,话虽如此,如果你真的想要一个正则表达式,它是如何构建的:

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-28 日始终有效

(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))

这些脚本很长而且无法维护。应该清楚,这不是一个好主意,但它是可能的。

注意事项:

  • 范围 1800-2099(可以添加更多,没有太大困难,但需要在 4-6 个不同的地方进行更改)
  • 需要 2 位数的月和日(可以从表达式中删除 ~8 处的严格性)
  • [\/.] 作为分隔符(8 个位置)
  • 尚未经过测试(我们可以对照所有数字组合进行检查并与 javascript 日期函数进行比较?[证明我们正在重新发明轮子])
略有更新。我发现了一个(仅针对性能?)已修复的错误。为什么写这样的正则表达式不好。
2021-03-20 09:51:01
即使您确实想将 1800 年和 1900 年视为闰年,闰年部分也可以简化为((18|19|20)([02468][048]|[13579][26]))在此示例中,十位和个位的长度必须是十位和个位的两倍左右,因为我必须拆分十位为零,因为它有特殊规则,因为 1800 和 1900 不是闰年。
2021-03-22 09:51:01
@despot - 1800 和 1900 不是闰年。重新发明轮子不是一个好主意的另一个原因是,您可能不了解实际作用力的复杂性。
2021-03-27 09:51:01
它还有一个警告,它适用于未来到 2099 年的日期
2021-03-29 09:51:01
“30 天月份”部分有一个额外的括号,因此例如11/30/2012不匹配,也不匹配 30 天月份中的任何第 30 天日期。
2021-04-07 09:51:01

我建议您放弃为此使用正则表达式的尝试。最好将日期解析为其组成部分(月、日、年),然后使用数值比较来确保它在正确的范围内。

更好的是,看看 Javascript Date.parse函数是否会做你想要的。

使用正则表达式解析日期是可能的,但令人沮丧。很难弄对,表达式对于非正则表达式的向导来说很难理解(这意味着很难证明事情是正确的),并且与其他选项相比速度较慢

这就是我将如何做到的:

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)

现场演示: http : //jsfiddle.net/9QNRx/

显然,正则表达式不是执行此操作的理想方式。此外,使用YYYY-MM-DD(ISO 8601) 格式更安全,而不是MM/DD/YYYY.

也就是说,这里是从 1800 年 1 月 1 日到 2099 年 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

这是一个测试从 00/00/1800 到 99/99/2099 的所有用例的小提琴

此外,为了更有趣,这里有另一个小提琴,它生成了最糟糕的正则表达式,但仍然有效,长度为 1205306 个字符。它看起来像这样:

^(01/01/1800|01/02/1800|01/03/1800|...|12/29/2099|12/30/2099|12/31/2099)$

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
添加一些解释并回答此答案如何帮助 OP 解决当前问题
2021-03-17 09:51:01
不会有好的答案出现,因为这甚至不是一个可行的解决方案。
2021-04-06 09:51:01