试图验证YYYY / MM / dd

时间:2014-07-28 05:45:16

标签: javascript regex

我知道那里有很多正则表达式线程,但我找不到任何地方。

我有一个适用于DD / MM / YYYY格式的正则表达式。 这是:

^((0?[13578]|1[02])[\/.]31[\/.][1-9][0-9]{3})|(([01,1]|0[3-9]|1[1-2])[\/.](29|30)[\/.][1-9][0-9]{3})|((0?[1-9]|1[0-2])[\/.](0?[1-9]|1[0-9]|2[0-8])[\/.][1-9][0-9]{3})|([02,2][\/.]29[\/.](([1-9][0-9](04|08|[2468][048]|[13579][26]))|([2468][0]{3})))$

示例: 29/2 / 2014,31 / 4/2014 ...

但是当我尝试使用YYYY / MM / DD格式进行验证时,我发现了这个:

^(((19|20)([2468][048]|[13579][26]|0[48])|2000)[\/.]02[\/.]29|((19|20)[0-9]{2}[\/.](0?[469]|11)[\/.](0?[1-9]|[12][0-9]|30)|(19|20)[0-9]{2}[\/.](0?[13578]|1[02])[\/.](0?[1-9]|[12][0-9]|3[01])|(19|20)[0-9]{2}[\/.]02[\/.](0?[1-9]|1[0-9]|2[0-8])))$

此模式仅适用于YYYY:19xx - > 20xxx。

当我更改它时,我预计它可以与YYYY一起使用:1xxx - > 9xxx。但它做得不好。

我是正则表达式的新手,很难逃脱它。

非常感谢,抱歉我的英语不好。

2 个答案:

答案 0 :(得分:4)

TL; DR Minitech的评论和TheQ的评论是正确的 - 这对正则表达式来说不是一项伟大的任务。下面给出了非正则表达式解决方案。只是为了踢,一个可怕的正则表达式。

编辑:我决定花几分钟时间在午餐时清理这个并添加"对"这样做的方法(没有正则表达式)以及jsFiddle。


真实答案

为了可读性,可维护性和健全性而执行此操作的正确方法是避免在此处使用正则表达式。编写正则表达式是非常困难的,结果非常难以阅读。

相反,只需将日期解析为其组成部分,然后像这样验证它们:

/**
 * Check whether a given year is a leap year
 * @param integer y The integer
 * @return boolean True for yes, false for no
 */
function isLeapYear(y) {
    return (0 == y % 4 && (0 == y % 400 || 0 != y % 100));
}

/**
 * Validate a date in YYYY/MM/DD format (allows YYYY/M/D and periods instead of slashes) without regex.
 * @param string str The date to test.
 * @return boolean True for valid, false for invalid.
 */
function validateDateWithoutRegex(str) {
    // check the format first
    if(!/^\d{4}[\/.]\d{1,2}[\/.]\d{1,2}$/.test(str)) {
        return false;
    }
    var parts = str.split(/\D/) // split on non-digits
        .map(function(val) { return parseInt(val); }); // convert strings to ints
    if(parts[0] < 1000 || parts[0] > 2999) { // invalid year
        return false;
    }
    if(parts[1] > 12 || parts[1] === 0) { // invalid month
        return false;
    }
    if(parts[2] > 31 || parts[2] === 0) { // invalid day
        return false;
    }
    switch(parts[1]) {
        case 4:
        case 6:
        case 9:
        case 11:
            if(parts[2] > 30) { // invalid day
                return false;
            }
            break;
        case 2: // February...
            return (parts[2] < 29 || parts[2] == 29 && isLeapYear(parts[0]));
            break;
    }
    return true;       
}

// Usage: validateDateWithoutRegex('2014/07/28'); // returns true

绝对疯狂的正则表达式答案

但是,因为我觉得今晚要做一些正则表达式......你可以使用这个怪物:

/^(?=(?!\d+\D0?2\D29)|(?:\d{2}(?:04|08|[13579][26]|[2468][048])|1[26]00|2000)\D0?2\D29$)([12]\d{3})[\/.]((?:0?[13578]|10|12)[\/.](?:0?[1-9]|[12]\d|3[0-1])|(?:0?[469]|11)[\/.](?:0?[1-9]|[12]\d|30)|0?2[\/.](?:0?[1-9]|1\d|2[0-9]))$/

这甚至证实了闰年。

Here's an interactive jsFiddle that also runs 50+ unit tests验证此方法的工作方式与非正则表达式的工作方式相同。

here's a Regex101 DEMO

可能有用的解释,由Debuggex提供:

Regular expression visualization

Debuggex Demo

示例匹配:

  • 2014年12月31日
  • 1937年4月17日
  • 1066年1月4日
  • 1066年2月28日
  • 2014年1月1日
  • 2012/02/29
  • 2000/02/29(400的倍数是闰年)
  • 1600/02/29(400的倍数是闰年)
  • 2015年1月1日

非匹配示例:

  • 1900/02/29(不是400的倍数的100的倍数不是闰年)
  • 2012年2月30日
  • 2012/00/01
  • 2012/00/00
  • 2012年4月31日
  • 2012/17/01
  • 2012/02/00
  • 1066/1/4444
  • 1066年2月29日
  • 1065年2月29日
  • 2014/14/1
  • 2014年2月29日

已经很晚了,所以非常有用的建议。

关于两种方法的历史问题的注释

除了其他方面,公历与新教改革的历史密切相关。它在创建时极具争议性,并未得到普遍采用。采用它的地方在不同的时间以不同的方式这样做。结果:哪些日期在格里高利历中有效取决于您所在的位置。 See, e.g., Wikipedia

基本上,格列高利历在1582年成立。从那时起,它的采用因国而异。例如,日历基本上定义为1582年的10月5日至14日。许多国家同样跳过了将旧(朱利安)日历日期改为与公历相匹配的日子,但他们并没有跳过相同的日子(甚至相同的天数);他们跳过的日子取决于国家和采用日历的时间。

因此,如果您希望能够在1582年之前(或某些地方甚至1923年)验证日期,请记住所有这些。玩得开心!

答案 1 :(得分:1)

由于闰年,仅使用正则表达式验证日期是一个坏主意。我会在javascript中使用模式^\d{4}/\d{1,2}/\d{1,2}$,并在服务器端进行更完整的验证。这种模式也更容易理解和修改。