Задумался, как лучше выводить дату и время. Использование конструкций типа 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«. Внося изменения в копию этого файла в модуле «ядро», я заодно исправил и этот недочет.
cmsformat режет ухо. почему не зделать просто format extends format_Core?
cmsformat в моем видении — вспомогательный хэлпер будущей CMSки, там еще позже добавятся методы. Вообще можно и станадартный кохановский хэлпер расширить, но в приведенном мной варианте есть завязки на конфиги и прочую стороннюю информацию.