Контент


Своя CMS: форматирование дат

Задумался, как лучше выводить дату и время. Использование конструкций типа date(«m.d.y H:i», $var->created) кажется мне несколько громоздкой, да и формат может поменяться — и придется вносить коррективы во все шаблоны. Кроме того, хочется видеть локализованные названия месяцев и дней недели, если понадобится. В системном хэлпере date почему-то подобных вещей нет, поэтому я приступил к созданию хэлпера.

Больше всего мне подходила функция strftime(), которая учитывает установленную локаль. Однако после пары экспериментов выяснилось, что она возвращает русские названия в кодировке cp1251 (впрочем, это справедливо только для платформы Windows). Поэтому придется немного поработать напильничком. Выделим плейсхолдеры, которые подразумевают подстановку текстовых данных:

  • %A — полное название дня недели
  • %a — сокращенное название дня недели (три буквы)
  • %B — полное название месяца
  • %b — сокращенное название месяца (три буквы)

Теперь необходимо отслеживать появление в строке формата этих символов и вручную заменять их на соответствующие слова (брать их будем конечно из файлов i18n). Собственно сами строки формата я хочу хранить в конфигурационных файлах модулей по имени ‘format.php‘. Вот мой пример для форматов модуля «форум»:

$config['dateformats'] = array
(
	'forum_comment' => '%e %B %Y, %H:%M',
	'forum_post' => '%e %B %Y, %H:%M',
	'forum_last_comment' => '%e %B',
);


В модуле «ядро» также создается файл format.php, в него мы занесем формат по умолчанию:

$config['dateformats'] = array
(
	'default' => '%c',
);

Итак, форматы мы создали, надо подумать о хэлпере. Сперва получился вот такой набросок:

class cmsformat_Core {
 
	public static function timestamp($section, $timestamp) {
		if (is_null($section)) $section = 'default';
		is_null($timestamp) AND $timestamp = time();
		is_string($timestamp) AND $timestamp = strtotime($timestamp);
		if (! $format = Kohana::config('format.dateformats.'.$section))
		  $format = Kohana::config('format.dateformats.default');
		return strftime($format, $timestamp);
	}
}


Конечно, вынесение функции strftime() в хэлпер ничего коренным образом не поменяло, и по-прежнему вместо честной UTF-8 строка выдается в cp1251. Поэтому указанные выше плейсхолдеры надо заменить на i18n-строки. Получим следующий вариант:

public static function timestamp($section, $timestamp) {
	if (is_null($section)) $section = 'default';
	is_null($timestamp) AND $timestamp = time();
	is_string($timestamp) AND $timestamp = strtotime($timestamp);
	if (! $format = Kohana::config('format.dateformats.'.$section))
	  $format = Kohana::config('format.dateformats.default');
	$texts = array
	(
		'%B' => 'F',
		'%b' => 'M',
		'%a' => 'D',
		'%A' => 'l',
	);
	foreach($texts as $code => $type) {
		if (FALSE === strpos($format, $code)) continue;
		$title = strtolower(date($type,$timestamp));
		$format = str_replace($code, Kohana::lang('calendar.'.$title), $format);
	}
	return strftime($format, $timestamp);
}


Поясню — в массиве $texts мы указываем соответствие между плейсхолдерами функций strftime() и date(), чтобы получить из последней английское значение месяца/дня недели. Далее мы ищем с помощью метода Kohana::lang() в языковом файле calendar.php соответствующие ресурсы и подставляем их в формат. Ну и под конец вызываем функцию strftime() для подстановки всех прочих (численных) значений.

Вроде бы неплохо получается, но есть и недостаток — хочется видеть «21 апреля«, а не «21 апрель«. Поэтому я скопировал файл calendar.php из SYSPATH в папку с модулем «ядро» и там добавил следующий строчки в конец массива $lang:

	'_january'   => 'января',
	'_february'  => 'февраля',
	'_march'     => 'марта',
	'_april'     => 'апреля',
	'_may'       => 'мая',
	'_june'      => 'июня',
	'_july'      => 'июля',
	'_august'    => 'августа',
	'_september' => 'сентября',
	'_october'   => 'октября',
	'_november'  => 'ноября',
	'_december'  => 'декабря',


Как видно, названия месяцев я привел в родительный падеж, а также сделал полностью в маленьком регистре (как-то привычнее). Соответственно, необходимо внести небольшие изменения в коде:

public static function timestamp($section, $timestamp) {
	if (is_null($section)) $section = 'default';
	is_null($timestamp) AND $timestamp = time();
	is_string($timestamp) AND $timestamp = strtotime($timestamp);
	if (! $format = Kohana::config('format.dateformats.'.$section))
	  $format = Kohana::config('format.dateformats.default');
	if (strpos($format, '%B') !== FALSE) {
		$month = strtolower(date('F',$timestamp));
		$format = str_replace("%B", Kohana::lang('calendar._'.$month), $format);
	}
	$others = array
	(
		'%b' => 'M',
		'%a' => 'D',
		'%A' => 'l',
	);
	foreach($others as $code => $type) {
		if (FALSE === strpos($format, $code)) continue;
		$title = strtolower(date($type,$timestamp));
		$format = str_replace($code, Kohana::lang('calendar.'.$title), $format);
	}
	return strftime($format, $timestamp);
}


Ну и напоследок выяснилось, что под Win32 также не поддерживается часть плейсхолдеров, например %e (день месяца двумя символами, но вместо ведущего нуля будет пробел). Поэтому попробуем это исправить. Для этого добавим еще одну функцию в хэлпер:

public static function strftime($format, $ts = null) {
    if (!$ts) $ts = time();
 
    $mapping = array(
        '%C' => sprintf("%02d", date("Y", $ts) / 100),
        '%D' => '%m/%d/%y',
        '%e' => sprintf("%' 2d", date("j", $ts)),
        '%h' => '%b',
        '%n' => "\n",
        '%r' => date("h:i:s", $ts) . " %p",
        '%R' => date("H:i", $ts),
        '%t' => "\t",
        '%T' => '%H:%M:%S',
        '%u' => ($w = date("w", $ts)) ? $w : 7
    );
    $format = str_replace(
        array_keys($mapping),
        array_values($mapping),
        $format
    );
 
    return strftime($format, $ts);
}


Теперь осталось немного откорректировать метод timestamp(). В итоге хэлпер выглядит следующим образом:

// MODPATH/cms/helpers/cmsformat.php
class cmsformat_Core {
 
	public static function timestamp($section, $timestamp) {
		if (is_null($section)) $section = 'default';
		is_null($timestamp) AND $timestamp = time();
		is_string($timestamp) AND $timestamp = strtotime($timestamp);
		if (! $format = Kohana::config('format.dateformats.'.$section))
		  $format = Kohana::config('format.dateformats.default');
		if (strpos($format, '%B') !== FALSE) {
			$month = strtolower(date('F',$timestamp));
			$format = str_replace("%B", Kohana::lang('calendar._'.$month), $format);
		}
		$others = array
		(
			'%b' => 'M',
			'%a' => 'D',
			'%A' => 'l',
		);
		foreach($others as $code => $type) {
			if (FALSE === strpos($format, $code)) continue;
			$title = strtolower(date($type,$timestamp));
			$format = str_replace($code, Kohana::lang('calendar.'.$title), $format);
		}
		return cmsformat::strftime($format, $timestamp);
	}
 
	public static function strftime($format, $ts = null) {
		if (!$ts) $ts = time();
 
		$mapping = array
		(
			'%C' => sprintf("%02d", date("Y", $ts) / 100),
			'%D' => '%m/%d/%y',
			'%e' => sprintf("%' 2d", date("j", $ts)),
			'%h' => '%b',
			'%n' => "\n",
			'%r' => date("h:i:s", $ts) . " %p",
			'%R' => date("H:i", $ts),
			'%t' => "\t",
			'%T' => '%H:%M:%S',
			'%u' => ($w = date("w", $ts)) ? $w : 7
		);
		$format = str_replace
		(
			array_keys($mapping),
			array_values($mapping),
			$format
		);
 
		return strftime($format, $ts);
	}
}

Теперь в шаблоне просто можем использовать

<?=cmsformat::timestamp('forum_post', $comment->created)?>

и указанный в конфигурации формат даты будет отображен безо всяких там cp1251.
Как обычно, готов выслушать ваши замечания и предложения, и даже (конструктивную) критику вплоть до полного разноса ввиду ненадобности подобных решений ;)

PS. В оригинальном файле SYSPATH/i18n/ru_RU/calendar.php я обнаружил ошибку — в разделе полных названий месяцев для мая выделен неправильный ключ — «mayl«. Внося изменения в копию этого файла в модуле «ядро», я заодно исправил и этот недочет.

Google Bookmarks Digg Reddit del.icio.us Ma.gnolia Technorati Slashdot Yahoo My Web News2.ru БобрДобр.ru RUmarkz Ваау! Memori.ru rucity.com МоёМесто.ru Mister Wong

Опубликовано в Пишем CMS.

Теги: , , , , , .


Комментарии (2)

Будьте в курсе обсуждения, подпишитесь на RSS ленту комментариев к этой записи.

  1. Xobb пишет:

    cmsformat режет ухо. почему не зделать просто format extends format_Core?

  2. BIakaVeron пишет:

    cmsformat в моем видении — вспомогательный хэлпер будущей CMSки, там еще позже добавятся методы. Вообще можно и станадартный кохановский хэлпер расширить, но в приведенном мной варианте есть завязки на конфиги и прочую стороннюю информацию.



Можно включить подсветку кода: <code><pre lang="">...</pre></code>
Разрешены некоторые HTML теги

или используйте trackback.