Статьи

Работа с датами и временем в PHP и MySQL

При работе на любом языке программирования работа с датами и временем часто является тривиальной и простой задачей. То есть, пока не будут поддерживаться часовые пояса. К счастью, в PHP есть один из самых мощных инструментов даты / времени, которые помогут вам справиться со всеми видами проблем, связанных с временем: отметки времени Unix, форматирование дат для потребления человеком, отображение времени с часовыми поясами, разница между текущим и вторым Вторник следующего месяца и т. Д. В этой статье я познакомлю вас с основами PHP-функций времени ( time()mktime()date()

PHP Дата и Время Функции

Большая часть этой статьи будет работать со временем Unix, или POSIX или временем эпохи, как это иначе известно. Время представляется в виде смещения в количестве секунд, отсчитываемых с полуночи 1 января 1970 года по Гринвичу. Если вы заинтересованы в полной истории времени Unix, посмотрите статью о времени Unix в Википедии .

UTC, также известное под своим полным названием «Всемирное координированное время», также называемое «GMT», а иногда «зулусское время», — это время на долготе 0 градусов. Все остальные часовые пояса в мире выражены как положительные или отрицательные смещения от этого времени. Обработка времени в UTC и Unix облегчит вашу жизнь, когда вам нужно иметь дело с часовыми поясами. Я расскажу об этом позже, но давайте пока проигнорируем проблемы с часовыми поясами и рассмотрим некоторые временные функции.

Получение текущего времени Unix

time() Чтобы проиллюстрировать это, я буду использовать интерактивную оболочку CLI PHP.

  Шон @ beerhaus: ~ $ php -a
 php> время печати ();
 1324402770 

Если вам нужно представление массива времени Unix, используйте getdate() Он принимает необязательный аргумент метки времени Unix, но по умолчанию принимает значение time()

 php> $ unixTime = time ();
 php> print_r (getdate ($ unixTime));
 массив
 (
 [секунд] => 48
 [минуты] => 54
 [часы] => 12
 [mday] => 20
 [wday] => 2
 [пн] => 12
 [год] => 2011
 [yday] => 353
 [weekday] => вторник
 [месяц] => декабрь
 [0] => 1324403688
 ) 

Форматирование времени Unix

Время Unix может быть легко отформатировано практически в любую строку, которую человек захочет прочитать. date() Если дополнительная временная метка не указана, используется значение time()

  php> печатать дату ("r", $ unixTime);
 Вт, 20 дек 2011 12:54:48 -0500 

Строка форматирования «r» возвращает время, отформатированное в соответствии с RFC 2822. Конечно, вы можете использовать другие спецификаторы для определения ваших собственных пользовательских форматов.

  php> печатать дату ("m / d / yh: i: sa", $ unixTime);
 20.12.11 12:54:48
 php> распечатать дату ("м / д / ч: я: са");
 20.12.11 01:12:11
 php> печатать дату ("jS of FY", $ unixTime);
 20 декабря 2011 

Полный список допустимых символов форматирования см. На странице date() в документации PHP. Однако эта функция становится более полезной в сочетании с функциями mktime()strtotime()

Создание Unix Time из данного времени

mktime() Требуется несколько целочисленных аргументов, чтобы установить каждую часть даты в следующем порядке:

  mktime (час, минута, секунда, месяц, день, год, isDST) 

Вы устанавливаете isDST

  php> печатать дату («r», mktime (12, 0, 0, 1, 20, 1987));
 Вт, 20 января 1987 г., 12:00:00 -0500
 php> печатать дату ("r", mktime (0, 0, 0, дата ("n"), дата ("j"), дата ("Y")));
 Вт, 20 дек 2011 00:00:00 -0500
 php> печатать дату («r», mktime (23, 59, 59, дата («n»), дата («j»), дата («Y»)));
 Вт, 20 дек 2011 23:59:59 -0500 

Вы можете видеть, что mktime() Например, если вы храните временные метки как целые числа (время Unix) в MySQL (предзнаменование кого-либо?), Очень легко настроить общий диапазон запросов на текущий год.

 <?php
$startTime = mktime(0, 0, 0, 1, 1, date("y"));
$endTime   = mktime(0, 0, 0, date("m"), date("d"), date("y"));

Разбор английской даты в Unix Time

Почти волшебная функция strtotime() Смотрите документацию для приемлемых форматов даты .

  php> print strtotime ("сейчас");
 1324407707
 php> печатать дату ("r", strtotime ("сейчас"));
 Вт, 20 дек 2011 14:01:51 -0500
 php> print strtotime ("+ 1 неделя");
 1325012569
 php> печатать дату ("r", strtotime ("+ 1 неделя"));
 Вт, 27 дек 2011 14:03:03 -0500
 php> печатать дату ("r", strtotime ("следующий месяц"));
 Пт, 20 января 2012 14:04:20 -0500
 php> печатать дату ("r", strtotime ("следующий месяц", mktime (0, 0, 0)));
 Пт, 20 января 2012 00:00:00 -0500
 php> печатать дату ("r", strtotime ("следующий месяц", mktime (0, 0, 1, 31)));
 Чт, 03 марта 2011 г. 00:00:00 -0500 

Объекты PHP DateTime и DateTimeZone

PHP-объект DateTime Метод конструктора принимает строковое представление времени, очень похожее на strtotime() Значением по умолчанию, если аргумент не указан, является «сейчас».

  php> $ dt = new DateTime ("сейчас");
 php> print $ dt-> format ("r");
 Вт, 20 дек 2011 16:28:32 -0500
 php> $ dt = new DateTime ("31 декабря 1999 12:12:12 EST");
 php> print $ dt-> format ("r");
 Пт, 31 декабря 1999 12:12:12 -0500 

Метод DateTimeformat() Объекты DateTimeformat()

  php> print $ dt-> format (DATE_ATOM);
 2011-12-20T15: 57: 45-05: 00
 php> print $ dt-> format (DATE_ISO8601);
 2011-12-20T15: 57: 45-0500
 php> print $ dt-> format (DATE_RFC822);
 Вт, 20 дек. 15:57:45 -0500
 php> print $ dt-> format (DATE_RSS);
 Вт, 20 дек 2011 15:57:45 -0500 

Полный список констант можно найти на странице документации DateTime .

Поскольку мы скоро будем иметь дело с часовыми поясами, давайте предоставим PHP часовой пояс по умолчанию для использования. В вашем файле конфигурации php.ini

  [Дата]
 ;  Определяет часовой пояс по умолчанию, используемый функциями даты
 ;  http://php.net/date.timezone
 ;  date.timezone = 

Если значение date.timezone Вы можете проверить, какое значение использует PHP с помощью date_default_timezone_get()

  php> print date_default_timezone_get ();
 Америка / Триатлон 

Давайте установим часовой пояс сервера на время UTC ( date.timezone = UTC Вам нужно будет перезапустить Apache или оболочку CLI, чтобы увидеть изменения.

PHP DateTimeDateTimeZone При создании нового экземпляра DateTimeDateTimeZonephp.ini

  php> $ dt = new DateTime ();
 php> print $ dt-> getTimeZone () -> getName ();
 универсальное глобальное время 

Полный список допустимых имен часовых поясов можно найти на странице документации о часовых поясах .

Теперь вы можете видеть разницу во времени, когда двум объектам DateTime Например, вот пример, который конвертирует время из UTC в Америку / Нью-Йорк (EST).

  php> $ dt = new DateTime ();
 php> print $ dt-> format ("r");
 Вт, 20 дек 2011 20:57:45 +0000
 php> $ tz = new DateTimeZone ("America / New_York");
 php> $ dt-> setTimezone ($ tz);
 php> print $ dt-> gt; format ("r");
 Вт, 20 дек 2011 15:57:45 -0500 

Обратите внимание на смещение -0500 за декабрь. Если вы измените значение времени на летнюю дату, например, 1 июля, вы увидите, что оно знает о летнем времени (EDT).

  php> $ tz = new DateTimeZone ("America / New_York");
 php> $ july = new DateTime ("01.07.2011");
 php> $ july-> setTimezone ($ tz);
 php> print $ июль - >> формат ("r");
 Четверг, 30 июня 2011 г. 20:00:00 -0400 

Использование дат с MySQL и PHP

Если вы использовали MySQL на любом уровне, вы, вероятно, заметили тип DATETIME Это выглядит и пахнет как свидание, и если бы вы сказали, что это свидание, вы были бы правы. Но как только вы SELECT У него нет информации о часовом поясе, и он уже отформатирован для потребления человеком, прежде чем человеку когда-либо понадобится на него посмотреть

* Да, я знаю, что MySQL имеет множество функций форматирования даты, но мы уже имеем дело и с PHP, который, как вы видели, надрывается при работе с форматами даты. Зачем нам его форматировать прямо из базы данных? У нас может быть несколько разных преобразований и форматов, которые мы хотим применить к нему. Лучше форматировать дату только тогда, когда ее увидит человек.

 <?php
$db = new PDO("mysql:host=localhost;dbname=testdb", "dbuser", "dbpassword");

$result = $db->query("SELECT dt_date FROM some_table");
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
    print_r($row);
    var_dump($row["dt_date"]);
}
$result->closeCursor();

Запустив этот простой скрипт, вы увидите, что все, что у вас есть из поля DATETIME

  массив
 (
 [dt_date] => 2012-01-16 13:03:49
 )
 string (19) "2012-01-16 13:03:49" 

Значение DATETIME Если все, что вы когда-либо делаете, касается только одного сервера в одном часовом поясе, DATETIME

Итак, как вы справляетесь с датами и часовыми поясами с помощью PHP и MySQL? Храните даты как метки времени Unix.

Вы уже знаете, что время Unix — это количество секунд с 1 января 1970 года в UTC, так что это дает вам постоянный часовой пояс для работы, и, надеюсь, вы заметили, что многие функции даты / времени в PHP основаны на Unix метка времени.

При работе с MySQL я обычно создаю столбцы таблицы, в которых даты будут храниться как INTEGER UNSIGNED При вставке даты вы можете использовать либо time()UNIX_TIMESTAMP()

  mysql> SELECT UNIX_TIMESTAMP ();
 + ------------------ +
 |  UNIX_TIMESTAMP () |
 + ------------------ +
 |  1326738918 |
 + ------------------ + 

Если вы хотите, чтобы MySQL форматировал дату, вы можете. Но время будет находиться в часовом поясе сервера, и я предлагаю вам отложить любое форматирование, пока вы не достигнете уровня шаблона / представления в своем веб-приложении и не будете готовы к тому, чтобы его увидели человеческие глаза.

  mysql> SELECT FROM_UNIXTIME (UNIX_TIMESTAMP ());
 + --------------------------------- +
 |  FROM_UNIXTIME (UNIX_TIMESTAMP ()) |
 + --------------------------------- +
 |  2012-01-16 13:37:16 |
 + --------------------------------- + 

В любом приложении, охватывающем часовой пояс, у вас обычно будет таблица, в которой отслеживаются пользовательские настройки часового пояса, которые затем будут считаны в значение $_SESSION Предполагая, что у вас есть запись сеанса, которая выглядит так:

  $ _SESSION ["userTZ"] => "Америка / Чикаго" 

Вы можете легко преобразовать сохраненное время Unix (в UTC) в любую дату в часовом поясе конкретного пользователя.

 <?php
$result = $db->query("SELECT int_date FROM some_table");
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
    $dt = new DateTime();
    $tz = new DateTimeZone($_SESSION["userTZ"]); 
    $dt->setTimestamp($row["int_date"]);
    $dt->setTimezone($tz); 
    print $dt->format("r");
}

Это привело бы к дате «Пн, 16 января 2012 12:03:49 -0600». -0600 сообщает, что отстает от UTC на 6 часов, смещение 0.

Если бы мы изменили настройку часового пояса на America / Los_Angeles, получилась бы следующая дата:

  Пн, 16 Янв 2012 10:03:49 -0800 

И Америка / Нью-Йорк даст:

  Пн, 16 Янв 2012 13:03:49 -0500 

Резюме

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

Вы видели, как легко получить метку времени Unix, как отформатировать дату в любой мыслимый формат, как проанализировать английское представление даты в метке времени, как добавить период времени в метку времени, и как конвертировать между часовыми поясами. Если у статьи есть два основных момента, которые следует отнять, они будут: 1) придерживаться времени Unix и 2) придерживаться UTC в качестве базового часового пояса для всех дат при работе с PHP и MySQL.

Идея все время основываться на UTC не относится только к PHP и MySQL; это считается хорошей практикой на любом языке. И если вы когда-нибудь обнаружите, что работаете на другом языке, есть большая вероятность, что вы скажете себе: «Черт возьми, почему они не могут сделать это как PHP?»

Изображение через Якобчука Василия / Shutterstock