Strtotime() считается вредным?

Кажется, что многие люди борются с проблемами времени и времени на PHP, и неизбежно многие принятые ответы, как правило, "Используйте strtotime таким образом".

Действительно ли это лучший способ направить людей, занимающихся проблемами с датами? Я начинаю чувствовать, что strtotime - это своего рода отличный трюк, на который не обязательно следует полагаться на важные вычисления даты/времени, а по характеру его на произвольные строки он кажется потенциальным источником ошибок, трудно предсказанное поведение. Его неспособность дифференцировать между MM/DD/YYYY и DD/MM/YYYY - это нечто вроде большого, не?

StackOverflow обычно очень хорош в продвижении хороших практик (я редко вижу диалог mysql_real_escape_string, который не говорит кому-то "Использовать PDO вместо этого".)

Но, похоже, не существует приемлемой нормы в вопросах даты на PHP, и многие люди отказываются от костыля strtotime.

Итак, что мы должны делать с этим, если что-нибудь вообще? Есть ли лучшая норма, которую мы должны применять для людей, задающих такие вопросы, как "Как добавить 1 неделю в X", или "Как мне преобразовать этот формат даты в этот другой формат даты?"

Какой лучший, самый надежный способ решения проблем Дата/Время, например, strtotime, но слишком часто не удается?

Ответ 1

Я начну, сказав, что я большой сторонник использования объекта DateTime, который позволяет использовать DateTime::createFromFormat() функция. Объект DateTime делает код более читаемым и избегает необходимости делать всю временную метку Unix с использованием 60 * 60 * 24 для продвижения дней дней.

Говоря это, произвольная строка, которую принимает strtotime(), предсказать не очень сложно. Поддерживаемые форматы даты и времени перечислены поддерживаемые форматы.

В соответствии с вашим примером неспособности дифференцироваться между MM/DD/YYYY и DD/MM/YYYY, он различается на основе Форматы даты. Даты, которые используют слэши, всегда читаются как американский формат. Таким образом, дата в формате 00/00/0000 всегда будет считаться MM/DD/YYYY. Альтернативно использование тире или периодов будет DMY. например 00-00-0000 всегда будет считаться DD-MM-YYYY.

Вот несколько примеров:

<?php
$dates = array(
    // MM DD YYYY
    '11/12/2013' => strtotime('2013-11-12'),
    // Using 0 goes to the previous month
    '0/12/2013' => strtotime('2012-12-12'), 
    // 31st of November (30 days) goes to 1st December
    '11/31/2013' => strtotime('2013-12-01'), 
    // There isn't a 25th month... expect false
    '25/12/2013' => false,

    // DD MM YYYY
    '11-12-2013' => strtotime('2013-12-11'),
    '11.12.2013' => strtotime('2013-12-11'),
    '31.12.2013' => strtotime('2013-12-31'),
    // There isn't a 25th month expect false
    '12.25.2013' => false,
);

foreach($dates as $date => $expected) {
    assert(strtotime($date) == $expected);
}

Как вы можете видеть, несколько ключевых примеров: 25/12/2013 и 12.25.2013, которые будут действительны, если читать их с противоположным форматом, однако они возвращают false, поскольку они недействительны согласно Форматированным форматам даты и времени...

Итак, вы видите, что поведение вполне предсказуемо. Как всегда, если вы получаете дату от использования ввода, вы должны сначала подтвердить этот ввод. Никакой метод не будет работать, если вы сначала не проверяете ввод.

Если вы хотите быть очень конкретным в отношении даты, которую вы читаете, или формат, который вы даете, не поддерживается, тогда я рекомендую использовать DateTime::createFromFormat().

Ответ 2

strtotime() предлагается, потому что он был в PHP с тех пор, как v4.x дней, поэтому в принципе гарантированно будет доступно. Доступность превосходит нечетное время (без каламбура), что он обернется и укусит вас в прикладе с неверно обработанной датой.

В текущем "правильном" способе делать математику date будут использовать объекты DateTime/DateInterval, но это более свежие дополнения к PHP (5.2/5.3, я думаю) и, следовательно, не всегда доступны - там много хостов все еще на 4.x.

Ответ 3

Я часто использую strtotime(). Дело в том, что вы не должны использовать его как костыль, но вы должны знать его ограничения.

Если вы хотите обеспечить соблюдение стандарта, я полагаю, что вы должны пойти с c-based mktime(). Например, чтобы получить 1 неделю спустя:

date('Y-m-d', mktime(0, 0, 0, date('n'), date('j') + 7);

Ответ 5

Самый надежный способ вычисления даты в PHP - использование метки времени.

Это правда, что оно ограничено 32-битным целым числом, но использование объекта Date не является опцией при наборе текста в какой-то странной среде, например, в PHP 4.

Чтобы быть уверенным, что вы получаете наиболее правильное поведение, я бы предложил использовать strptime() чтобы получить метку времени из удобочитаемой даты. Это лучше, чем strtotime() и strftime() потому что он не пытается угадать, каков формат даты, а скорее использует предоставленный вами формат даты.

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

ОБНОВЛЕНИЕ: Для PHP> = 5.3 вместо этого следует использовать date_parse_from_format(). Обратите внимание, что strptime() может работать по-разному в разных операционных системах, потому что он использует strptime() предоставляемый системной библиотекой C.

Ответ 6

Проблема заключается в том, что вы должны выбрать строгий формат данных для ваших значений даты/времени/зоны (ISO8601: 2004) на всех уровнях вашего приложения и найти функцию PHP, которая лучше всего подходит для этого формата: DateTime::createFromFormat().

IMHO, имеющий разные форматы даты/времени/зоны для пользовательского интерфейса (уровень GUI), пытаясь угадать ввод даты в функции PHP, означает, что вы не выполнили какую-либо дезинфекцию ваших данных со стороны клиента (используя Javascript, возможно,?), и вы передаете данные из своего веб-формуляра в неизвестном формате.

Если формат даты/времени/зоны не соответствует стандарту ISO8601 на стороне сервера, вы просто отклоняете его, а также voilà!

Надеюсь, что это поможет!

Ответ 7

Это, безусловно, возможно в PHP: посмотрите руководство strtotime, особенно этот комментарий.

Если у вас есть доступное соединение MySQL, SELECT DATE_ADD ('2011-05-31', INTERVAL 1 MONTH) будет менее избыточным, поскольку (правильная) функциональность уже реализована без необходимости выполнять ее самостоятельно.