ISO 8601时长的正则表达式

15

我需要一个正则表达式来验证ISO 8601持续时间格式(不包括我不需要的小数部分)。

PnYnMnDTnHnMnS

PnW

这是我现在有的:

^P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$

唯一的问题是这个正则表达式允许字符串 PPT,因为所有部分都是“零或一”?
  • 至少需要一个组件(日期或时间)
  • 如果有一个T,则需要有一个时间组件(H、M或S)
  • 如果有一个T,则可以没有任何日期组件(Y、M或D)
  • 允许溢出(例如P72H大多等同于P3D

可接受的输入:

P1Y        // date component only
P2MT30M    // date and time components
PT6H       // time component only
P5W        // another date component

不可接受的输入:

P         // no components
PT        // no components
P3MT      // T specified but not time components

目前,无效的字符串可以通过客户端验证,但是在服务器端失败,因为它被传递到DateInteval中,但如果可能的话,我希望在客户端上失败。如果每个人都使用Chrome 40+,我可以在输入元素上指定minlength='3'来帮助,但不幸的是这并不是事实。

5个回答

25
如果您几乎所有部分都是可选的,但希望确保在PT之后还有其他内容,可以利用先行断言:
^P(?=\d+[YMWD])(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d+[HMS])(\d+H)?(\d+M)?(\d+S)?)?$
  ^^^^^^^^^^^^                               ^^^^^^^^^^^^

要求在先前出现的模式之后紧跟指定集合中的字母,需要一系列数字序列。 请查看演示更新 如果P可以为空,请使用。
^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d+[HMS])(\d+H)?(\d+M)?(\d+S)?)?$

请看另一个演示。在这里,(?!$) 确保字符串不等于 P,且右侧必须有其他符号。
或者,正如 @UlugbekUmirov 建议的那样,只需使用 T(?=\d) 即可(因为所有可选部分都以数字开头)。
^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$

更新2

如果数字可以是浮点数或整数,请在每个\d+后面添加(?:\.\d+)?。这里是来自更新1的更新后的模式:

^P(?!$)(\d+(?:\.\d+)?Y)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?W)?(\d+(?:\.\d+)?D)?(T(?=\d)(\d+(?:\.\d+)?H)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?S)?)?$

3
上述答案未包括小数部分的情况(详情请见此处)。小数部分可以出现在最后一位元素上。以下正则表达式包括小数部分:
^P(?!$)((\d+Y)|(\d+\.\d+Y$))?((\d+M)|(\d+\.\d+M$))?((\d+W)|(\d+\.\d+W$))?((\d+D)|(\d+\.\d+D$))?(T(?=\d)((\d+H)|(\d+\.\d+H$))?((\d+M)|(\d+\.\d+M$))?(\d+(\.\d+)?S)?)??$

点击这里查看测试。


1
根据this message关于ISO 8601-2:2019(en),负持续时间用减号-表示,显式正持续时间也可以用加号+表示。
P之前使用[-+]?(-|\+)?很有用。
最后,该消息还显示了来自ISO 8601-2:2019(en)的以下示例,指出持续时间的每个组件都可以带有负号。
| EXAMPLE 7 '-P2M1D' is equivalent to 'P-2M-1D'.
| EXAMPLE 8 '-P5DT10H' is equivalent to 'P-5DT-10H'.

在组件中数字前加上[-+]?,并允许在T的前瞻中加入-+,这样做符合8601-2标准。

^[-+]?P(?!$)(([-+]?\d+Y)|([-+]?\d+\.\d+Y$))?(([-+]?\d+M)|([-+]?\d+\.\d+M$))?(([-+]?\d+W)|([-+]?\d+\.\d+W$))?(([-+]?\d+D)|([-+]?\d+\.\d+D$))?(T(?=[\d+-])(([-+]?\d+H)|([-+]?\d+\.\d+H$))?(([-+]?\d+M)|([-+]?\d+\.\d+M$))?([-+]?\d+(\.\d+)?S)?)??$


0

上面的答案需要额外的后处理。 /^(-?)P(?=\d|T\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)([DW]))?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ 输出:

["P2Y9M3DT12H31M8.001S", "", "2", "9", "3", "D", "12", "31", "8.001", index: 0, input: "P2Y9M3DT12H31M8.001S", groups: undefined]

仅支持秒的小数部分。 - Vladimir Ulchenko

0
如果您正在寻找一个包含开始日期和持续时间的正则表达式,请查看以下内容:
^(\d{4}(-\d{2}(-\d{2})?(?!:))?(T\d{2}(:\d{2}(:\d{2})?(\.\d+)?)?)?(Z|([+,-]\d{2}(:\d{2})?))?)?P(([0-9]+([.,][0-9]*)?Y)?([0-9]+([.,][0-9]*)?M)?([0-9]+([.,][0-9]*)?D)?T?([0-9]+([.,][0-9]*)?H)?([0-9]+([.,][0-9]*)?M)?([0-9]+([.,][0-9]*)?S)?)|\d{4}-?(0[1-9]|11|12)-?(?:[0-2]\d|30|31)T((?:[0-1][0-9]|[2][0-3]):?(?:[0-5][0-9]):?(?:[0-5][0-9]|60)|2400|24:00)$

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接