Почти в каждом своем проекте я использую набор функций, которые делают сайт чуточку приятнее для глаза и удобнее для восприятия. Эти функции я частично позаимствовал в разных местах и частично написал сам. Наверняка, многие используют подобные, но я решил все же поделиться — возможно, кому-то они будут полезны.

Они умеют склонять существительные по числовому признаку, выводить дату с нормальными русскими названиями месяцев и выводить дату в удобочитаемом человекопонятном виде (вчера, позавчера, 2 дня 3 часа и 2 минуты назад, через 1 год и 2 месяца и т.д.).

Использовать их можно примерно так:
<acrnonym title="<?php print r_date($timestamp, 'j M Y в H:i', false); ?>"><?php print human_date($timestamp, 2, false);?></acronym>
<acrnonym title="<?php print r_date($timestamp, 'j M Y в H:i'); ?>"><?php print human_date($timestamp);?></acronym>


Это вернет примерно такой результат:
<acrnonym title="2 января 2009 в 23:39">1 день назад</acronym>
<acrnonym title="2 января в 23:39">Вчера</acronym>

(Хабр обрезает тег <acronym>)

И вот так:
<?php
$count 
= 10;
printf('%d %s', $count, declension($count, array('комментарий', 'комментария', 'комментариев')));
?>


Это вернет:
10 комментариев

Собственно, функции:

<?php

/** 
* Склонение существительных по числовому признаку
*
* @var integer    Число, по которому производится склонение
* @var array    Массив форм существительного
* @return string Существительное в нужной форме
* 
* Например:
* $count = 10;
* sprintf('%d %s', $count, declension($count, array('комментарий', 'комментария', 'комментариев')));
*
* Возвращает:
* 10 комментариев
*/
function declension($number, $words) {
    
$number = abs($number);
    if (
$number > 20) $number %= 10;
    if (
$number == 1) return $words[0];
    if (
$number >= 2 && $number <= 4) return $words[1];
    return 
$words[2];
}

/** 
* Приводит дату к заданному формату с учетом русских названий месяцев
*
* В качестве параметров функция принимает все допустимые значения функции date(),
* но символ F заменяется на русское название месяца (вне зависимости от локали),
* а символ M — на русское название месяца в родительном падеже
*
* @var integer    Unix-timestamp времени 
* @var string    Формат даты согласно спецификации для функции date() с учетом замены значения символов F и M
* @var boolean    Флаг отсекания года, если он совпадает с текущим
* @return string Отформатированная дата
*/
function r_date($time = '', $format = 'j M Y', $cut_year = true) {
    if(empty(
$time)) $time = time();
    if(
$cut_year && date('Y') == date('Y', $time)) $format = preg_replace('/o|y|Y/', '', $format);
    
$month = abs(date('n', $time)-1);
    
$rus = array('января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря');
    
$rus2 = array('январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь');
    
$format = preg_replace(array("'M'", "'F'"), array($rus[$month], $rus2[$month]), $format);
    return 
date($format, $time);
}


/** 
* Выводит дату в приблизительном удобочитаемом виде (например, "2 часа и 13 минут назад")
*
* Необходимо наличие функции declension() для корректной работы
*
* @var integer    Unix-timestamp времени 
* @var integer    Степень детализации
* @var boolean    Флаг использования упрощенных названий (вчера, позавчера, послезавтра)
* @var string    Формат даты с учетом замены значения символов F и M, если объявлена функция r_date()
* @return string Отформатированная дата
*/
function human_date($timestamp, $granularity = 1, $use_terms = true, $default_format = 'j M Y') {
    
$curtime = time();
    
$original = $timestamp;
    
$output = '';
    if(
$curtime >= $original) {
        
$timestamp = abs($curtime - $original);
        
$tense = 'past';
    } else {
        
$timestamp = abs($original - $curtime);
        
$tense = 'future';
    }
    
$units = array('years' => 31536000,
                 
'weeks' => 604800, 
                 
'days' => 86400, 
                 
'hours' => 3600, 
                 
'min' => 60, 
                 
'sec' => 1);
    
$titles = array('years' => array('год', 'года', 'лет'),
                 
'weeks' => array('неделя', 'недели', 'недель'), 
                 
'days' => array('день', 'дня', 'дней'), 
                 
'hours' => array('час', 'часа', 'часов'), 
                 
'min' => array('минута', 'минуты', 'минут'), 
                 
'sec' => array('секунда', 'секунды', 'секунд'));
    foreach (
$units as $key => $value) {
        if (
$timestamp >= $value) {
            
$number = floor($timestamp / $value);
            
$output .= ($output ? ($granularity == 1 ? ' и ' : ' ') : '') . $number .' '. declension($number, $titles[$key]);
            
$timestamp %= $value;
            
$granularity--;
        }
    }
    if(
$tense == 'future') {
        
$output = 'Через '.$output;
    } else {
        
$output .= ' назад';
    }
  if(
$use_terms) {
      
$terms = array('Через 1 день' => 'Послезавтра',
                     
'1 день назад' => 'Вчера',
                     
'2 дня назад' => 'Позавчера'
                     
);
      if(isset(
$terms[$output])) $output = $terms[$output];
  }
  return 
$output ? $output : (function_exists('r_date') ? r_date($original, $default_format) : date($default_format, $original));
}
?>


Надеюсь, кому-нибудь они пригодятся.