用于验证正确的 ISO8601 日期字符串的正则表达式

RegEx for validating correct ISO8601 date string

例如:2013-08-11T17:22:04.51+01:00

在这个 中涵盖没有 .51 部分的 ISODateTime。

请帮助更正此正则表达式

^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:Z|[+-][01]\d:[0-5]\d)$

处理我的格式。

这个正则表达式应该可以完成工作:

^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?([0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$

参考。 https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/

使用捕获组,您可以简单地设计一个表达式来捕获您希望从输入中捕获的任何内容。例如this expression,

(\d{4}-\d{2}-\d{2})[A-Z]+(\d{2}:\d{2}:\d{2}).([0-9+-:]+)

将您的输入分成三个捕获组,您可以使用 $1-$3 简单地调用它们。

您还可以在 [] 中添加您可能想要的任何字符。

正则表达式描述图

此图显示了表达式的工作原理,您可以在此 link:

中可视化其他表达式

Java 测试

import java.util.regex.Matcher;
import java.util.regex.Pattern;

final String regex = "(\d{4}-\d{2}-\d{2})[A-Z]+(\d{2}:\d{2}:\d{2}).([0-9+-:]+)";
final String string = "2013-08-11T17:22:04.51+01:00";
final String subst = "\1 \2 \3";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);

// The substituted value will be contained in the result variable
final String result = matcher.replaceAll(subst);

System.out.println("Substitution result: " + result);

Java脚本演示

const regex = /(\d{4}-\d{2}-\d{2})[A-Z]+(\d{2}:\d{2}:\d{2}).([0-9+-:]+)/gm;
const str = `2013-08-11T17:22:04.51+01:00`;
const subst = `\nGroup 1: \nGroup 2: \nGroup 3: \n`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

基本性能测试

此 Java 脚本片段显示了使用简单的 100 万次 for 循环的表达式性能。

const repeat = 1000000;
const start = Date.now();

for (var i = repeat; i >= 0; i--) {
 const string = '2013-08-11T17:22:04.51+01:00';
 const regex = /(\d{4}-\d{2}-\d{2})[A-Z]+(\d{2}:\d{2}:\d{2}).([0-9+-:]+)/gm;
 var match = string.replace(regex, "\nGroup #1:  \n Group #2:  \n Group #3:  \n");
}

const end = Date.now() - start;
console.log("YAAAY! \"" + match + "\" is a match  ");
console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test.  ");

正则表达式有时很方便,但通常难以阅读并且(如您所见)难以调试。 Java 具有 built-in ISO 8601 格式的解析和验证,它接受带和不带小数的字符串,如 .51。我知道您问这个问题是因为您需要通过 javax.validation 进行验证,这需要一个正则表达式。所以只针对其他读者:选择很明显:不要在这里使用正则表达式。

    try {
        OffsetDateTime.parse("2013-08-11T17:22:04.51+01:00");
        System.out.println("Valid ISO 8601");
    } catch (DateTimeParseException e) {
        System.out.println("Not valid ISO 8601");
    }

Valid ISO 8601

警告:OffsetDateTime.parse 仍然不接受 ISO 8601 的 所有 变体,但比您的正则表达式接受的变体多得多。