Полезные функции для работы с датой на PHP

Вывод информации об оставшемся времени на PHP

Как вывести кол-во оставшегося времени на PHP, например: "до Нового Года осталось 11 месяцев"? Т.е. не подсчитать кол-во месяцев, а вывести слово "месяц" в правильном падеже? Эту задачу решит функция, опубликованная ниже:


function time_left_text($time, $period)
  {
  // http://petrenco.com/php.php?txt=122
  $arr['s'] = array('секунда', 'секунды', 'секунд');
  $arr['m'] = array('минута', 'минуты', 'минут');
  $arr['h'] = array('час', 'часа', 'часов');
  $arr['d'] = array('день', 'дня', 'дней');
  $arr['M'] = array('месяц', 'месяца', 'месяцев');
  $arr['Y'] = array('год', 'года', 'лет');
  $arr['Y2'] = array('года', 'лет', 'лет');
  $time_abs = abs($time);  
 
  $time_abs = intval(substr($time_abs, -2));
  if ($time_abs > 19)
    $time_abs = intval(substr($time_abs, -1));
   
  $sec_arr[0] = array(2,3,4);
  $sec_arr[1] = array(0,5,6,7,8,9);
  if ($time_abs == 1)
    $ret = $arr[$period][0];
  else if (in_array($time_abs, $sec_arr[0]))
    $ret = $arr[$period][1];
  else if (($time_abs >= 5 AND $time_abs <= 20) OR $time_abs == 0)
    $ret = $arr[$period][2];

  return $ret;
  }
 
  • $time - величина времени, целое число;
  • $period - название периода времени: s - секунда, m - минута, d - день, M - месяц, Y - год, Y2 - год в немного другом формате.

Отличия Y от Y2 в том, что Y применяется только к количественным числительным в именительном падеже: "Моему брату 22 года". Y2 применяется в таких конструкциях: "Моему брату еще нет 22 лет". Подробнее, о правилах русского языка, касающихся применения слов "год" и "лет" можно узнать тут: "Использование слов "лет" и "год"". Эти же правила, применимы и для других названий величин времени: секунда, минута и др., но в функции используются только наиболее часто встречающиеся склонения.

На выходе этой функции - строковое значение. Например:

echo time_left_text(1535, 'd'); // дней
echo time_left_text(251, 's'); // секунда
echo time_left_text(22, 'Y'); // года
echo time_left_text(22, 'Y2'); // лет

Приведение даты к стандарту столбца типа date MySQL

Как известно, любые пришедшие от пользователя данные, необходимо скрупулезно проверять на соответствие ожидаемым средствами PHP. Привести дату 20-1-2 к требуемому БД MySQL строковому представлению 0020-01-02 поможет функция sprintf().

echo sprintf('%04d-%02d-%02d', 2012, 2, 30); // 2012-02-30
echo sprintf('%04d-%02d-%02d', 20, 01, 02); // 0020-01-02

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

echo date('Y-m-d', strtotime('2012-2-30')); // 2012-03-01
echo date('Y-m-d', strtotime('20-1-2')); // 2020-01-02

Но вот только неконтролируемое изменение данных (не существующая дата 30 февраля приводится к ближайшей корректной дате 1 марта и 20 год изменяется на 2020) далеко не всегда приемлемо. Например, если пользователь ввел информацию о том, что он родился 30 февраля, лучше обнаружить ошибку и вывести предупреждение, чем изменить дату рождения на 1 марта.

Проверка даты и привидение к формату MySQL

Функция $result = check_datetime($datetime, $add, $date_min, $date_max, $out_forma) принимает дату в формате, приближенном к формату MySQL, проверяет ее и возвращает результат: ошибка ($result['err']) и (либо) дату ($result['datetime']) в точном соответствии со стандартами MySQL.

  • $datetime - дату в функцию можно передавать в полном (2012-03-02 20:50:01) или сокращенном формате (2012-03). Минимальное принимаемое значение - год, состоящий из 1 цифры. Дата и время могут корректно проверяться и без ведущих нулей, например проверив такую дату: 1001-3-2 2:3:1, на выходе функции она будет выглядеть так: 1001-03-02 02-03-01.
  • $add - дополнить дату максимальными или минимальными значениями. Допустимые значения: min (по умолчанию) или max. Например, передав дату 2012-02 и установив $add = max, на выходе функция вернет 2012-02-29 23:59:59. При $add = min дата 2012-02 преобразуется в 2012-02-01 00:00:00. Этот параметр очень удобно использовать, когда нужно запросить диапазон дат, например от 2010 до 2012 года. В этом случае, для проверки поля "от" устанавливается $add = min, а для "до", - $add = max, в результате чего на выходе функции будут такие даты: для 2010 г.: 2010-01-01 00:00:00; для 2012г.: 2012-12-31 23:59:59.
  • $date_min и $date_max - минимально и максимально допустимые даты в полном MySQL формате (например 2012-03-02 20:50:01). Также, в качестве значения можно установить ключевое слово now, в этом случае будет использована текущая дата. Если дата, отправленная на проверку, будет менее или более дат, указанных в этих параметрах - функция вернет ошибку. Если эти параметры не указаны - минимальная и максимальная даты будут, соответственно, -730 и 730 дней от даты на момент проверки. В официальной документации к MySQL сказано, что для столбца таблицы с типом datetime, допустимыми являются значения от 1000-00-00 00:00:00 до 9999-12-12 23:59:59 - эту информацию следует принять во внимание при установке граничных порогов дат.
  • $out_format - формат даты, возвращаемый функцией: datetime (ГГГГ-ММ-ДД чч:мм:сс) или date (ГГГГ-ММ-ДД).

Результат функции - массив, с двумя ключами: [err] и [datetime]:

  • [datetime] - дата в формате MySQL date или datetime, в зависимости от установленной переменной $out_format. Дата возвращается всегда, даже в случае ошибки (если проверяемая дата некорректна, возвращается дата 0000-00-00 00:00:00).
  • [err] - текстовое сообщение об ошибке.

Проверить, есть ли ошибка (присутствие ключа [err]) в возвращаемом результате, можно с помощью функций: isset($result['err']) или array_key_exist('err', $result).

Эта функция требует наличия функции mysqldate_to_mktime($datetime), код которой опубликован на этой странице.

// ДАТА - ПРОВЕРКА И ПРИВЕДЕНИЕ К ФОРМАТУ MYSQL
// http://petrenco.com/php.php?txt=122
//
// $datetime ..... - дата, в формате MySQL гггг[-мм[-дд[ чч[:мм[:сек]]]]]
// $add .......... - добавить недостающие части даты и времени. min или max (из 2011 -> min:2011-01-01 00:00:00 или max:2011-12-12 23:59:59)
// $date_min ..... - минимально возможная дата, now - теперешняя дата
// $date_max ..... - максимально возможная дата, now - теперешняя дата
// $out_format ... - выходящий формат даты: datetime или date
function check_datetime($datetime, $add = 'min', $date_min = '', $date_max = '', $out_format = 'datetime')
  {
  $add = ($add === 'max') ? 'max' : 'min';
  if ($out_format === 'datetime')
    $ret['datetime'] = '0000-00-00 00:00:00';
  else
    $ret['datetime'] = '0000-00-00';

  if ($date_min === 'now')
    $date_min = date("Y-m-d H:i:s");
  if ($date_max === 'now')
    $date_max = date("Y-m-d H:i:s");
   
  if (!$date_min)
    $date_min_mkt = time() - 47304000; // 730 дней
  else
    $date_min_mkt = mysqldate_to_mktime($date_min);
  if (!$date_max)
    $date_max_mkt = time() + 47304000; // 730 дней
  else
    $date_max_mkt = mysqldate_to_mktime($date_max);
  $variant = 0;
  $pattern[1] = '!^[\d]{1,4}[-]{1}[\d]{1,2}[-][\d]{1,2}[ ]{1}[\d]{1,2}[:]{1}[\d]{1,2}[:]{1}[\d]{1,2}$!';
  $pattern[2] = '!^[\d]{1,4}[-]{1}[\d]{1,2}[-][\d]{1,2}[ ]{1}[\d]{1,2}[:]{1}[\d]{1,2}$!';
  $pattern[3] = '!^[\d]{1,4}[-]{1}[\d]{1,2}[-][\d]{1,2}[ ]{1}[\d]{1,2}$!';
  $pattern[4] = '!^[\d]{1,4}[-]{1}[\d]{1,2}[-][\d]{1,2}$!';
  $pattern[5] = '!^[\d]{1,4}[-]{1}[\d]{1,2}$!';
  $pattern[6] = '!^[\d]{1,4}$!';
  $tamplate[1]['min'] = '';
  $tamplate[2]['min'] = ':00';
  $tamplate[3]['min'] = ':00:00';
  $tamplate[4]['min'] = ' 00:00:00';
  $tamplate[5]['min'] = '-01 00:00:00';
  $tamplate[6]['min'] = '-01-01 00:00:00';
  $tamplate[1]['max'] = '';
  $tamplate[2]['max'] = ':59';
  $tamplate[3]['max'] = ':59:59';
  $tamplate[4]['max'] = ' 23:59:59';
  $tamplate[5]['max'] = '-31 23:59:59';
  $tamplate[6]['max'] = '-12-31 23:59:59';
  foreach ($pattern AS $key => $val)
    {
    if (preg_match($val, $datetime))
      {
      $variant = $key;
      }
    }
  if ($variant == 0)
    {
    $ret['err'] = 'формат даты не верен, (пример даты в необходимом формате: '.date("Y-m-d").')';
    return  $ret;
    }
  $datetime = $datetime.$tamplate[$variant][$add];
  $datetime_explode = explode(' ', $datetime);
  $datetime_explode['date'] = explode('-', $datetime_explode[0]);
  $datetime_explode['date'][2] = ($variant >= 5 AND $add === 'max') ? cal_days_in_month(CAL_GREGORIAN, $datetime_explode['date'][1], $datetime_explode['date'][0]) : $datetime_explode['date'][2];
  $datetime_explode['time'] = explode(':', $datetime_explode[1]);
  if (!checkdate($datetime_explode['date'][1], $datetime_explode['date'][2], $datetime_explode['date'][0]))
    $ret['err'] = 'дата введена некорректно, необходимо проверить корректность номера месяца, дня и существование номера дня в выбранном месяце';
  if ($datetime_explode['time'][0] > 23 OR
      $datetime_explode['time'][1] > 59 OR
      $datetime_explode['time'][2] > 59)
    {
    $ret['err'] = 'время указано не верно';
    return $ret;
    }
  else
    {
    if ($out_format === 'datetime')
      {
      $datetime = $datetime_explode['date'][0].'-'.$datetime_explode['date'][1].'-'.$datetime_explode['date'][2].' '.$datetime_explode['time'][0].':'.$datetime_explode['time'][1].':'.$datetime_explode['time'][2];
      $datetime = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $datetime_explode['date'][0], $datetime_explode['date'][1], $datetime_explode['date'][2], $datetime_explode['time'][0], $datetime_explode['time'][1], $datetime_explode['time'][2]);
      }
    else if ($out_format === 'date')
      {
      $datetime = $datetime_explode['date'][0].'-'.$datetime_explode['date'][1].'-'.$datetime_explode['date'][2];
      $datetime = sprintf('%04d-%02d-%02d', $datetime_explode['date'][0], $datetime_explode['date'][1], $datetime_explode['date'][2]);
      }
    $datetime_mkt = mysqldate_to_mktime($datetime);
    $ret['datetime'] = $datetime;
    }
 
  if ($datetime_mkt < $date_min_mkt)
    $ret['err'] = 'минимально-допустимая дата: '.date("Y-m-d", $date_min_mkt);
  if ($datetime_mkt > $date_max_mkt)
    $ret['err'] = 'максимально-допустимая дата: '.date("Y-m-d", $date_max_mkt);
  return $ret;
  }

Если допустимый период времени зависит от значения даты на момент запуска функции, например это актуально для лог-файла, в котором хранятся записи за последние 30 дней, то минимальную дату параметра $date_min можно установить следующим образом: $date_min = date('Y-m-d 00:00:00', time() - 60*60*24*30);. Нули в значениях времени позволят избежать введения пользователя в заблуждение, когда в выводимой ошибке о выходе даты за пределы допустимых значений указывается 2012-02-01, а настоящее значение является 2012-02-01 01:15:32 - повторно введя "допустимое" значение 2012-02-01 - пользователь снова увидит ту же ошибку, так как введенное значение преобразуется в 2012-02-01 00:00:00 и будет лежать за пределами минимально-допустимой даты 2012-02-01 01:15:32 (не говоря уже про то, что предел минимально-допустимой даты, зависящей от даты запуска функции, по прошествии времени изменится).

Примеры использования функции check_datetime()
$date = date('Y-m');

$result = check_datetime($date);
print_r($result); // Array ([datetime] => ГГГГ-ММ-01 00:00:00 )

$result = check_datetime($date, 'max');
print_r($result); // Array ([datetime] => ГГГГ-ММ-31 23:59:59 )

$result = check_datetime('2002-1', 'min', '2002-01-01 01:00:00');
print_r($result); // Array ( [datetime] => 2002-01-01 00:00:00 [err] => минимально-допустимая дата: 2002-01-01 )

$result = check_datetime('2002-1', 'max', '2002-01-01 01:00:00', '2002-01-10 00:00:00');
print_r($result); // Array ( [datetime] => 2002-01-31 23:59:59 [err] => максимально-допустимая дата: 2002-01-10 )

$result = check_datetime('2002-1-2 1:2:3', 'max', '2002-01-01 01:00:00', '2002-01-10 00:00:00', 'date');
print_r($result); // Array ( [datetime] => 2002-01-02 )

$result = check_datetime('2002-2-30 1:2:3', 'max', '2002-01-01 01:00:00', '2345-01-10 00:00:00', 'date');
print_r($result); // Array ( [datetime] => 2002-02-30 [err] => дата введена некорректно, необходимо проверить корректность номера месяца, дня и существование номера дня в выбранном месяце )

Приведение даты из формата MySQL в Timestamp

Функция mysqldate_to_mktime($date) принимает в качестве параметра дату в формате MySQL datetime (ГГГГ-ММ-ДД чч:мм:сс) или date (ГГГГ-ММ-ДД) и возвращает её значение (строка) в формате timestamp (кол-во секунд с 00:00:00 UTC 1 января, 1970 года).

Основное назначение функции - приведение дат в формате MySQL к Timestamp и дальнейшее их сравнение между собой средствами PHP. Например: необходимо узнать, находится ли дата в допустимых пределах.

// ПЕРЕВОД ФОРМАТА ДАТЫ ИЗ ФОРМАТА SQL В mktime
// $date .... - в формате 0000-00-00 00:00:00
function mysqldate_to_mktime($date)
  {
  $date = trim($date);
  $datetime_explode = explode(" ", $date);
  $time_explode[0] = 0;
  $time_explode[1] = 0;
  $time_explode[2] = 0;
  if (count($datetime_explode) == 2)
    {
    $time_explode = explode(":", $datetime_explode[1]);
    }
  $date_explode = explode("-", $datetime_explode[0]);
  return mktime($time_explode[0], $time_explode[1], $time_explode[2], $date_explode[1], $date_explode[2], $date_explode[0]);
  }

Опубликовано: 2012/02/02
HTML-код ссылки на эту страницу:
<a href="http://petrenco.com/php.php?txt=122" target="_blank">Работа с датой на PHP</a>
11625
Добавить комментарий
Ваш e-mail: (не виден посетителям сайта)
Ваше имя:
Комментарий:
Символы с картинки:
Только выделенные поля формы добавления комментариев обязательны к заполнению.