简体   繁体   English

用于匹配 ISO 8601 日期时间字符串的正则表达式

[英]Regex to match an ISO 8601 datetime string

does anyone have a good regex pattern for matching iso datetimes?有没有人有一个很好的正则表达式模式来匹配 iso 日期时间?

ie: 2010-06-15T00:00:00即:2010-06-15T00:00:00

For the strict, full datetime, including milliseconds, per the W3C's take on the spec .:对于严格的、完整的日期时间,包括毫秒,根据W3C 对规范的看法。:

//-- Complete precision:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/

//-- No milliseconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/

//-- No Seconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/

//-- Putting it all together:
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/

. .
Additional variations allowed by the actual ISO 8601:2004(E) doc :实际ISO 8601:2004(E) 文档允许的其他变化:

/********************************************
**    No time-zone varients:
*/
//-- Complete precision:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+/

//-- No milliseconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d/

//-- No Seconds:
/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d/

//-- Putting it all together:
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+)|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d)|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d)/

WARNING: This all gets messy fast, and it still allows certain nonsense such as a 14th month.警告:这一切很快就会变得混乱,它仍然允许某些废话,例如第 14 个月。 Additionally, ISO 8601:2004(E) allows a several other variants.此外,ISO 8601:2004(E) 允许多种其他变体。

. .
"2010-06-15T00:00:00" isn't legal, because it doesn't have the time-zone designation. "2010-06-15T00:00:00" 不合法,因为它没有时区指定。

For matching just ISO date, like 2017-09-22, you can use this regexp:为了匹配 ISO 日期,比如 2017-09-22,你可以使用这个正则表达式:

^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$

It will match any numeric year, any month specified by two digits in range 00-12 and any date specified by two digits in range 00-31它将匹配任何数字年份、由 00-12 范围内的两位数字指定的任何月份以及由 00-31 范围内的两位数字指定的任何日期

I reworked the top answer into something a bit more concise.我将最佳答案改写为更简洁的内容。 Instead of writing out each of the three optional patterns, the elements are nested as optional statements.不是写出三个可选模式中的每一个,而是将元素嵌套为可选语句。

/[+-]?\\d{4}(-[01]\\d(-[0-3]\\d(T[0-2]\\d:[0-5]\\d:?([0-5]\\d(\\.\\d+)?)?[+-][0-2]\\d:[0-5]\\dZ?)?)?)?/

I'm curious if there are downsides to this approach?我很好奇这种方法是否有缺点?

You can find tests for my suggested answer here: http://regexr.com/3e0lh你可以在这里找到我建议的答案的测试: http : //regexr.com/3e0lh

Here is a regular expression to check ISO 8601 date format including leap years and short-long months.这是检查 ISO 8601日期格式的正则表达式,包括闰年和短长月。 To run this, you'll need to "ignore white-space".要运行它,您需要“忽略空白”。 A compacted version without white-space is on regexlib: http://regexlib.com/REDetails.aspx?regexp_id=3344 regexlib 上有一个没有空格的压缩版本: http: //regexlib.com/REDetails.aspx?regexp_id=3344

There's more to ISO 8601 - this regex only cares for dates, but you can easily extend it to support time validation which is not that tricky. ISO 8601 还有更多内容 - 这个正则表达式只关心日期,但您可以轻松扩展它以支持时间验证,这并不那么棘手。

Update: This works now with javascript (without lookbehinds)更新:这现在适用于 javascript(没有lookbehinds)

  ^(?:
      (?=
            [02468][048]00
            |[13579][26]00
            |[0-9][0-9]0[48]
            |[0-9][0-9][2468][048]
            |[0-9][0-9][13579][26]              
      )

      \d{4}

      (?:

        (-|)

        (?:

            (?:
                00[1-9]
                |0[1-9][0-9]
                |[1-2][0-9][0-9]
                |3[0-5][0-9]
                |36[0-6]
            )
            |
                (?:01|03|05|07|08|10|12)
                (?:
                  \1
                  (?:0[1-9]|[12][0-9]|3[01])
                )?            
            |
                (?:04|06|09|11)
                (?:
                  \1
                  (?:0[1-9]|[12][0-9]|30)
                )?            
            |
                02
                (?:
                  \1
                  (?:0[1-9]|[12][0-9])
                )?

            |
                W(?:0[1-9]|[1-4][0-9]|5[0-3])
                (?:
                  \1
                  [1-7]
                )?

        )            
      )?
  )$
  |
  ^(?:
      (?!
            [02468][048]00
            |[13579][26]00
            |[0-9][0-9]0[48]
            |[0-9][0-9][2468][048]
            |[0-9][0-9][13579][26]              
      )

      \d{4}

      (?:

        (-|)

        (?:

            (?:
                00[1-9]
                |0[1-9][0-9]
                |[1-2][0-9][0-9]
                |3[0-5][0-9]
                |36[0-5]
            )
            |
                (?:01|03|05|07|08|10|12)
                (?:
                  \2
                  (?:0[1-9]|[12][0-9]|3[01])
                )?

            |
                (?:04|06|09|11)
                (?:
                  \2
                  (?:0[1-9]|[12][0-9]|30)
                )?
            |
                (?:02)
                (?:
                  \2
                  (?:0[1-9]|1[0-9]|2[0-8])
                )?
            |
                W(?:0[1-9]|[1-4][0-9]|5[0-3])
                (?:
                  \2
                  [1-7]
                )?
       ) 
    )?
)$

To cater for time, add something like this to the mixture (from: http://underground.infovark.com/2008/07/22/iso-date-validation-regex/ ):为了满足时间,在混合物中添加类似的东西(来自: http : //underground.infovark.com/2008/07/22/iso-date-validation-regex/ ):

([T\s](([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)?(\15([0-5]\d))?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?

The ISO 8601 specification allows a wide variety of date formats. ISO 8601规范允许使用多种日期格式。 There's a mediocre explanation as to how to do it here .这里有一个关于如何做到这一点的平庸解释。 There is a fairly minor discrepancy between how Javascript's date input formatting and the ISO formatting for simple dates which do not specify timezones, and it can be easily mitigated using a string substitution . Javascript 的日期输入格式与不指定时区的简单日期的 ISO 格式之间存在相当小的差异,并且可以使用字符串替换轻松缓解。 Fully supporting the ISO-8601 specification is non-trivial.完全支持 ISO-8601 规范并非易事。

Here is a reference example which I do not guarantee to be complete, although it parses the non-duration dates from the aforementioned Wikipedia page.这是一个参考示例,我不保证它是完整的,尽管它解析了上述 Wikipedia 页面中的非持续时间。

Below is an example, and you can also see it's output on ideone .下面是一个示例,您还可以在 ideone 上看到它的输出 Unfortunately, it does not work to specification as it does not properly implement weeks.不幸的是,它不符合规范,因为它没有正确实施几周。 The definition of the week number 01 in ISO-8601 is non-trivial and requires some browsing the calendar to determine where week one begins, and what exactly it means in terms of the number of days in the specified year. ISO-8601 中周数 01 的定义非常重要,需要浏览日历来确定第一周从哪里开始,以及它在指定年份的天数方面的确切含义。 This can probably be fairly easily corrected (I'm just tired of playing with it).这可能很容易纠正(我只是厌倦了玩它)。

function parseISODate (input) {
    var iso = /^(\d{4})(?:-?W(\d+)(?:-?(\d+)D?)?|(?:-(\d+))?-(\d+))(?:[T ](\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?)?(?:Z(-?\d*))?$/;

    var parts = input.match(iso);

    if (parts == null) {
        throw new Error("Invalid Date");
    }

    var year = Number(parts[1]);

    if (typeof parts[2] != "undefined") {
        /* Convert weeks to days, months 0 */
        var weeks = Number(parts[2]) - 1;
        var days  = Number(parts[3]);

        if (typeof days == "undefined") {
            days = 0;
        }

        days += weeks * 7;

        var months = 0;
    }
    else {
        if (typeof parts[4] != "undefined") {
            var months = Number(parts[4]) - 1;
        }
        else {
            /* it's an ordinal date... */
            var months = 0;
        }

        var days   = Number(parts[5]);
    }

    if (typeof parts[6] != "undefined" &&
        typeof parts[7] != "undefined")
    {
        var hours        = Number(parts[6]);
        var minutes      = Number(parts[7]);

        if (typeof parts[8] != "undefined") {
            var seconds      = Number(parts[8]);

            if (typeof parts[9] != "undefined") {
                var fractional   = Number(parts[9]);
                var milliseconds = fractional / 100;
            }
            else {
                var milliseconds = 0
            }
        }
        else {
            var seconds      = 0;
            var milliseconds = 0;
        }
    }
    else {
        var hours        = 0;
        var minutes      = 0;
        var seconds      = 0;
        var fractional   = 0;
        var milliseconds = 0;
    }

    if (typeof parts[10] != "undefined") {
        /* Timezone adjustment, offset the minutes appropriately */
        var localzone = -(new Date().getTimezoneOffset());
        var timezone  = parts[10] * 60;

        minutes = Number(minutes) + (timezone - localzone);
    }

    return new Date(year, months, days, hours, minutes, seconds, milliseconds);
}

print(parseISODate("2010-06-29T15:33:00Z-7"))
print(parseISODate("2010-06-29 06:14Z"))
print(parseISODate("2010-06-29T06:14Z"))
print(parseISODate("2010-06-29T06:14:30.2034Z"))
print(parseISODate("2010-W26-2"))
print(parseISODate("2010-180"))

I have made this regex and solves the validation for dates as they come out of Javascript's .toISOString() method.我已经制作了这个正则表达式并解决了日期验证,因为它们来自 Javascript 的.toISOString()方法。

^[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]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\\.[0-9]{3}Z$

Contemplated:考虑:

  • Proper symbols ('-', 'T', ':', '.', 'Z') in proper places.在适当的位置使用适当的符号('-'、'T'、':'、'.'、'Z')。
  • Consistency with months of 29, 30 or 31 days.与 29、30 或 31 天的月份保持一致。
  • Hours from 00 to 23.小时从 00 到 23。
  • Minutes and seconds from 00 to 59.从 00 到 59 的分和秒。
  • Milliseconds from 000 to 999.从 000 到 999 的毫秒数。

Not contemplated:未考虑:

  • Leap years.闰年。

Example date: 2019-11-15T13:34:22.178Z示例日期: 2019-11-15T13:34:22.178Z

Example to run directly in Chrome console: /^[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]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\\.[0-9]{3}Z$/.test("2019-11-15T13:34:22.178Z");直接在 Chrome 控制台中运行的示例: /^[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]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\\.[0-9]{3}Z$/.test("2019-11-15T13:34:22.178Z");

Regex flow diagram ( Regexper ):正则表达式流程图( Regexper ): 正则表达式流程图

yyyy-MM-dd yyyy-MM-dd

Too much explanation for most of the answers here, here's a short variation of @Sergey answer addressing some weird scenarios (like 2020-00-00 ), this RegExp only cares about the yyyy-MM-dd date:对这里的大多数答案的解释太多,这是@Sergey答案的简短变体,用于解决一些奇怪的情况(如2020-00-00 ),这个RegExp只关心yyyy-MM-dd日期:

// yyyy-MM-dd
^\d{4}-([0][1-9]|1[0-2])-([0-2][1-9]|[1-3]0|3[01])$

Also this one doesn't care about the number of days per month, like 2020-11-31 (because November has only 30 days).此外,这个也不关心每月的天数,例如2020-11-31 (因为 11 月只有 30 天)。

My use-case was to convert a String into a Date (from an API param) and I needed only to know that the input string didn't contained strange stuff, I do the next validation against an actual Date object.我的用例是将String转换为Date (来自API参数),我只需要知道输入字符串不包含奇怪的东西,我对实际的Date对象进行下一次验证。

从 1900 年到 2999 年的 02/29 验证

 (((2000|2400|2800|((19|2[0-9])(0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30)))T([01][0-9]|[2][0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z

Brocks answers are good, but should start with ^ and end with $ so as not to allow prefix/suffix characters if all you are trying to match is the date string alone. Brocks 的答案很好,但应该以 ^ 开头并以 $ 结尾,以便在您尝试匹配的只是日期字符串时不允许使用前缀/后缀字符。

While using QRegExp with IsoDateWithMs the millisecond ones here did not work.在将 QRegExp 与 IsoDateWithMs 一起使用时,这里的毫秒数不起作用。 instead the following saved the day.相反,以下挽救了这一天。

\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d{1,3}

(I know this is a JS entry but it pops up first and would be helpful for c++ devs) (我知道这是一个 JS 条目,但它首先弹出,对 c++ 开发人员有帮助)

Here is my take on this:这是我对此的看法:

^\d{4}-(?:0[1-9]|1[0-2])-(?:[0-2][1-9]|[1-3]0|3[01])T(?:[0-1][0-9]|2[0-3])(?::[0-6]\d)(?::[0-6]\d)?(?:\.\d{3})?(?:[+-][0-2]\d:[0-5]\d|Z)?$

Examples for a match:匹配示例:

2016-12-31T23:59:60+12:30
2021-05-10T09:05:12.000Z
3015-01-01T23:00+02:00
1001-01-31T23:59:59Z
2023-12-20T20:20

The minutes and seconds part could be refined more, but this is good enough for me.分钟和秒部分可以更细化,但这对我来说已经足够了。

Regexper 正则表达式

在此处输入图像描述

Not sure if it's relevant to the underlying problem you are trying to solve, but you can pass an ISO date string as a constructor arg to Date() and get an object out of it.不确定它是否与您尝试解决的潜在问题相关,但是您可以将 ISO 日期字符串作为构造函数 arg 传递给 Date() 并从中获取一个对象。 The constructor is actually very flexible in terms of coercing a string into a Date.构造函数实际上在将字符串强制转换为日期方面非常灵活。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM