Setting up store hours information in 1C-Bitrix
A user sees "Pickup" button and navigates to the list of points. Beside each address — nothing about hours, or worse: static text "Mon-Fri 9:00-18:00" that's accurate until the first change, but nobody knows which file to edit. Task — store hours in structured form and show actual "open/closed" status in real time.
Schedule storage structure
Standard table b_sale_store contains field SCHEDULE type TEXT — arbitrary string without structure. This doesn't work for machine processing.
Right approach: store schedule as JSON. Either add custom field to pickup point (via CUserTypeEntity with ENTITY_ID = 'SALE_STORE'), or create separate table:
CREATE TABLE bl_store_schedule (
id SERIAL PRIMARY KEY,
store_id INT NOT NULL REFERENCES b_sale_store(ID) ON DELETE CASCADE,
day_of_week SMALLINT NOT NULL, -- 1=Mon, 7=Sun
open_time TIME, -- NULL = closed that day
close_time TIME,
is_closed BOOLEAN DEFAULT FALSE,
UNIQUE (store_id, day_of_week)
);
This structure allows storing different schedule for each day of week and explicitly marking closed days via is_closed = TRUE.
Exceptions: holidays and temporary changes
Besides standard schedule you need exceptions table:
CREATE TABLE bl_store_schedule_exception (
id SERIAL PRIMARY KEY,
store_id INT NOT NULL,
date DATE NOT NULL,
open_time TIME,
close_time TIME,
is_closed BOOLEAN DEFAULT FALSE,
note VARCHAR(255),
UNIQUE (store_id, date)
);
When calculating "open/closed" status, first check bl_store_schedule_exception for today's date, and only if no exception take data from bl_store_schedule by day of week number.
Calculating current status
function getStoreStatus(int $storeId): array
{
$connection = \Bitrix\Main\Application::getConnection();
$now = new \DateTime('now', new \DateTimeZone('Europe/Minsk'));
$date = $now->format('Y-m-d');
$time = $now->format('H:i:s');
$dow = (int)$now->format('N'); // 1=Mon, 7=Sun
// First check exception for today
$exception = $connection->query(
"SELECT * FROM bl_store_schedule_exception
WHERE store_id = {$storeId} AND date = '{$date}'"
)->fetch();
$schedule = $exception ?: $connection->query(
"SELECT * FROM bl_store_schedule
WHERE store_id = {$storeId} AND day_of_week = {$dow}"
)->fetch();
if (!$schedule || $schedule['is_closed']) {
return ['status' => 'closed', 'label' => 'Closed'];
}
$isOpen = $time >= $schedule['open_time'] && $time < $schedule['close_time'];
return [
'status' => $isOpen ? 'open' : 'closed',
'label' => $isOpen
? 'Open until ' . substr($schedule['close_time'], 0, 5)
: 'Opens at ' . substr($schedule['open_time'], 0, 5),
'open' => $schedule['open_time'],
'close' => $schedule['close_time'],
];
}
Time zones for multi-location chains
If the chain spans multiple time zones — add timezone field to b_sale_store (custom field or extension). When calculating status create DateTime with correct DateTimeZone for each store. Storing schedule in UTC and converting for display is a common mistake that breaks during daylight saving time transitions.
Display in component and caching
Store list component bitrix:sale.store.list is extended via result_modifier.php. In it call getStoreStatus() for each point and add data to $arResult. Caching: "open/closed" status changes twice a day (opening and closing), so cache TTL — no more than 30 minutes. You can use tag cache with tag store_{$storeId}_schedule and invalidate it when updating schedule in admin.
What we configure
- Tables
bl_store_scheduleandbl_store_schedule_exception - Administrative interface for editing schedule (update agent or form in admin section)
- Status calculation function with time zone support
- Extension of
bitrix:sale.store.listcomponent viaresult_modifier.php - Caching with TTL 30 minutes and invalidation on schedule change







