Регулярное выражение для продолжительности ISO 8601

Мне нужно регулярное выражение для проверки длительности в формате ISO 8601 (за исключением дробные части, которые мне не нужны).

ПнЙнМнДТнХнМнС

ПНВ

Вот что у меня есть:

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

Единственная проблема заключается в том, что в этом регулярном выражении разрешены строки P и PT, поскольку все части равны нулю или одному ?.

  • Должен быть хотя бы один компонент (дата или время)
  • Если есть 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' в элементе ввода, чтобы помочь, но, к сожалению, это не так.


person rink.attendant.6    schedule 17.08.2015    source источник
comment
Вы можете установить минимальную длину как ^(?=.{3,}$)P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$.   -  person Wiktor Stribiżew    schedule 17.08.2015
comment
@stribizhev Это будет охватывать только некоторые случаи, так как P4DT все еще можно ввести. Также есть причина для понижения?   -  person rink.attendant.6    schedule 17.08.2015
comment
@stribizhev Два очевидных уже были разделены P и PT. Я отредактировал, чтобы уточнить, пожалуйста, дайте мне знать, если я могу добавить что-нибудь еще.   -  person rink.attendant.6    schedule 17.08.2015


Ответы (4)


Если у вас есть почти все необязательные части, но вы хотите убедиться, что после P или T есть что-то еще, вы можете использовать просмотр вперед:

^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)?)?$
person Wiktor Stribiżew    schedule 17.08.2015
comment
Пожалуйста, оставьте комментарий здесь, если есть еще какие-либо проблемы. - person Wiktor Stribiżew; 17.08.2015
comment
Это очень близко, но также могут быть случаи, когда указывается только компонент времени (например, PT3H). - person rink.attendant.6; 17.08.2015
comment
Пожалуйста, смотрите мое редактирование. Я вижу, что часть P может быть пустой, но строка не может равняться P. - person Wiktor Stribiżew; 17.08.2015
comment
Может быть, вы можете уменьшить (?=\d+[HMS]) до (?=\d) - person Ulugbek Umirov; 17.08.2015
comment
@UlugbekUmirov: Да, вполне возможно, просто шаблон настолько мал, что я думаю, что производительность не будет проблемой. - person Wiktor Stribiżew; 17.08.2015
comment
только секунды не являются обязательными, чтобы иметь дробные секунды. @UlugbekUmirov, если вы указываете число, вы ДОЛЖНЫ указать [HMS]. - person WORMSS; 17.02.2020

Приведенные выше ответы не включают ситуацию с десятичной дробью (подробности см. здесь). ). Десятичная дробь может стоять на последнем элементе. Следующее регулярное выражение включает десятичную дробь:

^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)?)??$

См. здесь тесты.

person Rafi    schedule 04.11.2018

Ответы выше требуют дополнительной постобработки. /^(-?)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]
person Pavel Husakouski    schedule 20.04.2020
comment
поддерживает дробную часть только в секундах - person vavan; 14.05.2021

Если вы ищете регулярное выражение, содержащее дату начала и продолжительность, проверьте это:

^(\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)$

Ваше здоровье!

person elias    schedule 19.07.2020