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

有没有人有一个好的正则表达式模式来匹配 iso 日期时间?

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

93819 次浏览

对于严格的,完整的日期时间,包括毫秒,每 ABC:

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

.
实际 ISO8601: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)/

警告: 这一切变得混乱迅速,它 还是允许某些废话,如第14个月。 此外,ISO8601:2004(E)允许其他一些变体。

.
“2010-06-15 T00:00:00”是不合法的,因为它没有时区名称。

不确定它是否与您试图解决的底层问题相关,但是您可以将一个 ISO 日期字符串作为构造函数 arg 传递给 Date () ,并从中获取一个对象。构造函数实际上在将字符串强制转换为 Date 方面非常灵活。

ISO 8601规范允许各种各样的日期格式。有一个平庸的解释,如何做到这一点 给你。对于没有指定时区的简单日期,Javascript 的日期输入格式和 ISO 格式之间有一个相当小的差异,使用 字符串替换字符串替换可以很容易地减小这个差异。完全支持 ISO-8601规范是非常重要的。

下面是一个参考示例,我不能保证它是完整的,尽管它解析了前面提到的 Wikipedia 页面中的非持续时间日期。

下面是一个例子,你也可以看到它的 一号机输出。不幸的是,它不能按照规范工作,因为它没有正确地实现星期。在 ISO-8601中,第01周的定义是非常重要的,需要一些人浏览日历来确定第一周从哪里开始,以及它在指定年份的天数方面到底意味着什么。这可能很容易纠正(我只是厌倦了玩它)。

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

下面是一个检查 ISO 8601 约会格式的正则表达式,包括闰年和短长月。要运行它,您需要“忽略空白”。Regexlib 上有一个没有空白的压缩版本: http://regexlib.com/REDetails.aspx?regexp_id=3344

还有更多的 ISO 8601-这正则表达式只关心日期,但您可以轻松地扩展它,以支持时间验证,这不是那么棘手。

更新: 现在可以使用 javascript (不需要后退)

  ^(?:
(?=
[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]
)?
)
)?
)$

为了满足时间的需要,在混合物中加入以下内容(来自: 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)?)?)?

我把最上面的答案改写得更简洁一些。元素嵌套为可选语句,而不是写出三种可选模式中的每一种。

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

我很好奇这种方法有什么缺点吗?

你可以在这里找到我建议的答案的测试: http://regexr.com/3e0lh

如果只匹配 ISO 日期,比如2017-09-22,可以使用 regexp:

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

它将匹配任何数字年份、任何在00-12范围内由两位数字指定的月份以及在00-31范围内由两位数字指定的任何日期

从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

我已经创建了这个 regex 并解决了 验证日期,因为它们来自 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$

考虑:

  • 正确的符号(’-’,’T’,’:’,’.’,’Z’)放在正确的地方。
  • 与29、30或31天的月份保持一致。
  • 零点到二十三点。
  • 从00到59的分秒。
  • 从000到999毫秒。

未经考虑:

  • 闰年。

示例日期: 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");

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

天哪

对于这里的大多数答案,解释太多了,这里有一个@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])$

而且这个不关心每个月的天数,像 2020-11-31(因为11月只有30天)。

我的用例是将一个 String转换成一个 Date(从一个 API参数) ,我只需要知道输入字符串不包含奇怪的东西,我对一个实际的 Date对象进行下一个验证。

布罗克斯答案很好,但是应该以 ^ 开头,以 $结尾,以便在仅匹配日期字符串时不允许使用前缀/后缀字符。

虽然使用 QRegExp 与 IsoDateWithMs 毫秒在这里不工作。相反,以下挽救了一天。

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

(我知道这是一个 JS 条目,但它会首先弹出,对 c + + 开发人员很有帮助)