Dan l-artiklu bħalissa huwa bir-Russu — it-traduzzjoni bl-Ingliż għaddejja.
В SQL CURRENT_DATE и CURRENT_TIME — это не функции, а специальные значения: они возвращают сегодняшнюю дату и текущее время суток без единой скобки. Звучит как мелочь, но именно отсутствие () и тип результата каждый раз сбивают людей с толку.
Чаще всего эти значения встречаются в трёх местах: в фильтре WHERE («строки за сегодня», «за последние 30 дней»), в DEFAULT для колонок с датой создания и в выражениях, где нужен текущий момент времени. Главная развилка — какой из трёх вариантов брать. CURRENT_DATE даёт только дату (date), CURRENT_TIME — только время суток с поясом (timetz), а CURRENT_TIMESTAMP — полный момент (timestamptz). Перепутать их легко, и тогда фильтр по дню молча сравнивает date с timestamptz, а DEFAULT теряет время суток. К тому же все три значения вычисляются один раз на транзакцию, а не на каждую строку, поэтому в массовой вставке у всех строк будет одна и та же отметка времени. Ниже разберём каждое из трёх значений, его тип и типичные ловушки на границах суток и часового пояса.
Специальные значения без скобок
Стандарт SQL определяет горстку «специальных регистров»: CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP. Их пишут голым ключевым словом — никаких CURRENT_DATE().
SELECT CURRENT_DATE, CURRENT_TIME;
CURRENT_DATE даёт значение типа date — только год, месяц и день.
CURRENT_TIME даёт time with time zone (сокращённо timetz) — время суток плюс смещение пояса.
CURRENT_TIMESTAMP объединяет оба: полная дата и время с поясом (timestamptz).
Все они берут момент начала транзакции, а не момент исполнения строки. Внутри одной транзакции CURRENT_DATE не «перепрыгнет» полночь между запросами — это поведение по стандарту, а не баг.
Сегодня в WHERE и в отчётах
Самое частое применение — отобрать строки за сегодня. Сравнивать created_at (это timestamptz) напрямую с CURRENT_DATE можно, но аккуратно с границами: лучше брать полуинтервал «сегодня включительно, завтра уже нет».
SELECT id, email, created_at
FROM users
WHERE created_at >= CURRENT_DATE
AND created_at < CURRENT_DATE + INTERVAL '1 day';
Заказы за последние 30 дней — арифметика с CURRENT_DATE читается без приведений типов:
SELECT id, user_id, amount, status
FROM orders
WHERE status = 'paid'
AND created_at >= CURRENT_DATE - INTERVAL '30 days';
Ловушка: не пишите DATE(created_at) = CURRENT_DATE в большом наборе. Обёртка DATE(...) вокруг столбца убивает индекс по created_at — планировщик не сможет использовать его и пойдёт в seq scan. Диапазон >= ... AND < ... сохраняет индекс.
CURRENT_DATE как DEFAULT
CURRENT_DATE идеально подходит на роль значения по умолчанию для колонки-даты: оно вычисляется при каждой вставке.
CREATE TABLE signups (
user_id bigint PRIMARY KEY,
signed_on date NOT NULL DEFAULT CURRENT_DATE
);
INSERT INTO signups (user_id) VALUES (101);
Для полей «когда создано» чаще берут CURRENT_TIMESTAMP (или now()), чтобы сохранить и время с поясом:
ALTER TABLE orders
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;
Ключевая разница: CURRENT_DATE хранит только сутки и теряет время дня, а CURRENT_TIMESTAMP сохраняет полный момент. Выбирайте тип колонки осознанно — date против timestamptz.
CURRENT_TIME против полного timestamp
CURRENT_TIME — это именно время суток, без даты. Тип timetz несёт смещение пояса, но не несёт самой даты, и из-за этого с ним легко наделать ошибок.
SELECT CURRENT_TIME AS clock,
CURRENT_TIMESTAMP AS full_moment,
CURRENT_TIMESTAMP::time AS local_clock;
- Нельзя надёжно вычесть один
timetz из другого через границу пояса — без даты переход через полночь неоднозначен.
- Для бизнес-логики «рабочие часы» обычно берут
CURRENT_TIMESTAMP::time (тип time без пояса), а не CURRENT_TIME.
- Если нужна и дата, и время — почти всегда правильный ответ это
CURRENT_TIMESTAMP, а не пара CURRENT_DATE + CURRENT_TIME.
Пример: смена считается дневной, если заказ создан между 09:00 и 18:00 по локальному времени.
SELECT id, amount
FROM orders
WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';
Различия в других СУБД
- PostgreSQL: всё по стандарту.
CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP — без скобок; now() — синоним CURRENT_TIMESTAMP.
- MySQL:
CURRENT_DATE и CURDATE() эквивалентны, CURRENT_TIME и CURTIME() тоже. Тип TIME в MySQL не хранит пояс, так что timetz тут просто нет.
- ClickHouse: есть
today() и now(); CURRENT_TIMESTAMP поддержан как алиас, а отдельного «времени суток с поясом» как самостоятельного типа нет.
Главный источник ошибок с CURRENT_DATE и CURRENT_TIMESTAMP — не сам синтаксис, а часовой пояс и границы суток. Все эти значения берут момент начала транзакции, поэтому в одной длинной транзакции, начатой в 23:59, CURRENT_DATE так и останется «вчерашним», даже если строки вставляются уже после полуночи. А значение CURRENT_DATE зависит от пояса сессии: при SET timezone тот же абсолютный момент может попасть в соседние сутки, и фильтр «за сегодня» сдвинется на день. Поэтому для отчётов по дню стоит явно фиксировать пояс — например, сравнивать created_at AT TIME ZONE 'UTC' с границами, посчитанными в том же поясе, а не полагаться на пояс по умолчанию.
Отдельная ловушка — CURRENT_TIME и его тип timetz. Вычитать одно значение timetz из другого через полночь бессмысленно: без даты непонятно, прошли сутки или нет, а смещение пояса в timetz ещё и не учитывает переход на летнее время. Поэтому в PostgreSQL тип timetz считается скорее наследием, чем рабочим инструментом, и в схемах его почти не держат. Если нужно сравнивать время суток (рабочие часы, окна обслуживания), почти всегда правильнее взять CURRENT_TIMESTAMP::time — чистый time без пояса — и держать дату отдельно в CURRENT_DATE или прямо в CURRENT_TIMESTAMP. А если важен сам факт «когда это произошло», храните CURRENT_TIMESTAMP целиком и режьте его на дату и время уже в запросе.
Если код должен быть переносимым, держитесь CURRENT_DATE/CURRENT_TIMESTAMP без скобок — это самый широко понятый вариант — и не полагайтесь на семантику timetz за пределами PostgreSQL.
В SQL
CURRENT_DATEиCURRENT_TIME— это не функции, а специальные значения: они возвращают сегодняшнюю дату и текущее время суток без единой скобки. Звучит как мелочь, но именно отсутствие()и тип результата каждый раз сбивают людей с толку.Чаще всего эти значения встречаются в трёх местах: в фильтре
WHERE(«строки за сегодня», «за последние 30 дней»), вDEFAULTдля колонок с датой создания и в выражениях, где нужен текущий момент времени. Главная развилка — какой из трёх вариантов брать.CURRENT_DATEдаёт только дату (date),CURRENT_TIME— только время суток с поясом (timetz), аCURRENT_TIMESTAMP— полный момент (timestamptz). Перепутать их легко, и тогда фильтр по дню молча сравниваетdateсtimestamptz, а DEFAULT теряет время суток. К тому же все три значения вычисляются один раз на транзакцию, а не на каждую строку, поэтому в массовой вставке у всех строк будет одна и та же отметка времени. Ниже разберём каждое из трёх значений, его тип и типичные ловушки на границах суток и часового пояса.Специальные значения без скобок
Стандарт SQL определяет горстку «специальных регистров»:
CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,LOCALTIME,LOCALTIMESTAMP. Их пишут голым ключевым словом — никакихCURRENT_DATE().SELECT CURRENT_DATE, CURRENT_TIME; -- 2026-06-17 | 18:42:07.512+02CURRENT_DATEдаёт значение типаdate— только год, месяц и день.CURRENT_TIMEдаётtime with time zone(сокращённоtimetz) — время суток плюс смещение пояса.CURRENT_TIMESTAMPобъединяет оба: полная дата и время с поясом (timestamptz).Все они берут момент начала транзакции, а не момент исполнения строки. Внутри одной транзакции
CURRENT_DATEне «перепрыгнет» полночь между запросами — это поведение по стандарту, а не баг.Сегодня в WHERE и в отчётах
Самое частое применение — отобрать строки за сегодня. Сравнивать
created_at(этоtimestamptz) напрямую сCURRENT_DATEможно, но аккуратно с границами: лучше брать полуинтервал «сегодня включительно, завтра уже нет».SELECT id, email, created_at FROM users WHERE created_at >= CURRENT_DATE AND created_at < CURRENT_DATE + INTERVAL '1 day';Заказы за последние 30 дней — арифметика с
CURRENT_DATEчитается без приведений типов:SELECT id, user_id, amount, status FROM orders WHERE status = 'paid' AND created_at >= CURRENT_DATE - INTERVAL '30 days';CURRENT_DATE как DEFAULT
CURRENT_DATEидеально подходит на роль значения по умолчанию для колонки-даты: оно вычисляется при каждой вставке.CREATE TABLE signups ( user_id bigint PRIMARY KEY, signed_on date NOT NULL DEFAULT CURRENT_DATE ); INSERT INTO signups (user_id) VALUES (101); -- signed_on = today, filled automaticallyДля полей «когда создано» чаще берут
CURRENT_TIMESTAMP(илиnow()), чтобы сохранить и время с поясом:ALTER TABLE orders ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;Ключевая разница:
CURRENT_DATEхранит только сутки и теряет время дня, аCURRENT_TIMESTAMPсохраняет полный момент. Выбирайте тип колонки осознанно —dateпротивtimestamptz.CURRENT_TIME против полного timestamp
CURRENT_TIME— это именно время суток, без даты. Типtimetzнесёт смещение пояса, но не несёт самой даты, и из-за этого с ним легко наделать ошибок.SELECT CURRENT_TIME AS clock, -- 18:42:07.512+02 (timetz) CURRENT_TIMESTAMP AS full_moment, -- 2026-06-17 18:42:07.512+02 CURRENT_TIMESTAMP::time AS local_clock; -- 18:42:07.512 (plain time)timetzиз другого через границу пояса — без даты переход через полночь неоднозначен.CURRENT_TIMESTAMP::time(типtimeбез пояса), а неCURRENT_TIME.CURRENT_TIMESTAMP, а не параCURRENT_DATE+CURRENT_TIME.Пример: смена считается дневной, если заказ создан между 09:00 и 18:00 по локальному времени.
SELECT id, amount FROM orders WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';Различия в других СУБД
CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP— без скобок;now()— синонимCURRENT_TIMESTAMP.CURRENT_DATEиCURDATE()эквивалентны,CURRENT_TIMEиCURTIME()тоже. ТипTIMEв MySQL не хранит пояс, так чтоtimetzтут просто нет.today()иnow();CURRENT_TIMESTAMPподдержан как алиас, а отдельного «времени суток с поясом» как самостоятельного типа нет.Главный источник ошибок с
CURRENT_DATEиCURRENT_TIMESTAMP— не сам синтаксис, а часовой пояс и границы суток. Все эти значения берут момент начала транзакции, поэтому в одной длинной транзакции, начатой в 23:59,CURRENT_DATEтак и останется «вчерашним», даже если строки вставляются уже после полуночи. А значениеCURRENT_DATEзависит от пояса сессии: приSET timezoneтот же абсолютный момент может попасть в соседние сутки, и фильтр «за сегодня» сдвинется на день. Поэтому для отчётов по дню стоит явно фиксировать пояс — например, сравниватьcreated_at AT TIME ZONE 'UTC'с границами, посчитанными в том же поясе, а не полагаться на пояс по умолчанию.Отдельная ловушка —
CURRENT_TIMEи его типtimetz. Вычитать одно значениеtimetzиз другого через полночь бессмысленно: без даты непонятно, прошли сутки или нет, а смещение пояса вtimetzещё и не учитывает переход на летнее время. Поэтому в PostgreSQL типtimetzсчитается скорее наследием, чем рабочим инструментом, и в схемах его почти не держат. Если нужно сравнивать время суток (рабочие часы, окна обслуживания), почти всегда правильнее взятьCURRENT_TIMESTAMP::time— чистыйtimeбез пояса — и держать дату отдельно вCURRENT_DATEили прямо вCURRENT_TIMESTAMP. А если важен сам факт «когда это произошло», хранитеCURRENT_TIMESTAMPцеликом и режьте его на дату и время уже в запросе.Если код должен быть переносимым, держитесь
CURRENT_DATE/CURRENT_TIMESTAMPбез скобок — это самый широко понятый вариант — и не полагайтесь на семантикуtimetzза пределами PostgreSQL.