PHP: Validating flexible/incomplete date time strings

Published:

Need to validate some datetime strings, that may or may not be incomplete. Might be for example just a year and a month, while the rest is unknown.

Noting it here in case I need it again. And in case someone else needs it, knows a more efficient/cleaner way, or sees a flaw...

function flexi_time($value): bool
{
    $valid = preg_match(
        '/^(?<year>\d{4})(?:-(?<month>\d{2})(?:-(?<day>\d{2})(?:[ T](?<hour>\d{2}):(?<min>\d{2})(?::(?<sec>\d{2}))?)?)?)?$/',
        $value,
        $matches);

    if( ! $valid)
        return false;

    extract($matches);

    // Check month
    if($month ?? null AND ! between($month, 1, 12))
        return false;

    // Check date
    if($day ?? null AND ! checkdate($month, $day, $year))
        return false;

    // Check hour
    if($hour ?? null AND ! between($hour, 0, 23))
        return false;

    // Check minute
    if($min ?? null AND ! between($min, 0, 59))
        return false;

    // Check second
    if($sec ?? null AND ! between($sec, 0, 59))
        return false;

    return true;
}

function between($value, $min, $max): bool
{
    return $value >= $min && $value <= $max;
}

Test

$dates = [
    'foo', // Invalid
    '17', // Invalid
    '2017',
    '2017-01',
    '2017-13', // Invalid month
    '2017-01-17',
    '2017-02-31', // Invalid date
    '2017-01-17 20', // Invalid hour without minutes
    '2017-01-17 20:00',
    '2017-01-17T20:00', // Both space and T allowed as separator
    '2017-01-17 20:00:10',
    '2017-01-17 25:00:10', // Invalid hour
    '2017-01-17 20:70:70', // Invalid minute
    '2017-01-17 20:10:70', // Invalid second
];
print_r(array_filter($dates, 'flexi_time'));
Array
(
    [2] => 2017
    [3] => 2017-01
    [5] => 2017-01-17
    [8] => 2017-01-17 20:00
    [9] => 2017-01-17T20:00
    [10] => 2017-01-17 20:00:10
)