fix: Support wider range of ISO 8601 period strings

Signed-off-by: Sebastian Fey <info@sebastianfey.de>
This commit is contained in:
Sebastian Fey 2023-10-30 21:46:44 +01:00
Родитель 8377b31dd2
Коммит 540e378df6
1 изменённых файлов: 20 добавлений и 4 удалений

Просмотреть файл

@ -42,14 +42,30 @@ class ISO8601DurationHelper {
throw new InvalidDurationException($this->l->t('Could not parse duration {duration}', ['duration' => $duration]));
}
/**
* Parses the string $duration and checks if it has a valid ISO 8601 duration format. Otherwise throws
* InvalidDurationException.
*
* For reference of ISO 8601, see <a href="https://en.wikipedia.org/wiki/ISO_8601">Wikipedia</a>.
* @param string $duration
* @return string
* @throws InvalidDurationException if $duration does not comply to ISO 8601.
*/
private function parseIsoFormat(string $duration): string {
$pattern = '/^PT(\d+)H(?:(\d+)M(?:(\d+)S)?)?$/';
// P (for period) denotes the duration and must be at the start.
// The single parts are optional, but the lookahead (?=\d) ensures that there is some time (digit) given.
// Years, months, and days are improbable for recipes but so what.. ;) That being said: It's not supported.
$pattern = '/^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/';
$ret = preg_match($pattern, trim($duration), $matches);
if ($ret === 1) {
$hours = (int)$matches[1];
$minutes = (int) ($matches[2] ?? 0);
$seconds = (int) ($matches[3] ?? 0);
// $matches[0] is the complete string (like P1Y2M3DT4H5M6S)
// $matches[1] to $matches[3] is years to days (like 1Y 2M 3D)
// $matches[5] is the tome part of the string (like T4H5M6S)
// $matches[6] to $matches[8] is hours to seconds (like 4H 5M 6S)
$hours = (int)$matches[6];
$minutes = (int) ($matches[7] ?? 0);
$seconds = (int) ($matches[8] ?? 0);
while ($seconds >= 60) {
$seconds -= 60;