有没有一个好的正则表达式模式可以匹配ISO日期时间?
例如:2010-06-15T00:00:00
根据 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))/
.
根据实际的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)/
警告:这一切很快就会变得混乱不堪,并且仍然允许某些无意义的情况,比如第14个月。 此外,ISO 8601:2004(E)还允许其他几种变体。
.
"2010-06-15T00:00:00" 不合法,因为它没有时区指定。
(?: )
而不是 ( )
来避免捕获一个组。例如第一个正则表达式可以写成 \d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)
。 - Thanish如果只需要匹配 ISO 格式的日期,如 2017-09-22,可以使用以下正则表达式:
\d{4}-\d{2}-\d{2}
^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$
它将匹配任何数字年份,任何在00-12范围内指定两位数字的月份和任何在00-31范围内指定两位数字的日期。2023-02-30
一样无效。最终,正则表达式不适合用于验证日期是否正确,应仅用于指示预期格式。鉴于此,一个更易于阅读但允许一些明显错误的较短格式是一个很好的折衷方案。我实际上更喜欢 ^\d{4}-\d{2}-\d{2}$
,因为它更加简洁明了。 - Caoilte我将顶部答案进行了修改,使其更加简洁。不是写出每个三个可选的模式,而是将元素嵌套为可选语句。
/[+-]?\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
[+-][0-2]\d:[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]\d)?Z?)?)?)?
- Frank\d{4}
匹配的匹配部分。 - Raddish IoW.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$
考虑到:
未考虑:
示例日期:2019-11-15T13:34:22.178Z
/^[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): /^[1-9]\d{3}-((0[13578]|1[02])-([0-2]\d|31)|([469]|11)-([0-2]\d|31)|02-[0-2]\d)T([01](0-9]|2[0-3])):[0-5]\d\.\d{3}Z$/
。请注意,我刚刚发现Node.js(使用node@19.0.1
进行测试)仅支持1000年至9999年之间的年份,因此我相应地更新了正则表达式。Firefox Nightly web控制台也是如此。 - tukusejssirs ^(?:
(?=
[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]
)?
)
)?
)$
([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)?)?)?
ISO 8601规范允许使用多种日期格式。这里有一个中等水平的解释(点击此处)。对于不指定时区的简单日期,Javascript的日期输入格式和ISO格式之间存在一些较小的差异,可以使用字符串替换轻松解决。完全支持ISO-8601规范是非常困难的。
以下是一个参考示例,虽然它可以解析来自上述维基百科页面的非持续时间日期,但我不能保证其完整性。
下面是一个示例,您还可以在ideone上查看其输出。不幸的是,它无法按照规范工作,因为它没有正确实现周。 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"))
这里大多数答案都解释得太多了,以下是@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
对象进行下一步验证。
这里是我的观点:
^\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)?$
匹配的例子:
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
使用QRegExp和IsoDateWithMs时,这里的毫秒不起作用。相反,以下方法解决了问题。
\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d{1,3}
(我知道这是一篇JS入门文章,但它出现在首位,对C++开发人员也很有帮助。)
不确定它是否与您试图解决的根本问题相关,但您可以将ISO日期字符串作为构造函数参数传递给Date(),并从中获取一个对象。构造函数在将字符串强制转换为日期方面实际上非常灵活。
/^(\d{4})-0?(\d+)-0?(\d+)[T ]0?(\d+):0?(\d+):0?(\d+)$/
,(然而这并不是最严格的一个)... 将其转换为日期对象则是另外一回事 :) - mykhal