/
v.bolshakov
/
AIEcosystem-Testing
Обзор
Документация
Войти
/
v.bolshakov
/
AIEcosystem-Testing
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
common/helpers/DateHelper.php
827 строк
27 KB
Developer
Initial commit
03 авг 2026, 17:43
03 авг 2026, 17:43
7433917
Код
Авторство
О чём код?
<?php namespace common\helpers; use DateTime; use DateTimeZone; use Exception; use Yii; use yii\base\InvalidConfigException; use yii\helpers\ArrayHelper; /** * Дополнительные функции для работы с датой/временем * * @author Dmitry E. Semenov <sde.tomsk@gmail.com> * @copyright Self (c) 2019 */ class DateHelper { // Second amounts for various time increments const YEAR = 31556926; const MONTH = 2629744; const WEEK = 604800; const DAY = 86400; const HOUR = 3600; const MINUTE = 60; /** * Default timestamp format for formatted_datetime * @var string */ public static $timestamp_format = 'Y-m-d H:i:s'; /** * Default timestamp format for formatted_date * @var string */ public static $date_format = 'Y-m-d'; /** * Default timestamp format for formatted_time * @var string */ public static $time_format = 'H:i:s'; /** * Вернуть текущую дату/время, установленное на сервере * @param null $timestamp * @return false|string */ public static function now($timestamp = null) { if ($timestamp) { return date(self::$timestamp_format, $timestamp); } else { return date(self::$timestamp_format); } } /** * вывести текущее время и микросекунды * @return string */ public static function microtime() { list($usec, $sec) = explode(" ", microtime()); return DateHelper::now($sec) . ' .' . $usec; } /** * Вернуть текущую дату, установленное на сервере * * @param null $timestamp * @return false|string */ public static function date($timestamp = null) { if ($timestamp) { return date(self::$date_format, $timestamp); } else { return date(self::$date_format); } } /** * Перевод данных в нужный формат * * @param $input * @param bool $is_datetime * @return null|string * @throws InvalidConfigException */ public static function parse($input, $is_datetime = false) { if ($input) { if (is_array($input)) { $array = array_diff($input, array('', null, false)); if (!$array) { return null; } $input = self::fromArray($input); } switch ((int)$is_datetime) { case 1: return Yii::$app->formatter->asDatetime($input, 'php:' . self::$timestamp_format); case 0: return Yii::$app->formatter->asDate($input, 'php:' . self::$date_format); case -1: return Yii::$app->formatter->asTime($input, 'php:' . self::$time_format); } } return null; } /** * Привести время к другой временной зоне * * @param string $datetime * @param string $currentTimeZone - текущая временная зона * @param string $targetTimeZone - временная зона к которой происходит приведение * @param null|string $format - выходной формат даты * @return string - дата в строчном виде приведенная к другой временной зоне * @throws Exception */ public static function changeTimeZone($datetime, $currentTimeZone, $targetTimeZone, $format = null) { if (!$datetime) { return null; } $format = ($format === null) ? self::$timestamp_format : $format; $datetime = new DateTime($datetime, new DateTimeZone($currentTimeZone)); $datetime->setTimezone(new DateTimeZone($targetTimeZone)); return $datetime->format($format); } /** * Расчёт смещения * * @param $datetime * @param $offset * @param null $format * @return string|null * @throws Exception */ public static function changeTime($datetime, $offset, $format = null) { if (!$datetime) { return null; } if ($offset <> 0) { $format = ($format === null) ? self::$timestamp_format : $format; $datetime = new DateTime($datetime); $datetime->add(\DateInterval::createFromDateString((-$offset) . ' seconds')); return $datetime->format($format); } else { return $datetime; } } /** * Получить смещение в секундах от UTC * * @param $datetime * @return string|null * @throws Exception */ public static function getTimeZoneOffset($datetime) { if (!$datetime) { return 0; } $datetime = new DateTime($datetime); /** @var DateTimeZone $tz */ $tz = $datetime->getTimezone(); return $tz->getOffset(new DateTime('now')); } /** * Конвертация даты в удобный для пользователя формат * * @param string $input Дата и время * @param bool $is_datetime True - если передано дата и время - False только дата * @param null $format Формат вывода * @param string $timezone Часовой пояс * @return null|string * @throws InvalidConfigException */ public static function userdate($input, $is_datetime = false, $format = null, $timezone = 'UTC') { if ($input) { if ($is_datetime) { return Yii::$app->formatter->asDatetime($input, $format ?: 'php:d.m.Y H:i:s'); } else { return Yii::$app->formatter->asDate($input, $format ?: 'php:d.m.Y'); } } return null; } /** * Возвращает начало указанного дня * @param $date * @return string */ public static function dateBegin($date) { $input = self::toArray($date); $input['hour'] = 0; $input['minute'] = 0; $input['second'] = 0; return self::fromArray($input); } /** * Возвращает конец указанного дня * @param $date * @return string */ public static function dateEnd($date) { $input = self::toArray($date); $input['hour'] = 23; $input['minute'] = 59; $input['second'] = 59; return self::fromArray($input); } /** * Получить границы текущей недели указанного дня (понедельник или воскресенье) * * @param string $date Дата * @param bool $begin Флаг - какую часть нужно посчитать * @return string */ public static function nedelyaRange($date, $begin = false) { $ts = strtotime($date); if ($begin) { return DateHelper::now(strtotime('monday this week', $ts)); } else { return DateHelper::now(strtotime('sunday this week', $ts) + self::DAY - 1); } } /** * Вернуть текущее время, установленное на сервере * * @return string */ public static function time() { return date(self::$timestamp_format, mktime(date('H'), date('i'), date('s'), 1, 1, 2000)); } /** * Функция проверяет две даты, возращает TRUE если $date_begin ранее, чем $date_end, * в противном случае возвращает FALSE * * @param int|string $date_begin - дата для сравнения * @param int|string $date_end - дата для сравнения * @param bool $soft - использовать нестрогое сравнение * @param bool $is_numeric - даты в timestamp формате * @return bool */ public static function checkTime($date_begin, $date_end, $soft = false, $is_numeric = false) { if ($is_numeric) { if ($soft) { return $date_begin <= $date_end; } else { return $date_begin < $date_end; } } else { if ($soft) { return strtotime($date_begin) <= strtotime($date_end); } else { return strtotime($date_begin) < strtotime($date_end); } } } /** * Проверяем дата окончания больше начальной даты на указанное время или нет * * @param string $date_begin * @param null|string $date_end * @param mixed|null $seconds * @return bool|int */ public static function diff($date_begin, $date_end, $seconds) { return (strtotime($date_end) - strtotime($date_begin)) >= $seconds; } /** * Проверить равны ли указанные даты * * @param $date_left * @param $date_right * @return bool */ public static function equal($date_left, $date_right) { return strtotime($date_left) == strtotime($date_right); } /** * Собрать дату из массива * @param $array * @return string */ public static function fromArray($array) { $array = array_diff((array)$array, array('', null, false)); return self::now( mktime( ArrayHelper::getValue($array, 'hour'), ArrayHelper::getValue($array, 'minute'), ArrayHelper::getValue($array, 'second'), ArrayHelper::getValue($array, 'month'), ArrayHelper::getValue($array, 'day'), ArrayHelper::getValue($array, 'year') ) ); } /** * Форматирование даты с использованием встроенных функций * * @param mixed $value Значение, которое нужно преобразовать * @param null $paths Массив ключей - которые нужно получить в ответе, NULL - если нужно вернуть все поля * @return array|bool * @throws InvalidConfigException */ public static function toArrayF($value, $paths = null) { if ($value) { $default = [ 'day' => 'd', 'month' => 'F', 'year' => 'Y', 'hour' => 'H', 'minute' => 'i', 'second' => 's', ]; if ($paths) { $keys = array_intersect_key($default, array_flip($paths)); } else { $keys = $default; } $format = 'php:' . implode('-', $keys); $path = explode('-', Yii::$app->formatter->asDatetime($value, $format)); $result = []; $i = 0; foreach ($keys as $key => $val) { $result[$key] = $path[$i]; $i++; } return $result; } else { return false; } } /** * Получить массив компонентов из строкового представления даты * @param mixed $value Значение, которое нужно преобразовать * @param null $paths Массив ключей - которые нужно получить в ответе, NULL - если нужно вернуть все поля * @return array|bool */ public static function toArray($value, $paths = null) { if ($value) { if (!is_numeric($value)) { $value = strtotime($value); } $default = [ 'day' => 'd', 'month' => 'm', 'year' => 'Y', 'hour' => 'H', 'minute' => 'i', 'second' => 's', 'week' => 'W', 'day_week' => 'N', ]; if ($paths) { $keys = array_intersect_key($default, array_flip($paths)); } else { $keys = $default; } if (is_array($keys)) { $str = date(implode(' ', $keys), $value); $path = explode(' ', $str); $result = []; $i = 0; foreach ($keys as $key => $val) { $result[$key] = $path[$i]; $i++; } return $result; } else { return false; } } else { return false; } } /** * Проверить является ли текущий год високосным * * @param int $year Год для проверки * @return bool TRUE - если указанный год високосный иначе - FALSE */ public static function isLeapYear($year) { return ($year % 4 == 0) and ($year % 100 != 0) or ($year % 400 == 0); } /** * Правим дату окончания * * @param string $end * @return false|int */ public static function fixEnd($end) { $endArray = self::toArray($end, ['hour', 'minute', 'second']); if (array_sum($endArray) == 0) { return strtotime($end) + self::DAY; } else { return strtotime($end); } } /** * Получить название интервала времени в соответствии с числителем * Допустимые значения $intervalName: 'years, months, weeks, days, hours, minutes, seconds' * * @param string $intervalName * @param integer $count * @return string */ public static function getIntervalName($intervalName, $count) { $count = (int)$count; switch ($intervalName) { case 'years': return Yii::$app->getI18n()->format('{count, plural, one{год} few{года} many{лет} other{года}}', ['count' => $count], Yii::$app->language); break; case 'months': return Yii::$app->getI18n()->format('{count, plural, one{месяц} few{месяца} many{месяцев} other{месяца}}', ['count' => $count], Yii::$app->language); break; case 'weeks': return Yii::$app->getI18n()->format('{count, plural, one{неделя} few{недели} many{недель} other{недели}}', ['count' => $count], Yii::$app->language); break; case 'days': return Yii::$app->getI18n()->format('{count, plural, one{день} few{дня} many{дней} other{дня}}', ['count' => $count], Yii::$app->language); break; case 'hours': return Yii::$app->getI18n()->format('{count, plural, one{час} few{часа} many{часов} other{часов}}', ['count' => $count], Yii::$app->language); break; case 'minutes': return Yii::$app->getI18n()->format('{count, plural, one{min} few{min} many{min} other{min}}', ['count' => $count], Yii::$app->language); break; case 'seconds': return Yii::$app->getI18n()->format('{count, plural, one{sec} few{sec} many{sec} other{sec}}', ['count' => $count], Yii::$app->language); break; default: throw new \InvalidArgumentException("Unknown interval name '$intervalName'"); } } /** * Returns time difference between two timestamps, in human readable format. * If the second timestamp is not given, the current time will be used. * Also consider using [Date::fuzzy_span] when displaying a span. * * $span = DateHelper::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2) * $span = DateHelper::span(60, 182, 'minutes'); // 2 * * @param integer $remote timestamp to find the span of * @param integer $local timestamp to use as the baseline * @param string $output formatting string * @return string when only a single output is requested * @return array associative list of all outputs requested */ public static function span($remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds') { // Normalize output $output = trim(strtolower((string)$output)); if (!$output) { // Invalid output return false; } // Array with the output formats $output = preg_split('/[^a-z]+/', $output); // Convert the list of outputs to an associative array $output = array_combine($output, array_fill(0, count($output), 0)); // Make the output values into keys extract(array_flip($output), EXTR_SKIP); if ($local === null) { // Calculate the span from the current time $local = time(); } // Calculate timespan (seconds) $timespan = abs($remote - $local); if (isset($output['years'])) { $timespan -= DateHelper::YEAR * ($output['years'] = (int)floor($timespan / DateHelper::YEAR)); } if (isset($output['months'])) { $timespan -= DateHelper::MONTH * ($output['months'] = (int)floor($timespan / DateHelper::MONTH)); } if (isset($output['weeks'])) { $timespan -= DateHelper::WEEK * ($output['weeks'] = (int)floor($timespan / DateHelper::WEEK)); } if (isset($output['days'])) { $timespan -= DateHelper::DAY * ($output['days'] = (int)floor($timespan / DateHelper::DAY)); } if (isset($output['hours'])) { $timespan -= DateHelper::HOUR * ($output['hours'] = (int)floor($timespan / DateHelper::HOUR)); } if (isset($output['minutes'])) { $timespan -= DateHelper::MINUTE * ($output['minutes'] = (int)floor($timespan / DateHelper::MINUTE)); } // Seconds ago, 1 if (isset($output['seconds'])) { $output['seconds'] = $timespan; } if (count($output) === 1) { // Only a single output was requested, return it return array_pop($output); } // Return array return $output; } /** * Выводит интервал времени между двумя датами в формате "1 год, 2 месяца, 3 недели ... 5 секунд назад" * Необходимые временные интервалы перечисляются в $output * Допустимые значения $output: 'years, months, weeks, days, hours, minutes, seconds' * * @param integer $remote - удаленное время в timestamp формате * @param null|integer $local - текущее время в timestamp формате * @param string $output - строка с перечислением временных интервалов в которых * @param bool $showZeroValues - показывать нулевые значения * @param bool $showTimeWord - добавлять слово "через" / "назад" (5 дней назад) * @return string */ public static function intervalBetweenTwoTimestamps( $remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds', $showZeroValues = false, $showTimeWord = true ) { if ($local === null) { // Calculate the span from the current time $local = time(); } $span = static::span($remote, $local, $output); //Если вернулась $span - строка, значит был передан один итервал времени if (!is_array($span)) { $spanArr[$output] = $span; $span = $spanArr; } if (!$showZeroValues) { $span = array_filter($span, function ($val) { return $val > 0; }); } $firstElem = true; $result = ''; foreach ($span as $key => $value) { if (!$firstElem) { $result .= ', '; } else { $firstElem = false; } $result .= $value . ' ' . static::getIntervalName($key, $value); } if ($showTimeWord && $remote < $local) { $result .= ' ' . Yii::t('app', 'ago'); } if ($showTimeWord && $remote > $local) { $result = Yii::t('app', 'через') . ' ' . $result; } return $result; } /** * Привести время в формате timestamp к дате в виде строки * * @param int $timestamp * @param null $format - формат возвращаемой даты * @return string */ public static function timestampToString($timestamp, $format = null) { if ($format === null) { $format = self::$timestamp_format; } return (new \DateTime('@' . $timestamp))->format($format); } /** * Если $datetime относится к сегодняшнему дню - вывести время * Если к вчерашнему - вывести "Вчера" * В ином случае - дату в формате $dateFormat * * @param string $datetime * @param string $dateFormat * @param bool $isNumeric - дата в unix формате * @param null $timeZone - временная зона, в которой приходит $datetime, по умолчанию Yii::$app->timeZone * @return string */ public static function getTimeByDifference($datetime, $dateFormat = 'php:d F', $isNumeric = false, $timeZone = null) { $datetimeOrigin = $datetime; $datetimearray = static::toArray($datetime); $today = strtotime('today midnight'); $yesterday = $today - self::DAY; $tomorrow = $today + self::DAY; if (!$isNumeric) { $today = static::timestampToString($today); $yesterday = static::timestampToString($yesterday); $tomorrow = static::timestampToString($tomorrow); // для приведения всех значений к одной временной зоне $timeZone = ($timeZone === null) ? Yii::$app->timeZone : $timeZone; $datetime = static::changeTimeZone($datetime, $timeZone, 'UTC'); } // сегодня if (static::inInreval($datetime, $datetime, $today, $tomorrow, $isNumeric)) { $the_date = self::toArray($datetimeOrigin); $zero = ($the_date['hour'] + $the_date['minute'] + $the_date['second']); if ($zero == 0) { return Yii::t('app', 'Сегодня'); } else { return Yii::$app->formatter->asTime($datetimeOrigin, 'php:H:i'); } } // вчера if (static::inInreval($datetime, $datetime, $yesterday, $today, $isNumeric)) { return Yii::t('app', 'Вчера'); } // в любой другой день if ($datetimearray['year'] == gmdate('Y')) { // текущего года return Yii::$app->formatter->asDate($datetimeOrigin, $dateFormat); } else { // отличного от текущего года return Yii::$app->formatter->asDate($datetimeOrigin, 'php: d F Y'); } } /** * Проверить входит или нет указанный интервал дат в диапазон * * @param mixed $left левая граница интервала * @param mixed $right правая граница интервала * @param mixed $interval_left левая граница диапазона * @param null $interval_right правая граница диапазона * @param bool $is_numeric * @return bool */ public static function inInreval($left, $right, $interval_left, $interval_right = null, $is_numeric = false) { if (isset($interval_right)) { // проверяем дату окончания действия return self::checkTime($interval_left, $left, true, $is_numeric) and self::checkTime($right, $interval_right, true, $is_numeric); } else { return self::checkTime($interval_left, $right, true, $is_numeric) and !self::checkTime($interval_left, $left, true, $is_numeric); } } /** * Преобразовать интервал дат в массив дат * * @param string $range Строка для разбора данных * @param int $format Формат преобразования * @return array * @throws InvalidConfigException */ public static function rangeToInterval($range, $format = 1) { $interval = explode('-', $range); if (count($interval) == 2) { return [ 'date_begin' => self::parse($interval[0], $format), 'date_end' => self::parse(self::dateEnd($interval[1]), $format) ]; } else { return null; } } /** * Является ли DateTime сегодняшним. * * @param string $datetime * @return boolean */ public static function isToday($datetime) { return self::isSameDay($datetime); } /** * Является ли указанные даты одним днём * * @param string $left левыая метка времени * @param string $right правый метка времени * @return bool */ public static function isSameDay($left, $right = null) { if ($right) { $right_d = new \DateTime($right); } else { $right_d = new \DateTime(); } $left_d = new \DateTime($left); $difference = $right_d->diff($left_d); return $difference->days == 0; } /** * Получить период в виде дат * * @param string $begin Дата начала * @param string $end Дата окончания * @param string $label Строка для формирования ответа * @return string|null */ public static function getPeriodLabel($begin, $end, $label = 'Until now', $options = []) { if ($begin) { $path[] = DateHelper::userdate($begin, ArrayHelper::getValue($options, 'begin.is_datetime')); if ($end) { if (!DateHelper::isSameDay($begin, $end)) { $path[] = DateHelper::userdate($end, ArrayHelper::getValue($options, 'end.is_datetime')); } } else { $path[] = Yii::t('app', $label); } return implode(' - ', $path); } else { return null; } } }