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

Похоже, что многие люди борются с проблемами даты / времени на PHP, и неизбежно многие принятые ответы имеют тенденцию « Использовать strtotime таким образом ».

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

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

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

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

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

Я начну, сказав, что я большой сторонник использования объекта 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() .

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

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

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

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

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

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

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

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

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

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

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

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

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

Дерек Ретанс поговорил на Froscon 2010 в разделе «Расширенная обработка даты и времени в PHP» .

Найдите слайды в формате PDF по адресу http://derickrethans.nl/talks/time-froscon10.pdf . Они полагаются на недавний PHP (5.2 и выше), использование которых также должно быть лучшей практикой.

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

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