首页 文章

正则表达式匹配有效日期

提问于
浏览
56

我正在尝试编写一个验证日期的正则表达式 . 正则表达式需要匹配以下内容

  • M / D / YYYY

  • MM / DD / YYYY

  • 单个数字月份可以从前导零开始(例如:03/12/2008)

  • 单位数天可以从前导零开始(例如:2008年3月2日)

  • 不能包括2月30日或2月31日(例如:2008年2月31日)

到目前为止我有

^(([1-9]|1[012])[-/.]([1-9]|[12][0-9]|3[01])[-/.](19|20)\d\d)|((1[012]|0[1-9])(3[01]|2\d|1\d|0[1-9])(19|20)\d\d)|((1[012]|0[1-9])[-/.](3[01]|2\d|1\d|0[1-9])[-/.](19|20)\d\d)$

这恰当匹配除了它还包括2/30/2008和2/31/2008 .

有人有更好的建议吗?

Edit: 我在RegExLib上找到the answer

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

它匹配遵循MM / DD / YYYY格式的所有有效月份 .

谢谢大家的帮助 .

15 回答

  • 3

    如果你打算用正则表达式坚持这样做,我会建议:

    ( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
      0?2 / (0?1| <...> |28|29) ) 
    / (19|20)[0-9]{2}
    

    这可能使阅读和理解成为可能 .

  • 5

    这不适合使用正则表达式 . 你最好还是用

    [0-9]{2}/[0-9]{2}/[0-9]{4}
    

    然后用更高级别的语言检查范围 .

  • 2

    这是Reg ex,它匹配所有有效日期,包括闰年 . 格式接受mm / dd / yyyy或mm-dd-yyyy或mm.dd.yyyy格式

    ^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

    礼貌Asiq Ahamed

  • 1

    我登陆这里因为这个问题的 Headers 很广泛,我正在寻找一个可以用来匹配特定日期格式(如OP)的正则表达式 . 但后来我发现,由于许多答案和评论都得到了全面的强调,因此在提取与质量差或非结构化源数据混合的日期时,有许多陷阱会使构建有效模式变得非常棘手 .

    在我对这些问题的探索中,我提出了一个系统,通过将四个更简单的子表达式排列在分隔符上,以及命令中年,月,日字段的有效范围,可以构建正则表达式 . 你需要 .

    这些是 :-

    Delimeters

    [^\w\d\r\n:]
    

    这将匹配任何不是单词字符,数字字符,回车符,换行符或冒号的内容 . 冒号必须在那里以防止在看起来像日期的时间匹配(参见我的测试数据)

    您可以优化模式的这一部分以加速匹配,但这是检测大多数有效分隔符的良好基础 .

    但请注意;它将匹配一个字符串与混合分隔符,如2 / 12-73,可能实际上不是一个有效的日期 .

    Year Values

    (\d{4}|\d{2})
    

    这匹配一组两位或四位数,在大多数情况下这是可以接受的,但是如果你处理的是0-999年或9999年以上的数据,你需要决定如何处理这个数据,因为在大多数情况下是1,3或> 4位数年份是垃圾 .

    Month Values

    (0?[1-9]|1[0-2])
    

    匹配1到12之间的任何数字,有或没有前导零 - 注意:0和00不匹配 .

    Date Values

    (0?[1-9]|[12]\d|30|31)
    

    匹配1到31之间的任何数字,有或没有前导零 - 注意:0和00不匹配 .

    This expression matches Date, Month, Year formatted dates

    (0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})
    

    但它也会匹配部分年份,月份日期 . 它也应该与边界运算符一起预订,以确保选择整个日期字符串,并防止从格式不正确的数据中提取有效的子日期,即没有边界标记20/12/194匹配为20/12/19和101/12/1974比赛为01/12/1974

    将下一个表达式的结果与上面的表达式的结果与废话部分(下面)中的测试数据进行比较

    \b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b
    

    这个正则表达式没有验证,所以一个格式良好但无效的日期,如31/02/2001将匹配 . 这是一个数据质量问题,正如其他人所说,你的正则表达式不需要验证数据 .

    因为您(作为开发人员)无法保证源数据的质量,您需要在代码中执行和处理其他验证,如果您尝试匹配 and 验证RegEx中的数据,它会变得非常混乱并且变得很难支持没有 very 简明文档 .

    垃圾进垃圾出 .

    话虽如此,如果你确实有混合格式的日期值变化,你必须提取尽可能多的;你可以将几个表达式组合在一起;

    This (disastrous) expression matches DMY and YMD dates

    (\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b)|(\b(0?[1-9]|1[0-2])[^\w\d\r\n:](0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](\d{4}|\d{2})\b)
    

    但是你无法判断像1969年6月9日这样的日期是9月6日还是6月9日 . 我正在努力想到一个不会在某个地方出现问题的情况,这是不好的做法,你不应该像那样处理它 - 找到数据所有者并用治理锤击中它们 .

    最后,如果你想匹配一个没有分隔符的YYYYMMDD字符串,你可以把一些不确定性排除在外,表达式看起来像这样

    \b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b
    

    但请再次注意,它将匹配格式良好但无效的值像20010231(2月31日!):)

    Test data

    在试验这个线程中的解决方案时,我最终得到了一个测试数据集,其中包括各种有效和无效的日期以及一些您可能想要或可能不想匹配的棘手情况,即可能与日期和日期匹配的时间 . 多行 .

    我希望这对某人有用 .

    Valid Dates in various formats
    
    Day, month, year
    2/11/73
    02/11/1973
    2/1/73
    02/01/73
    31/1/1973
    02/1/1973
    31.1.2011
    31-1-2001
    29/2/1973
    29/02/1976 
    03/06/2010
    12/6/90
    
    month, day, year
    02/24/1975 
    06/19/66 
    03.31.1991
    2.29.2003
    02-29-55
    03-13-55
    03-13-1955
    12\24\1974
    12\30\1974
    1\31\1974
    03/31/2001
    01/21/2001
    12/13/2001
    
    Match both DMY and MDY
    12/12/1978
    6/6/78
    06/6/1978
    6/06/1978
    
    using whitespace as a delimiter
    
    13 11 2001
    11 13 2001
    11 13 01 
    13 11 01
    1 1 01
    1 1 2001
    
    Year Month Day order
    76/02/02
    1976/02/29
    1976/2/13
    76/09/31
    
    YYYYMMDD sortable format
    19741213
    19750101
    
    Valid dates before Epoch
    12/1/10
    12/01/660
    12/01/00
    12/01/0000
    
    Valid date after 2038
    
    01/01/2039
    01/01/39
    
    Valid date beyond the year 9999
    
    01/01/10000
    
    Dates with leading or trailing characters
    
    12/31/21/
    31/12/1921AD
    31/12/1921.10:55
    12/10/2016  8:26:00.39
    wfuwdf12/11/74iuhwf
    fwefew13/11/1974
    01/12/1974vdwdfwe
    01/01/99werwer
    12321301/01/99
    
    Times that look like dates
    
    12:13:56
    13:12:01
    1:12:01PM
    1:12:01 AM
    
    Dates that runs across two lines
    
    1/12/19
    74
    
    01/12/19
    74/13/1946
    
    31/12/20
    08:13
    
    Invalid, corrupted or nonsense dates
    
    0/1/2001
    1/0/2001
    00/01/2100
    01/0/2001
    0101/2001
    01/131/2001
    31/31/2001
    101/12/1974
    56/56/56
    00/00/0000
    0/0/1999
    12/01/0
    12/10/-100
    74/2/29
    12/32/45
    20/12/194
    
    2/12-73
    
  • 12

    可维护的Perl 5.10版本

    /
      (?:
          (?<month> (?&mon_29)) [\/] (?<day>(?&day_29))
        | (?<month> (?&mon_30)) [\/] (?<day>(?&day_30))
        | (?<month> (?&mon_31)) [\/] (?<day>(?&day_31))
      )
      [\/]
      (?<year> [0-9]{4})
    
      (?(DEFINE)
        (?<mon_29> 0?2 )
        (?<mon_30> 0?[469]   | (11) )
        (?<mon_31> 0?[13578] | 1[02] )
    
        (?<day_29> 0?[1-9] | [1-2]?[0-9] )
        (?<day_30> 0?[1-9] | [1-2]?[0-9] | 30 )
        (?<day_31> 0?[1-9] | [1-2]?[0-9] | 3[01] )
      )
    /x
    

    您可以在此版本中按名称检索元素 .

    say "Month=$+{month} Day=$+{day} Year=$+{year}";
    

    (没有尝试限制年份的值 . )

  • 3

    要按以下格式控制日期有效性:

    YYYY / MM / DD或YYYY-MM-DD

    我建议您使用以下正则表达式:

    (((19|20)([2468][048]|[13579][26]|0[48])|2000)[/-]02[/-]29|((19|20)[0-9]{2}[/-](0[4678]|1[02])[/-](0[1-9]|[12][0-9]|30)|(19|20)[0-9]{2}[/-](0[1359]|11)[/-](0[1-9]|[12][0-9]|3[01])|(19|20)[0-9]{2}[/-]02[/-](0[1-9]|1[0-9]|2[0-8])))
    

    火柴

    2016-02-29 | 2012-04-30 | 2019年9月31日

    非匹配

    2016-02-30 | 2012-04-31 | 2019/09/35

    如果您只想允许'/'或' - '分隔符,可以自定义它 . 该RegEx严格控制日期的有效性,并验证28,30和31天月,甚至是29/02月的闰年 .

    试试吧,它运行良好,防止你的代码出现大量错误!

    仅供参考:我为SQL datetime做了一个变种 . 你会在那里找到它(寻找我的名字):Regular Expression to validate a timestamp

    欢迎反馈:)

  • 1

    听起来你为此目的而过度扩展正则表达式 . 我要做的是使用正则表达式匹配几种日期格式,然后使用单独的函数来验证如此提取的日期字段的值 .

  • 126

    Perl扩展版

    注意使用 /x 修饰符 .

    /^(
          (
            ( # 31 day months
                (0[13578])
              | ([13578])
              | (1[02])
            )
            [\/]
            (
                ([1-9])
              | ([0-2][0-9])
              | (3[01])
            )
          )
        | (
            ( # 30 day months
                (0[469])
              | ([469])
              | (11)
            )
            [\/]
            (
                ([1-9])
              | ([0-2][0-9])
              | (30)
            )
          )
        | ( # 29 day month (Feb)
            (2|02)
            [\/]
            (
                ([1-9])
              | ([0-2][0-9])
            )
          )
        )
        [\/]
        # year
        \d{4}$
    
      | ^\d{4}$ # year only
    /x
    

    原版的

    ^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$
    
  • 0

    如果你没有得到上述建议的工作,我使用它,因为它得到任何日期我通过50个链接运行这个表达式,它得到每个页面上的所有日期 .

    ^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$
    
  • -1
    var dtRegex = new RegExp(/[1-9\-]{4}[0-9\-]{2}[0-9\-]{2}/);
        if(dtRegex.test(date) == true){
            var evalDate = date.split('-');
            if(evalDate[0] != '0000' && evalDate[1] != '00' && evalDate[2] != '00'){
                return true;
            }
        }
    
  • 15

    此正则表达式使用匹配的分隔符验证01-01-2000和12-31-2099之间的日期 .

    ^(0[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])\2(19|20)\d\d$
    
  • 2

    正则表达式并不是要验证数字范围(当数字前面的数字恰好是2而前面的数字恰好低于6时,这个数字必须是1到5) . 只需查看正则表达式中数字位置的模式 . 如果您需要验证日期的质量,请将其放在日期对象js / c#/ vb中,并在那里插入数字 .

  • 4

    我知道这不能解答您的问题,但为什么不使用日期处理程序来检查它是否是有效日期?即使用(?!31/0?2)的负前瞻断言修改正则表达式(即,不匹配31/2或31/02),你仍然会遇到非闰年接受29 02的问题以及一个分隔符日期格式 .

    如果你想真正验证日期,问题并不容易,请检查forum thread .

    有关示例或更好的方法,请在C#中查看this link

    如果您使用其他平台/语言,请告诉我们

  • 47

    Perl 6版本

    rx{
      ^
    
      $<month> = (\d ** 1..2)
      { $<month> <= 12 or fail }
    
      '/'
    
      $<day> = (\d ** 1..2)
      {
        given( +$<month> ){
          when 1|3|5|7|8|10|12 {
            $<day> <= 31 or fail
          }
          when 4|6|9|11 {
            $<day> <= 30 or fail
          }
          when 2 {
            $<day> <= 29 or fail
          }
          default { fail }
        }
      }
    
      '/'
    
      $<year> = (\d ** 4)
    
      $
    }
    

    使用此选项检查输入后,值在 $/ 中可用,或单独作为 $<month>$<day>$<year> . (这些只是用于访问 $/ 中的值的语法)

    没有尝试检查年份,或者它与非闰年的2月29日不匹配 .

  • 1

    稍微不同的方法可能对您有用,也可能没用 .

    我在PHP .

    与此相关的项目将永远不会在2008年1月1日之前有一个日期 . 因此,我将'date'设为instuted并使用strtotime() . 如果答案是> = 1199167200,那么我有一个对我有用的日期 . 如果输入的内容看起来不像日期,则返回-1 . 如果输入null,它确实返回今天的日期编号,因此您需要首先检查非空条目 .

    适用于我的情况,也许是你的情况?

相关问题