Применение класса DateInterval

mychatik

Support
Команда форума
Регистрация
26.05.15
Сообщения
533
Реакции
466
Баллы
63
Довольно часто на сайтах возникает необходимость посчитать, сколько времени прошло от какой-либо даты.

В чате, к примеру, рассчитывается длительность нахождения пользователя в чате, время его общения и так далее...
Расчёты выполняются с максимальным значением в днях. Примерно это выглядит так:
PHP:
         $registered_time = my_time() - $current_user->registered_at;
         $r_days     = intval($registered_time / (24*3600));
         $r_hours    = intval(($registered_time - $r_days*24*3600) / 3600);
         $r_minutes  = intval(($registered_time - $r_days*24*3600 - $r_hours*3600)/60);
         $r_seconds  = intval($registered_time - $r_days*24*3600 - $r_hours*3600 - $r_minutes*60);
Для молодых чатов и малого времени регистрации это допустимо, но для нескольких лет - становится уже неудобно разбираться, сколько же лет в этих тысячах дней в статистике юзера.
Если вы захотите способом расчётов выделить из дней - годы, возникает проблема с разным количеством дней в годах, т.е. с учётом високосных лет.
Эту неточность, конечно, можно компенсировать за счёт увеличения объёма кода и, соответственно, времени выполнения скрипта.

Если смириться с погрешностью до 3 дней за 12 лет - можно применить следующий код:
PHP:
                    $registered_time = my_time() - $current_user->registered_at;
                    $r_year    = $registered_time / 31556926 % 12;
                    $r_days    = $registered_time / 86400 % 365;
                    $r_hours   = $registered_time / 3600 % 24;
                    $r_minutes = $registered_time / 60 % 60;
                    $r_seconds = $registered_time % 60;
Но через 12 лет проявится и ещё одна проблема. Годы "обнулятся" и 12 лет будет показываться, как 0.
Проблему можно частично компенсировать, добавив к коду строку:
PHP:
if ($registered_time > 378683112 ) $t_year += 12;
Но это временное решение. Если вы планируете длительную работу вашего проекта, то через следующие 12 лет ошибка повторится и её придётся компенсировать снова.

С версии php5 введён новый класс DateInterval.
Главный его плюс - это учёт високосных лет.
Кроме того, скрипты с этим классом выполняются быстрее, чем написанные с применением расчётов.
Массив основных значений DateInterval Object (вместо 0 будут значения, полученные из расчётов):
PHP:
 ( [y] => 0
   [m] => 0
   [d] => 0
   [h] => 0
   [i] => 0
   [s] => 0
   [a] => 0 )
Где: y - число лет, m - месяцев, d - дней, h - часов, i - минут и s - секунд в заданном интервале.
a - это полное количество дней в интервале (аналогично стандартному расчёту времени в чате). Принимает значение только, если объект DateInterval создан методом DateTime::diff()

Для DateTime дата дожна быть в формате YY-MM-DD, а в чате, в основном, используется формат Unix timestamp.
Но это не проблема. Для того, чтобы сообщить, что передается не дата, а метка времени UNIX - применяется знак @.
DateTime() без аргумента принимает значение текущего времени.

Вышеприведённый расчёт времени (формат: годы-месяцы-дни-часы-минуты-секунды) теперь можно записать таким образом (все переменные условны и у себя их можете назвать, как вам удобно):
PHP:
                   $date_1 = new DateTime('@'.$current_user->registered_at);
                   $date_2 = new DateTime();
                   $registered_time = $date_1->diff($date_2);
                   $r_year          = $registered_time->format('%y');
                   $r_month         = $registered_time->format('%m');
                   $r_days          = $registered_time->format('%d');
                   $r_hours         = $registered_time->format('%h');
                   $r_minutes       = $registered_time->format('%i');
                   $r_seconds       = $registered_time->format('%s');
и добавить в строку вывода переменные $r_year и $r_month (или как вы их назовёте у себя).

Можно объединить вывод количества месяцев и количества дней - в общее количество дней одного года (формат: годы-дни-часы-минуты-секунды), используя значение [a]:
PHP:
                   $date_1 = new DateTime('@'.$current_user->registered_at);
                   $date_2 = new DateTime();
                   $registered_time = $date_1->diff($date_2);
                   $r_year          = $registered_time->format('%y');
                   $ra_days         = $registered_time->format('%a');
                   $r_hours         = $registered_time->format('%h');
                   $r_minutes       = $registered_time->format('%i');
                   $r_seconds       = $registered_time->format('%s');
                   $r_days          = round($ra_days-365.24*$r_year);
и добавить в строку вывода только одну переменную $r_year.

Использовав только значение [y], к примеру, можно легко написать функцию расчёта возраста пользователя по его дате рождения.

Если же нужно разложить не разницу дат, а только одно значение timestamp (например, время нахождения юзера онлайн, когда разница уже посчитана и хранится в переменной)
PHP:
                   $date = new DateTime('@'.$current_user->online_time);
                   $r_days           = $date->format('z');
                   $r_hours          = $date->format('G');
                   $r_minutes        = $date->format('i');
                   $r_seconds        = $date->format('s');
В данном случае, вывод будет аналогичен стандартному в чате (дни-часы-минуты-секунды), так как годы разговоров пользователи наберут не скоро... :)))
Форматирующие символы, используемые для функции date_format() можно посмотреть ЗДЕСЬ

DateInterval, кроме вышеперечисленного, может работать с временными зонами, производить сравнение объектов DateTime...
С версии php7 добавлены дополнительные свойства в DateInterval Object.
Всё это можно прочитать в документации по данному классу.

P.S. Все вышеприведённые способы были опробованы на работоспособность и работают в моём чате.
 

mychatik

Support
Команда форума
Регистрация
26.05.15
Сообщения
533
Реакции
466
Баллы
63
Ещё одна возможность DateTime:
Если вам для расчётов нужно получать какую-то дату текущего года (к примеру, 20 февраля), пишем так:
PHP:
$date = DateTime::createFromFormat('-m-d', '-02-20');
 
Сверху Снизу