sqlpostgresqldatestime

CURRENT_DATE и CURRENT_TIME в SQL: текущая дата, время и полный момент

CURRENT_DATE, CURRENT_TIME и CURRENT_TIMESTAMP — значения SQL без скобок: дата, время суток с поясом и полный момент. Разбираем типы, фильтры WHERE и DEFAULT.

11 мин чтенияСправочникsql · postgresql · dates · time · current-date

CURRENT_DATE и CURRENT_TIME в SQL возвращают текущую дату и текущее время. Выглядят они необычно: без скобок, как будто это не функции, а специальные ключевые слова.

SELECT
  CURRENT_DATE AS today,
  CURRENT_TIME AS clock_time;

Результат может быть таким:

today      | clock_time
-----------+----------------
2026-06-17 | 18:42:07.512+02

На практике рядом с ними почти всегда всплывает ещё одно значение — CURRENT_TIMESTAMP. Оно возвращает не просто дату или время, а полный момент: дату, время и часовой пояс.

Эти три значения легко перепутать:

  • CURRENT_DATE — только дата;
  • CURRENT_TIME — только время суток с часовым поясом;
  • CURRENT_TIMESTAMP — дата и время с часовым поясом.

Путаница кажется мелкой, но из неё рождаются неприятные ошибки: фильтр «за сегодня» захватывает не те строки, колонка created_at теряет время суток, а длинная транзакция после полуночи продолжает жить со «вчерашней» датой.

Разберём всё спокойно и по-человечески.

Это не обычные функции

В PostgreSQL CURRENT_DATE, CURRENT_TIME и CURRENT_TIMESTAMP пишутся без скобок.

Правильно:

SELECT
  CURRENT_DATE,
  CURRENT_TIME,
  CURRENT_TIMESTAMP;

Не нужно писать так:

SELECT
  CURRENT_DATE(),
  CURRENT_TIME();

В обычной работе новичка это один из первых странных моментов: почти все функции в SQL вызываются со скобками, а эти значения — нет.

Причина в том, что это специальные значения SQL. Их можно воспринимать как встроенные «переменные текущего времени», которые база данных умеет подставлять сама.

Что именно возвращает каждое значение

Главная разница между ними — в типе результата.

Значение Что возвращает Тип в PostgreSQL
CURRENT_DATE текущую дату date
CURRENT_TIME текущее время суток с поясом time with time zone
CURRENT_TIMESTAMP текущую дату и время с поясом timestamp with time zone

Посмотрим на примере:

SELECT
  CURRENT_DATE AS current_date_value,
  CURRENT_TIME AS current_time_value,
  CURRENT_TIMESTAMP AS current_timestamp_value;

Результат может выглядеть так:

current_date_value | current_time_value | current_timestamp_value
-------------------+--------------------+-------------------------------
2026-06-17         | 18:42:07.512+02    | 2026-06-17 18:42:07.512+02

CURRENT_DATE отвечает на вопрос: «Какое сегодня число?»

CURRENT_TIME отвечает на вопрос: «Который сейчас час?»

CURRENT_TIMESTAMP отвечает на вопрос: «Какой сейчас полный момент времени?»

В реальных таблицах для событий чаще нужен именно CURRENT_TIMESTAMP, потому что событие обычно произошло не просто «17 июня», а в конкретный момент: 2026-06-17 18:42:07.

CURRENT_DATE: когда нужна только дата

CURRENT_DATE возвращает только дату: год, месяц и день.

Например:

SELECT CURRENT_DATE AS today;

Результат:

today
----------
2026-06-17

Время суток в этом значении отсутствует. Нет ни часов, ни минут, ни секунд.

Это удобно для вещей, где время действительно не важно:

  • дата регистрации в отдельной колонке;
  • дата отчёта;
  • дата рождения;
  • дата списания;
  • календарный день события.

Например, можно создать таблицу регистраций, где дата заполняется автоматически:

CREATE TABLE signups (
  user_id bigint PRIMARY KEY,
  signed_on date NOT NULL DEFAULT CURRENT_DATE
);

Теперь при вставке строки можно не указывать signed_on:

INSERT INTO signups (user_id)
VALUES (101);

PostgreSQL сам подставит текущую дату.

Это хороший вариант, если вам важно только число в календаре. Но если нужно знать ещё и точное время регистрации, CURRENT_DATE уже не подходит.

CURRENT_TIMESTAMP: когда нужен полный момент

Для колонок вроде created_at, updated_at, paid_at, sent_at почти всегда нужен полный момент времени.

То есть не просто дата, а дата плюс время.

CREATE TABLE orders (
  id bigint PRIMARY KEY,
  amount numeric(10, 2) NOT NULL,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Теперь при создании заказа база сохранит момент вставки:

INSERT INTO orders (id, amount)
VALUES (1, 990.00);

В created_at попадёт значение вроде:

2026-06-17 18:42:07.512+02

Разница между CURRENT_DATE и CURRENT_TIMESTAMP здесь принципиальная.

Если поставить для created_at значение по умолчанию CURRENT_DATE, время суток потеряется:

CREATE TABLE bad_orders (
  id bigint PRIMARY KEY,
  created_at timestamptz NOT NULL DEFAULT CURRENT_DATE
);

Так делать обычно не стоит. База всё равно приведёт дату к типу timestamptz, но время будет началом суток. В аналитике потом может оказаться, что все заказы будто созданы ровно в полночь.

Для даты — CURRENT_DATE.

Для точного момента — CURRENT_TIMESTAMP.

CURRENT_TIME: только время суток

CURRENT_TIME возвращает время суток с часовым поясом.

SELECT CURRENT_TIME AS clock_time;

Результат:

clock_time
----------------
18:42:07.512+02

Здесь есть часы, минуты, секунды и смещение часового пояса. Но нет даты.

Это важное ограничение. Без даты нельзя надёжно понять, что было раньше или позже, если события пересекают полночь.

Например, смена началась в 23:00, а закончилась в 02:00. По одному только времени суток непонятно, это два часа ночи того же дня или уже следующего.

Поэтому CURRENT_TIME используют реже, чем кажется. Для бизнес-логики часто удобнее взять время из полного момента:

SELECT CURRENT_TIMESTAMP::time AS local_clock;

Так мы получаем обычное локальное время без часового пояса:

local_clock
--------------
18:42:07.512

Для условий вроде «сейчас рабочее время» такой вариант часто проще.

Пример: проверить рабочие часы

Допустим, бизнес считает рабочим временем промежуток с 09:00 до 18:00.

SELECT
  CURRENT_TIMESTAMP::time BETWEEN TIME '09:00' AND TIME '18:00' AS is_work_time;

Результат:

is_work_time
------------
true

Здесь мы берём полный текущий момент, приводим его к типу time и сравниваем только время суток.

А вот для хранения события лучше всё равно сохранять полный timestamptz, а не одно время:

CREATE TABLE support_tickets (
  id bigint PRIMARY KEY,
  subject text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Сохранить только время — всё равно что записать в журнале: «событие случилось в 14:30», но забыть день.

Как выбрать между CURRENT_DATE, CURRENT_TIME и CURRENT_TIMESTAMP

Самый простой способ выбрать — спросить себя, что именно вы хотите сохранить или сравнить.

Задача Лучше выбрать
Нужно сегодняшнее число CURRENT_DATE
Нужно время суток CURRENT_TIME или CURRENT_TIMESTAMP::time
Нужно сохранить момент создания строки CURRENT_TIMESTAMP
Нужно поле created_at CURRENT_TIMESTAMP
Нужно поле signed_on или report_date CURRENT_DATE
Нужно отфильтровать строки за сегодня диапазон от CURRENT_DATE до завтрашнего дня
Нужно посчитать последние 30 дней CURRENT_DATE - INTERVAL '30 days' или CURRENT_TIMESTAMP - INTERVAL '30 days'

В большинстве продуктовых таблиц created_at и updated_at должны быть timestamptz с DEFAULT CURRENT_TIMESTAMP.

А date подходит, когда время действительно не имеет смысла.

Строки за сегодня: правильный фильтр

Очень частая задача: выбрать пользователей, которые зарегистрировались сегодня.

Предположим, в таблице users есть колонка created_at типа timestamptz.

Хороший фильтр выглядит так:

SELECT
  id,
  email,
  created_at
FROM users
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Это называется полуинтервал:

  • нижняя граница включается;
  • верхняя граница не включается.

То есть мы берём всё начиная с сегодняшней полуночи и строго до завтрашней полуночи.

Такой подход лучше, чем пытаться сравнивать дату через преобразование колонки.

Плохой вариант для большой таблицы:

SELECT
  id,
  email,
  created_at
FROM users
WHERE created_at::date = CURRENT_DATE;

Он читается приятно, но может быть медленнее на больших данных. Почему?

Потому что мы оборачиваем колонку created_at в преобразование ::date. Если на created_at есть обычный индекс, базе сложнее использовать его напрямую. Ей приходится вычислять дату для строк, а не просто идти по диапазону значений.

Лучше писать так:

SELECT
  id,
  email,
  created_at
FROM users
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Диапазон хорошо дружит с индексом по created_at.

Заказы за последние 30 дней

Для отчёта за последние 30 дней можно использовать арифметику дат.

SELECT
  id,
  user_id,
  amount,
  status,
  created_at
FROM orders
WHERE status = 'paid'
  AND created_at >= CURRENT_DATE - INTERVAL '30 days';

Такой запрос берёт оплаченные заказы, созданные начиная с даты 30 дней назад.

Если нужна точность до текущей секунды, используйте CURRENT_TIMESTAMP:

SELECT
  id,
  user_id,
  amount,
  status,
  created_at
FROM orders
WHERE status = 'paid'
  AND created_at >= CURRENT_TIMESTAMP - INTERVAL '30 days';

Разница тонкая, но важная.

CURRENT_DATE - INTERVAL '30 days' даёт начало календарного дня 30 дней назад.

CURRENT_TIMESTAMP - INTERVAL '30 days' даёт момент ровно 30 дней назад от текущего времени.

Например, если сейчас 2026-06-17 18:42, то:

CURRENT_DATE - INTERVAL '30 days'       -> 2026-05-18 00:00
CURRENT_TIMESTAMP - INTERVAL '30 days'  -> 2026-05-18 18:42

Для календарных отчётов чаще подходит CURRENT_DATE. Для точных скользящих окон — CURRENT_TIMESTAMP.

Почему CURRENT_DATE не меняется внутри транзакции

В PostgreSQL CURRENT_DATE, CURRENT_TIME и CURRENT_TIMESTAMP берут момент начала текущей транзакции.

Это значит: внутри одной транзакции они остаются стабильными.

BEGIN;

SELECT CURRENT_TIMESTAMP;

SELECT CURRENT_TIMESTAMP;

COMMIT;

Оба запроса внутри транзакции вернут один и тот же текущий момент с точки зрения транзакции.

На первый взгляд это странно: время-то в реальном мире идёт. Но для базы данных такое поведение полезно. Если один большой запрос вставляет тысячу строк, у всех строк будет одинаковое значение created_at, а не чуть-чуть разное время на каждой строке.

Например:

INSERT INTO events (name, created_at)
SELECT
  name,
  CURRENT_TIMESTAMP
FROM imported_events;

Все вставленные строки получат одну и ту же отметку времени.

Это не баг, а нормальное поведение.

Если вам нужно именно «реальное время прямо сейчас» в момент вызова, в PostgreSQL есть другие функции, например clock_timestamp(). Но для created_at чаще как раз нужна стабильность CURRENT_TIMESTAMP.

Полночь и длинные транзакции

Из-за стабильности внутри транзакции есть важная ловушка.

Представьте, транзакция началась в 23:59.

BEGIN;

SELECT CURRENT_DATE;

Результат:

2026-06-17

Потом наступила полночь, но транзакция всё ещё открыта.

SELECT CURRENT_DATE;

Результат всё равно будет:

2026-06-17

Хотя на календаре уже 2026-06-18.

Это особенно важно для долгих фоновых задач, миграций, импортов и больших транзакций. Если значение «сегодня» должно обновляться после полуночи, не держите транзакцию открытой слишком долго или используйте подходящие функции времени осознанно.

Часовой пояс: почему сегодня может быть разным

CURRENT_DATE зависит от часового пояса сессии.

Один и тот же абсолютный момент может быть разной календарной датой в разных часовых поясах.

Например, в одном поясе уже наступило 18 июня, а в другом ещё 17 июня.

В PostgreSQL часовой пояс сессии можно поменять:

SET TIME ZONE 'UTC';

SELECT CURRENT_DATE;
SET TIME ZONE 'Europe/Berlin';

SELECT CURRENT_DATE;

В некоторых ситуациях дата может отличаться.

Это важно для отчётов «за сегодня». Если продукт международный, нужно заранее решить, что значит «сегодня»:

  • сегодня по UTC;
  • сегодня по Москве;
  • сегодня по часовому поясу пользователя;
  • сегодня по часовому поясу компании.

Без этого отчёт может быть формально правильным, но бизнес будет видеть «съехавшие» сутки.

Аккуратный фильтр по дню в нужном часовом поясе

Допустим, все события хранятся в timestamptz, а отчёт нужно строить по дню в UTC.

Можно привести момент к нужному поясу и сравнивать с календарной датой:

SELECT
  id,
  created_at
FROM events
WHERE (created_at AT TIME ZONE 'UTC')::date = CURRENT_DATE;

Такой вариант понятен, но для больших таблиц снова может быть неидеален из-за преобразования колонки в WHERE.

Для производительности часто лучше заранее посчитать границы дня в нужном поясе и сравнить created_at диапазоном. Главное — не смешивать разные часовые пояса в одном условии.

Базовая идея такая: если отчёт «за сегодня» важен для бизнеса, явно фиксируйте часовой пояс, а не надейтесь на настройки подключения по умолчанию.

DEFAULT: что ставить в таблицах

Разберём типичные колонки.

Дата регистрации без времени

Если нужна только дата:

CREATE TABLE signups (
  user_id bigint PRIMARY KEY,
  signed_on date NOT NULL DEFAULT CURRENT_DATE
);

Хорошие имена для таких колонок:

  • signed_on;
  • report_date;
  • birth_date;
  • billing_date.

Суффикс _on часто намекает, что хранится дата без времени.

Момент создания строки

Если нужен точный момент:

CREATE TABLE orders (
  id bigint PRIMARY KEY,
  amount numeric(10, 2) NOT NULL,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Хорошие имена:

  • created_at;
  • updated_at;
  • paid_at;
  • sent_at;
  • deleted_at.

Суффикс _at обычно намекает, что хранится дата и время.

Это не строгое правило SQL, но хороший стиль схемы. Он помогает читать таблицы без лишних вопросов.

CURRENT_TIMESTAMP и now()

В PostgreSQL часто встречается функция now().

SELECT now();

По смыслу в PostgreSQL она соответствует CURRENT_TIMESTAMP: возвращает текущий момент на начало транзакции.

Поэтому такие варианты часто взаимозаменяемы:

CREATE TABLE messages (
  id bigint PRIMARY KEY,
  body text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE messages (
  id bigint PRIMARY KEY,
  body text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Если хочется писать ближе к стандартному SQL, используйте CURRENT_TIMESTAMP.

Если вы работаете в PostgreSQL-проекте и команда привыкла к now(), это тоже нормальный вариант.

CURRENT_TIME и timetz: почему с ним осторожно

В PostgreSQL CURRENT_TIME возвращает тип time with time zone, или коротко timetz.

На бумаге это выглядит полезно: время суток плюс смещение часового пояса.

На практике timetz часто неудобен, потому что в нём нет даты. А без даты нельзя корректно учитывать многие вещи:

  • переход через полночь;
  • переход на летнее или зимнее время;
  • длительность между двумя событиями;
  • реальный порядок событий.

Например, само по себе значение 02:30+02 не говорит, в какой день оно произошло. А значит, для событий, логов, заказов и платежей оно почти всегда слишком бедное.

Если важен момент события — храните timestamptz.

Если важно только локальное время в расписании — часто достаточно time.

Например, расписание работы магазина:

CREATE TABLE store_hours (
  store_id bigint NOT NULL,
  opens_at time NOT NULL,
  closes_at time NOT NULL
);

А событие создания заказа:

CREATE TABLE orders (
  id bigint PRIMARY KEY,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Не стоит хранить событие как один CURRENT_TIME: потом вы не поймёте, в какой день оно случилось.

CURRENT_DATE в арифметике

С датой удобно делать простую арифметику.

Завтра:

SELECT CURRENT_DATE + INTERVAL '1 day' AS tomorrow_start;

Неделю назад:

SELECT CURRENT_DATE - INTERVAL '7 days' AS week_ago;

Начало сегодняшнего дня:

SELECT CURRENT_DATE::timestamp AS today_start;

Начало завтрашнего дня:

SELECT (CURRENT_DATE + INTERVAL '1 day') AS tomorrow_start;

Это часто используется в фильтрах:

SELECT
  id,
  created_at
FROM orders
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Такой запрос читается почти как обычная фраза: «создано сегодня или позже, но раньше завтрашнего дня».

Граница BETWEEN: почему лучше полуинтервал

Иногда хочется написать так:

SELECT
  id,
  created_at
FROM orders
WHERE created_at BETWEEN CURRENT_DATE
  AND CURRENT_DATE + INTERVAL '1 day';

Но для временных диапазонов лучше привыкнуть к форме:

SELECT
  id,
  created_at
FROM orders
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Почему?

BETWEEN включает обе границы. Значит, значение ровно в завтрашнюю полночь тоже попадёт в результат. А оно уже относится к следующему дню.

Полуинтервал >= и < аккуратнее:

  • сегодняшняя полночь входит;
  • все моменты сегодняшнего дня входят;
  • завтрашняя полночь уже не входит.

Это маленькая привычка, которая спасает много отчётов.

MySQL: похожие значения и функции

В MySQL есть похожие значения и функции.

Например:

SELECT
  CURRENT_DATE,
  CURRENT_TIME,
  CURRENT_TIMESTAMP;

Также часто используют функции:

SELECT
  CURDATE(),
  CURTIME(),
  NOW();

Обычно:

  • CURDATE() возвращает текущую дату;
  • CURTIME() возвращает текущее время;
  • NOW() возвращает текущие дату и время.

Но есть отличие от PostgreSQL: в MySQL нет такого же типа timetz, как в PostgreSQL. Тип TIME не хранит часовой пояс как отдельную часть значения.

Если вы пишете переносимый SQL, лучше не строить логику вокруг timetz.

ClickHouse: today и now

В ClickHouse часто используют функции:

SELECT
  today(),
  now();

today() возвращает текущую дату.

now() возвращает текущие дату и время.

Также в ClickHouse поддерживается CURRENT_TIMESTAMP как привычный SQL-вариант.

Например:

SELECT
  today() AS current_day,
  now() AS current_moment;

Для аналитических запросов в ClickHouse обычно явно выбирают функцию под задачу: дата для дневных отчётов, полный момент для точного времени.

Сравнение PostgreSQL, MySQL и ClickHouse

Задача PostgreSQL MySQL ClickHouse
Текущая дата CURRENT_DATE CURRENT_DATE или CURDATE() today()
Текущее время CURRENT_TIME CURRENT_TIME или CURTIME() обычно извлекают из now()
Текущий момент CURRENT_TIMESTAMP или now() CURRENT_TIMESTAMP или NOW() now() или CURRENT_TIMESTAMP
Тип с временем и поясом timestamptz зависит от типа колонки и настроек DateTime с настройками пояса

Главная идея переносимости простая: CURRENT_DATE и CURRENT_TIMESTAMP понятны многим СУБД, но детали типов, часовых поясов и точности отличаются.

Частые ошибки

Поставить CURRENT_DATE в created_at

Плохо:

CREATE TABLE orders (
  id bigint PRIMARY KEY,
  created_at timestamptz NOT NULL DEFAULT CURRENT_DATE
);

Лучше:

CREATE TABLE orders (
  id bigint PRIMARY KEY,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

created_at обычно должен хранить полный момент, а не только дату.

Фильтровать день через приведение колонки

Может быть медленно на большой таблице:

SELECT
  id,
  created_at
FROM orders
WHERE created_at::date = CURRENT_DATE;

Лучше диапазоном:

SELECT
  id,
  created_at
FROM orders
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Использовать BETWEEN для всего дня

Опасный вариант:

SELECT
  id,
  created_at
FROM orders
WHERE created_at BETWEEN CURRENT_DATE
  AND CURRENT_DATE + INTERVAL '1 day';

Лучше:

SELECT
  id,
  created_at
FROM orders
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Так завтрашняя полночь не попадёт в сегодняшний отчёт.

Хранить событие как CURRENT_TIME

Плохо для событий:

CREATE TABLE events (
  id bigint PRIMARY KEY,
  created_time timetz NOT NULL DEFAULT CURRENT_TIME
);

Лучше:

CREATE TABLE events (
  id bigint PRIMARY KEY,
  created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Время без даты редко достаточно для события.

Забыть про часовой пояс

Запрос «за сегодня» зависит от того, в каком часовом поясе база считает текущую дату.

SELECT CURRENT_DATE;

Для международных продуктов заранее решайте, чей день вы считаете: UTC, день пользователя или день бизнеса.

Главное из статьи

CURRENT_DATE, CURRENT_TIME и CURRENT_TIMESTAMP — специальные значения SQL для текущей даты и времени.

Их пишут без скобок:

SELECT
  CURRENT_DATE,
  CURRENT_TIME,
  CURRENT_TIMESTAMP;

Разница такая:

  • CURRENT_DATE возвращает только дату;
  • CURRENT_TIME возвращает только время суток с часовым поясом;
  • CURRENT_TIMESTAMP возвращает полный момент: дату, время и часовой пояс.

Для колонок created_at, updated_at, paid_at обычно нужен CURRENT_TIMESTAMP.

Для колонок с одной календарной датой подходит CURRENT_DATE.

Для фильтра «за сегодня» лучше использовать диапазон:

SELECT
  id,
  created_at
FROM orders
WHERE created_at >= CURRENT_DATE
  AND created_at < CURRENT_DATE + INTERVAL '1 day';

Главные тонкости:

  • не ставьте CURRENT_DATE туда, где нужно сохранить точное время;
  • не оборачивайте большую индексированную колонку в created_at::date, если можно написать диапазон;
  • не забывайте про часовой пояс;
  • внутри одной транзакции текущая дата и время стабильны;
  • CURRENT_TIME без даты редко подходит для хранения событий;
  • для полного момента в PostgreSQL можно использовать CURRENT_TIMESTAMP или now().

Если говорить совсем просто: CURRENT_DATE — это «какой сегодня день», CURRENT_TIME — «который сейчас час», а CURRENT_TIMESTAMP — «какой сейчас точный момент».

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр