PHP проверяет строку даты ISO 8601

Как вы проверяете строку даты ISO 8601 (например: 2011-10-02T23:25:42Z).

Я знаю, что существует несколько возможных представлений дат ISO 8601, но меня интересует только проверка формата, который я привел в качестве примера выше.

Спасибо!


person titel    schedule 03.11.2011    source источник
comment
Это не выглядит правильно. Разве день недели не должен состоять из двух цифр?   -  person Phil    schedule 04.11.2011
comment
@Phil - я думаю, ты прав. Я обновил пост, чтобы отразить это.   -  person titel    schedule 04.11.2011


Ответы (6)


Это сработало для меня, оно использует регулярное выражение, чтобы убедиться, что дата находится в нужном вам формате, а затем пытается проанализировать дату и воссоздать ее, чтобы убедиться, что вывод соответствует вводу:

<?php

$date = '2011-10-02T23:25:42Z';
var_dump(validateDate($date));

$date = '2011-17-17T23:25:42Z';
var_dump(validateDate($date));

function validateDate($date)
{
    if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', $date, $parts) == true) {
        $time = gmmktime($parts[4], $parts[5], $parts[6], $parts[2], $parts[3], $parts[1]);

        $input_time = strtotime($date);
        if ($input_time === false) return false;

        return $input_time == $time;
    } else {
        return false;
    }
}

Вы можете использовать checkdate, чтобы убедиться, что день месяца и год также действительны.

person drew010    schedule 04.11.2011
comment
а) Не используйте флаг без учета регистра; T и Z являются единственными применимыми буквенными символами, и оба они должны быть в верхнем регистре. б) Z — не единственный допустимый указатель часового пояса. См. en.wikipedia.org/wiki/ISO_8601#Dates. - person Phil; 04.11.2011
comment
Вы также должны разрешить необязательный + или - перед строкой даты (en.wikipedia.org/ wiki/ISO_8601#Годы). наверное ([+|-]?) будет достаточно - person Luciano Mammino; 13.09.2014

Редактировать. Самый простой способ — просто попытаться создать объект DateTime с помощью строки, например

$dt = new DateTime($dateTimeString);

Если конструктор DateTime не может проанализировать строку, он выдаст исключение, например

DateTime::__construct(): не удалось проанализировать строку времени (2011-10-02T23:25:72Z) в позиции 18 (2): непредвиденный символ

Обратите внимание, что если вы пропустите указатель часового пояса, он будет использовать настроенный часовой пояс по умолчанию.

Второй самый простой способ — использовать регулярное выражение. Что-то вроде этого, чтобы прикрыть это

if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(\+|-)\d{2}(:?\d{2})?)$/', $dateString, $parts)) {
    // valid string format, can now check parts

    $year  = $parts[1];
    $month = $parts[2];
    $day   = $parts[3];

    // etc
}
person Phil    schedule 04.11.2011
comment
Хорошая идея, но конструктор DateTime не ограничивается вводом ISO 8601. - person Grayside; 01.04.2014
comment
@Grayside Я не совсем понимаю, что вы имеете в виду. Я ничего не говорил об ограничении - person Phil; 01.04.2014
comment
@ Фил хорошо, если вы хотите проверить, является ли это действительной датой iso, то, если она разрешает другие форматы даты, которые не являются iso, это не делает ее проверкой для формата iso. - person ppetermann; 26.08.2014

Я хочу поделиться своим легким решением. Это не идеально, но может быть полезно для кого-то.

function validISO8601Date($value)
{
    if (!is_string($value)) {
        return false;
    }

    $dateTime = \DateTime::createFromFormat(\DateTime::ISO8601, $value);

    if ($dateTime) {
        return $dateTime->format(\DateTime::ISO8601) === $value;
    }

    return false;
}

Внимание!

Некоторые действительные даты ISO8601 не будут выполнены Посмотрите на список ниже.

NOT VALID  --> '' // Correct
NOT VALID  --> 'string' // Correct
VALID      --> '2000-01-01T01:00:00+1200' // This is the only format function returns as valid
NOT VALID  --> '2015-01 first' // Correct
NOT VALID  --> '2000-01-01T01:00:00Z' // Must be valid!
NOT VALID  --> '2000-01-01T01:00:00+01' // Must be valid!
person Jekis    schedule 21.01.2014
comment
Это кажется наиболее чистым подходом, за исключением того, что смещение часового пояса может включать двоеточие или нет в стандарте, но функция даты использует его или нет в зависимости от того, используется ли DATE_ISO8601 или c для указания формата. - person Grayside; 01.04.2014
comment
strtotime($value); возвращает false, если $value не поддается разбору. $date = date(DATE_ISO8601, $timestamp); с $timestamp = false; содержит минимальную дату (1970-01-01T01:00:00+0100). Так что этот пример плохой. Не использовать. - person MrBoolean; 22.02.2016
comment
@MrBoolean, правильно. А затем 1970-01-01T01:00:00+0100 будет сравниваться с $value и будет возвращено false - person Jekis; 22.02.2016
comment
Джеп. Но strtotime также будет анализировать что-то вроде a day ago или 2015-01 first и многое другое. В этот момент эта функция не работает. - person MrBoolean; 22.02.2016
comment
Я исправил функцию, и теперь она корректно работает для всех типов. - person Jekis; 22.02.2016

См. http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/. Это дает это регулярное выражение для использования:

^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$

Я полагаю, что это не дает точного ответа на ваш вопрос, поскольку он будет соответствовать любой действительной дате ISO 8601, но если это нормально, то это работает отлично.

person jli    schedule 04.11.2011
comment
Это регулярное выражение даже считает допустимой датой строку типа 3401. Используйте на свой риск. - person Madhur Bhaiya; 24.04.2021

Это функция, которую я использую. Это похоже на ответ Draw010, но работает также для меток времени, заканчивающихся на «+01:00» или «-01:00».

function isTimestampIsoValid($timestamp)
{
    if (preg_match('/^'.
            '(\d{4})-(\d{2})-(\d{2})T'. // YYYY-MM-DDT ex: 2014-01-01T
            '(\d{2}):(\d{2}):(\d{2})'.  // HH-MM-SS  ex: 17:00:00
            '(Z|((-|\+)\d{2}:\d{2}))'.  // Z or +01:00 or -01:00
            '$/', $timestamp, $parts) == true)
    {
        try {
            new \DateTime($timestamp);
            return true;
        }
        catch ( \Exception $e)
        {
            return false;
        }
    } else {
        return false;
    }
}
person Dimitris S.    schedule 21.01.2014

Карбон отлично справляется с этой задачей.

use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
...
/** @test */
public function checking_iso8601_date()
{
    $this->expectException(InvalidFormatException::class);
    Carbon::createFromFormat('c', '2021-05-11 20:03:45+02:00');

    $this->assertInstanceOf(Carbon::class, Carbon::createFromFormat('c', '2021-05-11T20:03:45+02:00'));
}

Вы можете сделать то же самое с DateTime

use DateTime;
...
/** @test */
public function checking_iso8601_date()
{
    $this->assertInstanceOf(DateTime::class, DateTime::createFromFormat('Y-m-d\TH:i:sP', '2021-05-11T20:03:45+02:00'));
    $this->assertFalse(DateTime::createFromFormat('Y-m-d\TH:i:sP', '2021-05-11 20:03:45+02:00'));
}
person Tyteck    schedule 11.05.2021