sqlpostgresqldatestime

SQL CURRENT_DATE and CURRENT_TIME: Today's Date and Time of Day

CURRENT_DATE and CURRENT_TIME are parenthesis-free SQL specials: today's date and the time of day with its zone.

3 min readReferencesql · postgresql · dates · time · current-date

In SQL, CURRENT_DATE and CURRENT_TIME are not functions but special values: they return today's date and the current time of day without a single parenthesis. It sounds trivial, but the missing () and the result type are exactly what trip people up.

Special values, no parentheses

The SQL standard defines a handful of "special registers": CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP. You write them as a bare keyword — never CURRENT_DATE().

SELECT CURRENT_DATE, CURRENT_TIME;
-- 2026-06-17 | 18:42:07.512+02
  • CURRENT_DATE yields a date — year, month and day only.
  • CURRENT_TIME yields time with time zone (timetz for short) — a time of day plus a zone offset.
  • CURRENT_TIMESTAMP combines both: full date and time with zone (timestamptz).

All of them read the moment the transaction started, not the moment the row executes. Inside one transaction CURRENT_DATE will not "jump" past midnight between queries — that is standard behavior, not a bug.

Today in WHERE and reports

The most common use is selecting rows from today. You can compare created_at (a timestamptz) directly against CURRENT_DATE, but mind the boundaries: prefer a half-open range [today, tomorrow).

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

Orders from the last 30 days — arithmetic on CURRENT_DATE reads cleanly, no casts needed:

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

Gotcha: do not write DATE(created_at) = CURRENT_DATE over a large set. Wrapping DATE(...) around the column kills any index on created_at — the planner cannot use it and falls back to a seq scan. The >= ... AND < ... range keeps the index in play.

CURRENT_DATE as a DEFAULT

CURRENT_DATE is a perfect default for a date column: it is evaluated on every insert.

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

For "created at" fields you usually reach for CURRENT_TIMESTAMP (or now()) instead, to keep the time and zone too:

ALTER TABLE orders
  ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;

The key difference: CURRENT_DATE stores only the day and discards the time of day, while CURRENT_TIMESTAMP keeps the full moment. Pick the column type deliberately — date versus timestamptz.

CURRENT_TIME versus a full timestamp

CURRENT_TIME is the time of day, with no date attached. The timetz type carries a zone offset but not a date, and that is exactly where it gets error-prone.

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)
  • You cannot reliably subtract one timetz from another across a zone boundary — without a date, crossing midnight is ambiguous.
  • For "business hours" logic you normally take CURRENT_TIMESTAMP::time (a plain time, no zone), not CURRENT_TIME.
  • When you need both date and time, the right answer is almost always CURRENT_TIMESTAMP, not a CURRENT_DATE + CURRENT_TIME pair.

Example: count an order as daytime when it was created between 09:00 and 18:00 local time.

SELECT id, amount
FROM orders
WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';

Differences across engines

  • PostgreSQL: fully standard. CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP take no parentheses; now() is a synonym for CURRENT_TIMESTAMP.
  • MySQL: CURRENT_DATE and CURDATE() are equivalent, as are CURRENT_TIME and CURTIME(). MySQL's TIME type stores no zone, so there is no timetz here at all.
  • ClickHouse: offers today() and now(); CURRENT_TIMESTAMP is supported as an alias, but there is no standalone "time of day with zone" type.

If the code must be portable, stick to the parenthesis-free CURRENT_DATE/CURRENT_TIMESTAMP — the most widely understood form — and do not lean on timetz semantics outside PostgreSQL.

Practice on real tasks

Solve tasks in the SQL trainer with instant grading and hints.

Open trainer