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?
CodePudding user response:
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.