Cotonti Extra fields: datetime data type — Date and time
How datetime type Extrafields work in Cotonti: storage in Unix timestamp, format parameters, import, output based on time zones and use in templates. Detailed analysis with code examples.
Cotonti Extra Fields: datetime Data Type — Date and Time
Overview
The datetime type in Cotonti's extra fields system is designed to store date and time as an integer — a Unix timestamp. This value represents the number of seconds since midnight on January 1, 1970, UTC. This approach ensures compact storage, easy comparison, and correct conversion between time zones.
All logic for handling datetime fields is concentrated in three main files:
system/extrafields.php— functions for building forms, importing and outputting data.system/forms.php— generation of date select dropdowns (cot_selectbox_date).system/functions.php— helper functions for date and time handling (cot_import_date,cot_date,cot_mktime,cot_date2stamp,cot_stamp2date).
Below we will examine each aspect of working with this type in detail.
Storing Values in the Database
When an extra field of type datetime is created via cot_extrafield_add(), a column is added to the database table with the following SQL type:
int DEFAULT '0'This means:
- The value is stored as an integer (Unix timestamp).
- An empty value (date not selected) corresponds to the number
0. - The maximum value is defined by the
inttype (usually 2^31-1), which allows dates up to 2038 on 32-bit systems, but in most modern environments 64-bit PHP is used, providing a wider range.
In practice, the database always contains either 0 or a positive integer — the number of seconds since the epoch.
Field Parameters (field_params)
The field_params field in the cot_extra_fields table for datetime stores a string consisting of three comma-separated components:
min,max,format- min — minimum year (integer). Used to limit the year range in the edit form and during import.
- max — maximum year (integer).
- format — date/time format string for output. Can be a standard PHP format (e.g.,
'Y-m-d H:i') or a Cotonti localized format key (e.g.,'datetime_medium').
Default Behavior
If parameters are not set or only partially set, the following rules apply:
- If
minis empty or ≤ 0, 2000 is used. - If
maxis empty or ≤ 0, 2030 is used. - If
formatis empty, the raw Unix timestamp is returned on output without formatting.
Parsing Parameters in the Code
In the cot_build_extrafields() function (for building the form), parameters are parsed as follows:
$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;In the import function cot_import_extrafields(), only the first two parameters are parsed:
list($min, $max) = explode(",", $extrafield['field_params'], 2);In the output function cot_build_extrafields_data():
list($min, $max, $format) = explode(",", $extrafield['field_params'], 3);
return (empty($format)) ? $value : cot_date($format, $value);Thus, the third parameter (format) is used exclusively for output. It is ignored during import and form building.
Building the Edit Form
The date/time input form is generated by the cot_build_extrafields() function in the case 'datetime' branch.
Algorithm of cot_build_extrafields for datetime
- Initialize global variables:
global $sys; - Clean parameters: remove extra spaces around commas.
- Parse parameters: extract
$min,$max,$format. - Set default values: if
$max≤ 0, use 2030; if$min≤ 0, use 2000. Handle relative dates: if the value
$datastarts with+or-, it is interpreted as an offset in seconds from the current time$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;This allows values like
+86400(tomorrow) or-3600(one hour ago).- Call the date generator:
cot_selectbox_date((int)$data, 'long', $name, (int)$max, (int)$min, true, $extrafield['field_html']).
Function cot_selectbox_date()
Let's examine its behaviour based on the code in system/forms.php.
Parameters
$utime— Unix timestamp of the selected date.$mode— display mode. For extra fields,'long'is always passed (full set: year, month, day, hour, minute).$name— base field name (e.g.,rxtra_x200_last_promotion).$max_year,$min_year— year boundaries.$usertimezone— flag to account for the user's timezone. For extra fields,trueis passed.$custom_rc— custom resource template.
Execution Steps
- Check for custom function: if
cot_selectbox_date_custom()is defined, it is called and the result is returned without further action. - Hook
form.date: developers can override output via plugins hooked to this event. - Determine resource name: if the field name contains square brackets (e.g.,
rxtra_date[user_id]), the base name before the brackets is extracted for resource string lookup. Adjust time for timezone:
$utime = ($usertimezone && $utime > 0) ? ($utime + $usr['timezone'] * 3600) : $utime;If
$utimeis greater than zero and timezone adjustment is enabled, the current user's offset (in hours, multiplied by 3600) is added. This ensures the time is displayed in the user's local zone.- Determine date components:
- If
$utime == 0(empty date), the components ($s_year,$s_month,$s_day,$s_hour,$s_minute) are set tonull. Then the buffered value is checked viacot_import_buffered(). This allows restoring selected values after a failed form submission (e.g., validation error). If the buffer contains an array, its values are used to fill the fields. - If
$utime > 0, components are extracted viadate('Y-m-d-H-i', $utime)and split by-.
- If
- Build month array: uses the language array
$Lfor localized month names (from$L['January']to$L['December']). - Create dropdowns:
- Year:
cot_selectbox($s_year, $name.'[year]', range($max_year, $min_year, -1)). Range from max to min. - Month:
cot_selectbox($s_month, $name.'[month]', array_keys($months), array_values($months)). - Day:
cot_selectbox($s_day, $name.'[day]', range(1, 31)). - Hour:
cot_selectbox($s_hour, $name.'[hour]', range(0, 23))with leading zeros viasprintf('%02d', $i). - Minute:
cot_selectbox($s_minute, $name.'[minute]', range(0, 59)).
- Year:
- Select resource template: first looks for
$R["input_date_{$mode}"], then$R["input_date_{$rc_name}"], then$custom_rc, otherwise'input_date'. - Assemble HTML: via
cot_rc()passing the generated lists.
The result is a form containing five sequential <select> elements, usually wrapped in a container from the input_date resource file.
Importing and Saving Values
Importing data from the form is handled by the cot_import_extrafields() function in the case 'datetime' branch.
Import Steps
- Clean parameters: remove spaces around commas.
- Parse
minandmax(third parameter is not used). - Call
cot_import_date($inputname, true, false, $source):$inputname— field name (e.g.,rxtra_x200_last_promotion).true— account for user's timezone.false— do not return an array, return a timestamp directly.$source— source ('P'for POST).
- Handle the result of
cot_import_date():- If the function returned
null(empty field), set$import = 0. If
minormaxare set (greater than 0), adjust the year: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); }Thus, if the selected year falls outside the allowed bounds, it is forcibly replaced with
minormaxwhile preserving the other components.
- If the function returned
- Check required status: if the field is required (
field_required = 1) and$importisnull,''or0, an error is triggered viacot_error().
Function cot_import_date()
This function is located in system/functions.php and plays a key role.
Logic
- Check for custom function: if
cot_import_date_custom()exists, it is called. - Hook
import.date: allows plugins to override import. - Retrieve data:
- First attempts to get an array from the source via
cot_import($name, $source, 'ARR'). Expects an array with keysyear,month,day,hour,minute. - If the array is empty, attempts to read the value as a plain string (
cot_import(..., 'TXT')). This can be a date in text format (e.g.,2025-12-31 23:59).
- First attempts to get an array from the source via
- Handle string value:
- If the string is not empty,
cot_date2stamp($date)is called to convert it to a timestamp. If conversion fails,nullis returned.
- If the string is not empty,
- Handle array:
- Array components are checked for presence; missing ones are set to
0. - If all components are
null(the form field was submitted but no element selected),nullis returned. - If the condition
($month && $day && $year) || ($day && $minute)is true, a timestamp is created viacot_mktime($hour, $minute, 0, $month, $day, $year). - Otherwise, the
stringandformatkeys (which may be passed in the array) are checked. If the string is not empty,cot_date2stamp($string, $format)is used. Otherwisenullis returned.
- Array components are checked for presence; missing ones are set to
Adjust timezone:
if ($usertimezone) { $timestamp -= Cot::$usr['timezone'] * 3600; }The current user's timezone offset is subtracted from the timestamp to convert it to UTC before saving to the database.
- Return result:
- If
$returnarray=true, an array with keysstamp,year,month,day,hour,minuteis returned. - Otherwise, an integer timestamp is returned.
- If
Thus, the final value stored in the database is always a UTC timestamp.
Outputting Values in Templates
To obtain a ready-to-display value for an extra field, the cot_build_extrafields_data() function (in system/extrafields.php) is used.
Behavior for 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;- Only the third component —
format— is extracted from the parameters. - If
formatis empty, the raw$value(timestamp) is returned. - If
formatis set, thecot_date($format, $value)function is called.
Function cot_date()
Located in system/functions.php. Used for localized date formatting.
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);
// ... replace English month/day names with localized versions
return ($lang == 'en') ? $datetime : str_replace($search, $replace, $datetime);
}- If
$timestampis not passed, the current timeCot::$sys['now']is used. - When
$usertimezone = true, the user's timezone offset (Cot::$usr['timezone'] * 3600) is added to the timestamp, converting UTC to local time for display. - If the format exists in the
$Ldtarray (localized formats), it is used; otherwise, the passed PHP format is used. - After formatting, English day and month names are replaced with localized versions from
$L(if the language is not English).
Timezone Handling
A key feature of the datetime type is correct handling of user time zones. The scheme is as follows:
- On input (import):
- The user selects a date and time in their local zone.
cot_import_date()subtracts their timezone offset (Cot::$usr['timezone'] * 3600), converting the time to UTC.- The UTC timestamp is saved to the database.
- On output:
- The UTC timestamp is retrieved from the database.
cot_date()adds the current user's timezone offset, converting it back to local time.- Then formatting and localization are applied.
Thus, each user sees the date and time in their own zone, regardless of where the record was saved.
The user's timezone is stored in Cot::$usr['timezone'] as an offset in hours from UTC (e.g., 3 for Moscow).
Using in Plugins and Modules
Registering a Field
To create a datetime extra field in a plugin, the cot_extrafield_add() function is used. Example:
cot_extrafield_add(
'users', // table to which the field is added
'my_event_datetime', // field name (without table prefix)
'datetime', // type
'', // HTML construction (empty for default)
'', // variants (not used for datetime)
'', // default value (timestamp or 0)
false, // required
'HTML', // parser (does not affect datetime)
'Event date', // description
'2000,2030,datetime_medium' // parameters: min,max,format
);After this call, the cot_users table will have a column user_my_event_datetime with type int DEFAULT '0'.
Saving a Value
In a plugin, when handling a POST request:
$exfld = [/* array of field description obtained via 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 or 0If the field is required, the function will automatically generate an error for an empty value.
After successfully importing all fields, cot_extrafield_movefiles() is usually called (for file fields, but does not affect datetime).
Retrieving a Value
To get the value from the database, a plain SQL query or the plugin's API can be used. The value will be an integer (timestamp).
Using in Templates
Automatic Tag Generation
For users, Cotonti provides the cot_generate_usertags() function, which creates tags for all extra fields, including datetime.
For example, for the field user_x200_last_promotion, the following tags will be created:
{USERS_DETAILS_XTRA_X200_LAST_PROMOTION}— formatted value (ifformatis set) or timestamp.{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_TITLE}— field label.{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE}— raw value (timestamp).
Note that in the standard tag generation for user extra fields, the prefix user_ is used, but it is not included in the tag name. In plugins like xtradbrowusers, there may be their own conventions.
Checking for a Value
Since an empty date is stored as 0, the _VALUE tag should be used to check if the field is filled:
<!-- 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 -->The condition <!-- IF {TAG_VALUE} --> is true if the value is not empty and not equal to the string "0". This approach ensures that the block with the date will not appear if no date is selected.
Alternative Check with Explicit Comparison
<!-- IF {USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE} > 0 -->
... output ...
<!-- ENDIF -->Both methods are equivalent for datetime, since an empty value is always 0. Explicit comparison may be useful when comparing with the current date or other values.
Outputting Date in a Custom Format
If no format is specified in the field parameters, you can format the timestamp directly in the template using the PHP date function inside {PHP}:
{PHP|cot_date('d.m.Y H:i', {USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE})}
or
{USERS_DETAILS_XTRA_X200_LAST_PROMOTION_VALUE|cot_date('d.m.Y H:i', $this)}But usually the format is set in field_params, and then the regular tag already returns a formatted string.
Complete Block Example
<!-- 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 -->Peculiarities and Pitfalls
Zero Value (0)
- An empty date is always stored as
0. - When outputting with a format set,
cot_date()will return January 1, 1970, which may look like "01.01.1970". Therefore, always check for a value using_VALUEbefore outputting. - When checking required fields during import,
0is considered an empty value.
Relative Dates
If a value in the database somehow contains a leading + or - (e.g., +86400), it will be converted to $sys['now'] + 86400 when building the form. This is convenient for dynamic deadlines but requires caution: when saving such a value through the form, it will be fixed as a concrete date.
Year Correction via min/max
minandmaxaffect the year dropdown in the form, as well as import: if the selected year is less thanmin, it is replaced withmin; if greater thanmax, replaced withmax.- These parameters do not restrict values already stored in the database if they were modified manually.
Timezone
- On import, the time is adjusted according to the current user's timezone (the one filling the form).
- On output, the timezone of the user viewing the page is used.
- If the user is not logged in,
Cot::$usr['timezone']may be0or the default, usually UTC.
String Parsing
cot_import_date() supports importing not only from an array of components but also from a plain date string. This can be useful when integrating with external data sources. However, string input is not used in the standard form.
Hooks
- The
form.datehook allows complete replacement of the date form output. - The
import.datehook allows overriding the import logic. - If the
cot_selectbox_date_custom()function is defined, it takes precedence.
Impact of Required Flag
If a datetime field is marked as required, an empty value (0) will trigger a validation error. The error will be associated with the field name, allowing a message to be displayed next to the relevant part of the form.
Conclusion
The datetime type in Cotonti is a powerful tool for storing dates and times with timezone awareness. It stores data as a UTC timestamp, provides convenient input via dropdowns, and flexible localized output. When using it in templates, always remember to check the value via _VALUE to correctly hide blocks with empty dates.
Plugin developers can fully control the field's behaviour through the min, max, and format parameters, and can use hooks to extend functionality. Knowing the details of cot_build_extrafields, cot_import_extrafields, cot_selectbox_date, and cot_date enables efficient integration of this type into any modules and themes.
This article is based solely on the analysis of the Cotonti source code, and contains no assumptions or unverified information.
Comments (0)
Page Discussion in Telegram
Recommended Products and Services
Extrafields Users Custom
The Plugin Custom Extrafields for Pages module of Cotonti CMF
Extrafields Market Custom
Content author
Offline
Sodium Carbonate
Last logged: 2026-08-17 15:46
- Page published: 2026-08-16 13:02
- Last update: 2026-08-17 00:16
- Language:
Связанные статьи
Плагин xtradbrowusers — Руководство по тегам в шаблонах
Плагин 'xtradbrowusers' — Руководство по тегам в шаблонахИнтеграция и прописывание тегов для вывода
Press Release: xtradbrowusers Plugin Update to Version 1.2.9
📢 Press Release: xtradbrowusers Plugin Update to Version 1.2.9Date: August 14, 2026Version:
"Xtra db row market" v.4.0.0: Admin panel and integration with Market Mass Edit
Plugin xtradbrowmarket 4.0.0: own control panel and full integration with Market Mass EditDate:
Recommended forum topics for this article
Руководство по тегам в шаблонах - полная версия шпаргалка
Плагин “Custom Extrafields” - Памятка про установку демо экстраполей
Экстраполя в Cotonti: полное руководство по типу «Список с множественным выбором» (checklistbox)
Русский