Экстраполя Cotonti: тип данных datetime — Дата и время
Общие сведения
Тип datetime в системе дополнительных полей (extrafields) Cotonti предназначен для хранения даты и времени в виде целого числа — Unix timestamp. Это значение представляет собой количество секунд, прошедших с полуночи 1 января 1970 года по UTC. Такой подход обеспечивает компактное хранение, простоту сравнения и корректное преобразование между часовыми поясами.
Вся логика обработки полей типа datetime сосредоточена в трёх основных файлах:
system/extrafields.php— функции для построения формы, импорта и вывода данных.system/forms.php— генерация выпадающих списков даты (cot_selectbox_date).system/functions.php— вспомогательные функции для работы с датами и временем (cot_import_date,cot_date,cot_mktime,cot_date2stamp,cot_stamp2date).
Ниже мы детально рассмотрим каждый аспект работы с этим типом.
Хранение значения в базе данных
При создании экстраполя типа datetime через функцию cot_extrafield_add() в таблицу базы данных добавляется колонка со следующим SQL-типом:
int DEFAULT '0'Это означает:
- Значение хранится как целое число (Unix timestamp).
- Пустое значение (дата не выбрана) соответствует числу
0. - Максимальное значение определяется типом
int(обычно 2^31-1), что позволяет хранить даты до 2038 года на 32-битных системах, но в большинстве современных окружений используется 64-битный PHP, где диапазон шире.
Фактически, в базе данных всегда находится либо 0, либо положительное число — количество секунд с начала эпохи.
Параметры поля (field_params)
Поле field_params в таблице cot_extra_fields для типа datetime хранит строку, состоящую из трёх компонентов, разделённых запятыми:
min,max,format- min — минимальный год (целое число). Используется для ограничения диапазона лет в форме редактирования и при импорте.
- max — максимальный год (целое число).
- format — строка формата даты/времени для вывода значения. Может быть как стандартным PHP-форматом (например,
'Y-m-d H:i'), так и ключом локализованного формата Cotonti (например,'datetime_medium').
Поведение по умолчанию
Если параметры не заданы или заданы частично, действуют следующие правила:
- Если
minпуст или ≤ 0, используется 2000. - Если
maxпуст или ≤ 0, используется 2030. - Если
formatпуст, при выводе возвращается исходный Unix timestamp без форматирования.
Разбор параметров в коде
В функции cot_build_extrafields() (для построения формы) параметры разбираются так:
$extrafield['field_params'] = str_replace([' , ', ', ', ' ,'], ',', $extrafield['field_params']);
list($min, $max, $format) = explode(",", $extrafield['field_params'], 3);
$max = (int)$max > 0 ? $max : 2030;
$min = (int)$min > 0 ? $min : 2000;В функции импорта cot_import_extrafields() разбираются только первые два параметра:
list($min, $max) = explode(",", $extrafield['field_params'], 2);В функции вывода cot_build_extrafields_data():
list($min, $max, $format) = explode(",", $extrafield['field_params'], 3);
return (empty($format)) ? $value : cot_date($format, $value);Таким образом, третий параметр (format) используется исключительно для вывода данных. При импорте и построении формы он игнорируется.
Формирование формы редактирования
Форма для ввода даты и времени генерируется функцией cot_build_extrafields() в ветке case 'datetime'.
Алгоритм cot_build_extrafields для datetime
- Инициализация глобальных переменных:
global $sys; - Очистка параметров: заменяются лишние пробелы вокруг запятых.
- Разбор параметров: извлекаются
$min,$max,$format. - Установка значений по умолчанию: если
$max≤ 0, берётся 2030; если$min≤ 0, берётся 2000. Обработка относительных дат: если значение
$dataначинается с+или-, оно интерпретируется как смещение в секундах от текущего времени$sys['now']:$data = (mb_substr($data, 0, 1) == "+") ? $sys['now'] + (int)(mb_substr($data, 1)) : $data; $data = (mb_substr($data, 0, 1) == "-") ? $sys['now'] - (int)(mb_substr($data, 1)) : $data;Это позволяет использовать такие значения, как
+86400(завтра) или-3600(час назад).- Вызов генератора даты:
cot_selectbox_date((int)$data, 'long', $name, (int)$max, (int)$min, true, $extrafield['field_html']).
Функция cot_selectbox_date()
Подробно рассмотрим её поведение на основе кода system/forms.php.
Параметры
$utime— Unix timestamp выбранной даты.$mode— режим отображения. Для экстраполей всегда передаётся'long'(полный набор: год, месяц, день, час, минута).$name— базовое имя поля (например,rxtra_x200_last_promotion).$max_year,$min_year— границы лет.$usertimezone— флаг учёта часового пояса пользователя. Для экстраполей передаётсяtrue.$custom_rc— пользовательский ресурсный шаблон.
Шаги выполнения
- Проверка наличия кастомной функции: если определена
cot_selectbox_date_custom(), она будет вызвана и результат возвращён без дальнейших действий. - Хук
form.date: разработчики могут переопределить вывод через плагины, подключённые к этому хуку. - Определение имени ресурса: если имя поля содержит квадратные скобки (например,
rxtra_date[user_id]), извлекается базовое имя до скобок для поиска ресурсных строк. Корректировка времени по часовому поясу:
$utime = ($usertimezone && $utime > 0) ? ($utime + $usr['timezone'] * 3600) : $utime;Если
$utimeбольше нуля и учёт часового пояса включён, к нему добавляется смещение текущего пользователя (в часах, умноженное на 3600). Это обеспечивает отображение времени в локальной зоне пользователя.- Определение компонентов даты:
- Если
$utime == 0(пустая дата), то компоненты ($s_year,$s_month,$s_day,$s_hour,$s_minute) устанавливаются вnull. Затем проверяется буферизованное значение черезcot_import_buffered(). Это позволяет восстановить выбранные значения после неудачной отправки формы (например, при ошибке валидации). Если буфер содержит массив, его значения используются для заполнения полей. - Если
$utime > 0, то компоненты извлекаются черезdate('Y-m-d-H-i', $utime)и разбиваются по-.
- Если
- Формирование массива месяцев: используется языковой массив
$Lдля локализованных названий месяцев (от$L['January']до$L['December']). - Создание выпадающих списков:
- Год:
cot_selectbox($s_year, $name.'[year]', range($max_year, $min_year, -1)). Диапазон лет от максимума к минимуму. - Месяц:
cot_selectbox($s_month, $name.'[month]', array_keys($months), array_values($months)). - День:
cot_selectbox($s_day, $name.'[day]', range(1, 31)). - Час:
cot_selectbox($s_hour, $name.'[hour]', range(0, 23))с ведущими нулями черезsprintf('%02d', $i). - Минута:
cot_selectbox($s_minute, $name.'[minute]', range(0, 59)).
- Год:
- Выбор ресурсного шаблона: сначала ищется
$R["input_date_{$mode}"], затем$R["input_date_{$rc_name}"], затем$custom_rc, иначе'input_date'. - Сборка HTML: через
cot_rc()с передачей сгенерированных списков.
В результате форма содержит пять последовательных <select> элементов, обычно обёрнутых в контейнер из ресурсного файла input_date.
Импорт и сохранение значения
Импорт данных из формы выполняется функцией cot_import_extrafields() в ветке case 'datetime'.
Шаги импорта
- Очистка параметров: заменяются пробелы вокруг запятых.
- Разбор
minиmax(третий параметр не используется). - Вызов
cot_import_date($inputname, true, false, $source):$inputname— имя поля (например,rxtra_x200_last_promotion).true— учитывать часовой пояс пользователя.false— не возвращать массив, а сразу timestamp.$source— источник ('P'для POST).
- Обработка результата
cot_import_date():- Если функция вернула
null(поле пустое), устанавливается$import = 0. Если заданы
minилиmax(больше 0), выполняется корректировка года:list($s_year, $s_month, $s_day, $s_hour, $s_minute) = explode('-', @date('Y-m-d-H-i', $import)); if ($min > $s_year) { $import = mktime($s_hour, $s_minute, 0, $s_month, $s_day, $min); } if ($max < $s_year) { $import = mktime($s_hour, $s_minute, 0, $s_month, $s_day, $max); }Таким образом, если выбранный год выходит за допустимые границы, он принудительно заменяется на
minилиmaxс сохранением остальных компонентов.
- Если функция вернула
- Проверка обязательности: если поле обязательное (
field_required = 1) и$importравноnull,''или0, генерируется ошибка черезcot_error().
Функция cot_import_date()
Эта функция находится в system/functions.php и играет ключевую роль.
Логика работы
- Проверка кастомной функции: если существует
cot_import_date_custom(), она вызывается. - Хук
import.date: позволяет плагинам переопределить импорт. - Получение данных:
- Сначала пытается получить массив из источника через
cot_import($name, $source, 'ARR'). Ожидается массив с ключамиyear,month,day,hour,minute. - Если массив пуст, предпринимается попытка прочитать значение как обычную строку (
cot_import(..., 'TXT')). Это может быть дата в текстовом формате (например,2025-12-31 23:59).
- Сначала пытается получить массив из источника через
- Обработка строкового значения:
- Если строка не пуста, вызывается
cot_date2stamp($date), которая преобразует её в timestamp. Если преобразование не удалось, возвращаетсяnull.
- Если строка не пуста, вызывается
- Обработка массива:
- Компоненты массива проверяются на наличие, при отсутствии устанавливаются в
0. - Если все компоненты равны
null(поле формы отправлено, но ни один элемент не выбран), возвращаетсяnull. - Если условие
($month && $day && $year) || ($day && $minute)истинно, создаётся timestamp черезcot_mktime($hour, $minute, 0, $month, $day, $year). - В противном случае проверяется строка
stringи форматformat, которые могут быть переданы в массиве. Если строка непуста, используетсяcot_date2stamp($string, $format). Иначе возвращаетсяnull.
- Компоненты массива проверяются на наличие, при отсутствии устанавливаются в
Корректировка часового пояса:
if ($usertimezone) { $timestamp -= Cot::$usr['timezone'] * 3600; }Из полученного timestamp вычитается часовой пояс текущего пользователя, чтобы привести время к UTC перед сохранением в базу.
- Возврат результата:
- Если
$returnarray=true, возвращается массив с ключамиstamp,year,month,day,hour,minute. - Иначе возвращается целочисленный timestamp.
- Если
Таким образом, итоговое значение, сохраняемое в базе данных, всегда является UTC timestamp.
Вывод значения в шаблонах
Для получения готового значения экстраполя используется функция cot_build_extrafields_data() (в system/extrafields.php).
Поведение для datetime
case 'datetime':
$extrafield['field_params'] = str_replace([' , ', ', ', ' ,'], ',', $extrafield['field_params']);
list($min, $max, $format) = explode(",", $extrafield['field_params'], 3);
return (empty($format)) ? $value : cot_date($format, $value);
break;- Из параметров извлекается только третий компонент —
format. - Если
formatпуст, возвращается исходное значение$value(timestamp). - Если
formatзадан, вызывается функцияcot_date($format, $value).
Функция cot_date()
Расположена в system/functions.php. Используется для локализованного форматирования даты.
function cot_date($format, $timestamp = null, $usertimezone = true)
{
global $lang, $Ldt;
if (is_null($timestamp)) {
$timestamp = Cot::$sys['now'];
}
$timestamp = (int) $timestamp;
if ($usertimezone) {
$timestamp += Cot::$usr['timezone'] * 3600;
}
$datetime = (isset($Ldt[$format])) ? @date($Ldt[$format], $timestamp) : @date($format, $timestamp);
// ... замена английских названий месяцев/дней на локализованные
return ($lang == 'en') ? $datetime : str_replace($search, $replace, $datetime);
}- Если
$timestampне передан, берётся текущее времяCot::$sys['now']. - При
$usertimezone = trueк timestamp добавляется часовой пояс пользователя (Cot::$usr['timezone'] * 3600), то есть время из UTC переводится в локальное для отображения. - Если формат присутствует в массиве
$Ldt(локализованные форматы), используется он, иначе — переданный PHP-формат. - После форматирования выполняется замена английских названий дней недели и месяцев на локализованные версии из
$L(если язык не английский).
Взаимодействие с часовыми поясами
Ключевая особенность типа datetime — корректная работа с часовыми поясами пользователей. Схема следующая:
- При вводе (импорте):
- Пользователь выбирает дату и время в своей локальной зоне.
- Функция
cot_import_date()вычитает его часовой пояс (Cot::$usr['timezone'] * 3600), приводя время к UTC. - В базу сохраняется UTC timestamp.
- При выводе:
- Из базы извлекается UTC timestamp.
- Функция
cot_date()добавляет часовой пояс текущего пользователя, переводя время обратно в локальное. - Затем происходит форматирование и локализация.
Таким образом, каждый пользователь видит дату и время в своей собственной зоне, независимо от того, где была сохранена запись.
Часовой пояс пользователя хранится в Cot::$usr['timezone'] как смещение в часах от UTC (например, 3 для Москвы).
Использование в плагинах и модулях
Регистрация поля
Для создания экстраполя типа datetime в плагине используется функция cot_extrafield_add(). Пример:
cot_extrafield_add(
'users', // таблица, к которой добавляется поле
'my_event_datetime', // имя поля (без префикса таблицы)
'datetime', // тип
'', // html-конструкция (пусто — стандартная)
'', // варианты (не используются для datetime)
'', // значение по умолчанию (timestamp или 0)
false, // обязательность
'HTML', // парсер (не влияет на datetime)
'Дата события', // описание
'2000,2030,datetime_medium' // параметры: min,max,format
);После вызова в таблице cot_users появится колонка user_my_event_datetime с типом int DEFAULT '0'.
Сохранение значения
В плагине при обработке POST-запроса:
$exfld = [/* массив описания поля, полученный через cot_load_extrafields() */];
$oldValue = $existingData['user_my_event_datetime'] ?? 0;
$newValue = cot_import_extrafields('rxtra_my_event_datetime', $exfld, 'P', $oldValue, 'xtra_');
// $newValue — UTC timestamp или 0Если поле обязательное, функция автоматически сгенерирует ошибку при пустом значении.
После успешного импорта всех полей обычно вызывается cot_extrafield_movefiles() (для файловых полей, но не влияет на datetime).
Получение значения
Для получения значения из базы данных достаточно обычного SQL-запроса или использования API плагина. Значение будет целым числом (timestamp).
Использование в шаблонах
Автоматическая генерация тегов
Для пользователей Cotonti предоставляет функцию cot_generate_usertags(), которая создаёт теги для всех экстраполей, включая datetime.
Например, для поля user_x200_last_promotion будут созданы следующие теги:
{USERS_DETAILS_XTRA_X200_LAST_PROMOTION}— отформатированное значение (еслиformatзадан) или timestamp.{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_TITLE}— название поля.{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE}— сырое значение (timestamp).
Обратите внимание, что в стандартной генерации тегов для экстраполей пользователей используется префикс user_, но он не включается в имя тега. В плагинах, подобных xtradbrowusers, могут быть свои соглашения.
Проверка наличия значения
Поскольку пустая дата хранится как 0, для проверки заполненности поля следует использовать тег _VALUE:
<!-- IF {USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE} -->
<div class="contact-label">{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_TITLE}</div>
<div class="contact-value">{USERS_DETAILS_XTRA_X200_LAST_PROMOTION}</div>
<!-- ENDIF -->Условие <!-- IF {TAG_VALUE} --> истинно, если значение не пустое и не равно строке "0". Такой подход гарантирует, что блок с датой не появится, если дата не выбрана.
Альтернативная проверка с явным сравнением
<!-- IF {USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE} > 0 -->
... вывод ...
<!-- ENDIF -->Оба способа эквивалентны для datetime, так как пустое значение всегда 0. Явное сравнение может быть полезно, если требуется сравнить с текущей датой или другими значениями.
Вывод даты в заданном формате
Если формат не задан в параметрах поля, можно отформатировать timestamp прямо в шаблоне, используя PHP-функцию date внутри {PHP}:
{PHP|cot_date('d.m.Y H:i', {USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE})}
или
{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE|cot_date('d.m.Y H:i', $this)}
кому как удобно или кто как привыкНо обычно формат указывается в field_params, и тогда обычный тег уже возвращает готовую строку.
Пример полного блока
<!-- IF {USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE} -->
<div class="d-flex align-items-center mb-3">
<div class="contact-icon calendar me-3">
<i class="fa-solid fa-calendar-alt fa-xl"></i>
</div>
<div>
<div class="contact-label">{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_TITLE}</div>
<div class="contact-value">{USERS_DETAILS_XTRA_X200_LAST_PROMOTION}</div>
</div>
</div>
<!-- ENDIF -->Особенности и подводные камни
Нулевое значение (0)
- Пустая дата всегда хранится как
0. - При выводе с заданным форматом
cot_date()вернёт дату 1 января 1970 года, что может выглядеть как «01.01.1970». Поэтому всегда проверяйте наличие значения через_VALUEперед выводом. - При проверке обязательности в импорте
0считается пустым значением.
Относительные даты
Если в значение поля в БД каким-либо образом попало число с ведущим + или - (например, +86400), то при построении формы оно будет преобразовано в $sys['now'] + 86400. Это удобно для динамических сроков, но требует осторожности: при сохранении такого значения через форму оно будет зафиксировано как конкретная дата.
Корректировка года по min/max
minиmaxвлияют на список годов в форме, а также на импорт: если выбранный год меньшеmin, он заменяется наmin, если большеmax— наmax.- Эти параметры не накладывают ограничений на значение, уже сохранённое в БД, если оно было изменено вручную.
Часовой пояс
- При импорте время корректируется по часовому поясу текущего пользователя (того, кто заполняет форму).
- При выводе используется часовой пояс пользователя, просматривающего страницу.
- Если пользователь не авторизован,
Cot::$usr['timezone']может быть0или значение по умолчанию, обычно UTC.
Парсинг строк
cot_import_date() поддерживает импорт не только из массива компонентов, но и из обычной строки с датой. Это может быть полезно при интеграции с внешними источниками данных. Однако строковый ввод не используется в стандартной форме.
Хуки
- Хук
form.dateпозволяет полностью заменить вывод формы даты. - Хук
import.dateпозволяет заменить логику импорта. - Если определена функция
cot_selectbox_date_custom(), она имеет приоритет.
Влияние обязательности
Если поле datetime помечено как обязательное, пустое значение (0) вызовет ошибку валидации. Ошибка будет связана с именем поля, что позволяет показать сообщение рядом с соответствующей частью формы.
Заключение
Тип datetime в Cotonti является мощным инструментом для хранения дат и времени с учётом часовых поясов. Он хранит данные в виде UTC timestamp, обеспечивает удобный ввод через набор выпадающих списков и гибкий вывод с локализацией. При использовании в шаблонах важно помнить о проверке значения через _VALUE, чтобы корректно скрывать блоки с пустой датой.
Разработчики плагинов могут полностью контролировать поведение поля через параметры min, max и format, а также использовать хуки для расширения функциональности. Знание деталей работы функций cot_build_extrafields, cot_import_extrafields, cot_selectbox_date и cot_date позволяет эффективно интегрировать этот тип в любые модули и темы оформления.
Статья основана исключительно на анализе исходного кода Cotonti, и не содержит предположений или недостоверной информации.