Validating ISO 8601 Duration string with fractions

Viewed 68

This might be a bit of a tricky one, I'm trying to come up with a regular expression to validate various given date intervals against the 8601 spec (https://en.wikipedia.org/wiki/ISO_8601#Durations). I have pretty much else all the cases working with the following regular expression:

^P(\d+(?:[,.]\d+)?Y)?(\d+(?:[,.]\d+)?M)?(\d+(?:[,.]\d+)?D)?(?:T(\d+(?:[,.]\d+)?H)?(\d+(?:[,.]\d+)?M)?(\d+(?:[,.]\d+)?S)?)?$

However the one place it falls down is in the aspect of the fractional unit only being allowed on the smallest supplied unit.

So for example:

P1DT1.5H is a valid string (The above regex technically allows this) P1.5DT1H is not a valid duration, as the hours are the smallest supplied unit. P1.5DT1.5H would also not be valid.

I've reached the end of my regex skills to try and figure out a way to incorporate this into the above. Anyone have any help or guidance on how this might be achived?

1 Answers

Assuming the fractional part can only appear if there are no more digits to the right, you can use

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

See the regex demo.

The (?!.*\d[,.]\d.*\d) negative lookahead fails the match if there is a number with a fractional part followed with another number anywhere in the string.

You can learn more about the pattern used here in the Regex for ISO 8601 durations post.

Related