Verdict first: the PostgreSQL now function returns the moment your transaction started, as timestamp with time zone. It’s the same value as CURRENT_TIMESTAMP. When you need the real clock inside a long transaction, call clock_timestamp() instead.
Quick answer: SELECT now(); is all it takes. Here’s the type and the server version from my run:
SELECT now(),
pg_typeof(now()) AS type,
current_setting('server_version') AS version;
Output:
now | type | version
-------------------------------+--------------------------+---------
2026-10-28 03:25:08.459581-04 | timestamp with time zone | 18.6
(1 row)
My runs used PostgreSQL 18.6 on Windows with the session TimeZone at America/New_York, so you’ll see -04 offsets (EDT).
NOW() vs CURRENT_TIMESTAMP vs statement_timestamp() vs clock_timestamp()
You’ll read that now() is “the current time.” Not once a transaction spans two statements. Same transaction: four functions, a two-second pg_sleep, the same four again:
\pset footer off
BEGIN;
SELECT now()::time(3) AS now,
current_timestamp::time(3) AS current_ts,
statement_timestamp()::time(3) AS statement_ts,
clock_timestamp()::time(3) AS clock_ts;
SELECT pg_sleep(2) \gset
SELECT now()::time(3) AS now,
current_timestamp::time(3) AS current_ts,
statement_timestamp()::time(3) AS statement_ts,
clock_timestamp()::time(3) AS clock_ts;
COMMIT;
Output:
BEGIN
now | current_ts | statement_ts | clock_ts
--------------+--------------+--------------+--------------
03:25:08.561 | 03:25:08.561 | 03:25:08.561 | 03:25:08.563
now | current_ts | statement_ts | clock_ts
--------------+--------------+--------------+--------------
03:25:08.561 | 03:25:08.561 | 03:25:10.576 | 03:25:10.576
COMMIT

now() and CURRENT_TIMESTAMP never budged. statement_timestamp() moved with the new statement, and clock_timestamp() reads the clock at each call.
Why does NOW() not change inside a loop or a big SELECT?
Because a statement never leaves its transaction. Across rows, now() repeats while clock_timestamp() ticks:
SELECT g AS row_no,
now()::time(6) AS now,
clock_timestamp()::time(6) AS clock_ts
FROM generate_series(1, 2) AS g;
Output:
row_no | now | clock_ts
--------+-----------------+-----------------
1 | 03:25:10.676842 | 03:25:10.679198
2 | 03:25:10.676842 | 03:25:10.679205
(2 rows)
The same holds in a PL/pgSQL FOR loop: every iteration sees one now(), so timed steps all look instant.
Where the answer flips: pick clock_timestamp() when you measure elapsed time or log steps of a long job. Keep now() when every row of one change must share a timestamp.
NOW()::date, NOW()::time and CURRENT_DATE
Cast for the date or time of day; add an interval for a future timestamp:
SELECT now()::date AS today,
current_date AS current_date,
now()::time(0) AS time_now,
now() + interval '30 days' AS due_in_30_days;
Output:
today | current_date | time_now | due_in_30_days
------------+--------------+----------+-------------------------------
2026-10-28 | 2026-10-28 | 03:25:11 | 2026-11-27 03:25:10.777921-05
(1 row)
Note the -05 on the last column. Thirty days from 10/28/2026 crosses the 11/01/2026 DST change, so PostgreSQL kept the wall-clock time and shifted the offset. To cut to a day or month, use PostgreSQL date_trunc.
NOW() minus INTERVAL: rows from the last 7 days
The sample table holds six US orders, with created_at set relative to now():
CREATE TABLE orders (
order_id int PRIMARY KEY,
customer varchar(20) NOT NULL,
state char(2) NOT NULL,
amount numeric(10,2) NOT NULL,
created_at timestamptz NOT NULL -- 2 hours to 40 days ago
);
SELECT order_id, customer, state, amount
FROM orders
WHERE created_at >= now() - interval '7 days'
ORDER BY created_at DESC;
Output:
order_id | customer | state | amount
----------+-------------+-------+--------
101 | Megan Bowen | TX | 129.99
102 | Alex Wilber | WA | 54.50
103 | Adele Vance | NY | 310.00
(3 rows)
Order 104, seven days and three hours old, falls out. For whole calendar days, compare with current_date - 7 (midnight). More patterns: PostgreSQL dates between two dates.
NOW() as a column DEFAULT, and the ‘now’ string trap
DEFAULT now() runs at insert time. The string 'now' is converted once, at CREATE TABLE. I waited three seconds before inserting:
CREATE TABLE signups (
id int GENERATED ALWAYS AS IDENTITY,
email varchar(30) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
frozen_at timestamptz DEFAULT 'now'
);
SELECT pg_sleep(3) \gset
INSERT INTO signups (email) VALUES ('megan.bowen@example.com');
SELECT email, created_at - frozen_at AS gap
FROM signups;
Output:
CREATE TABLE
INSERT 0 1
email | gap
-------------------------+-----------------
megan.bowen@example.com | 00:00:03.014014
(1 row)

On a real table that gap grows to months. To fix a column, use ALTER TABLE with ALTER COLUMN frozen_at SET DEFAULT now().
NOW() time zone: SET TIME ZONE and AT TIME ZONE
now() is an absolute instant; the session TimeZone (America/New_York on my demo database) only decides how it prints:
\pset footer off
SELECT now()::timestamp(0) AS new_york,
(now() AT TIME ZONE 'UTC')::timestamp(0) AS utc;
SET TIME ZONE 'America/Chicago';
SELECT now()::timestamptz(0) AS now_tz,
now()::timestamp(0) AS now_cast;
Output:
new_york | utc
---------------------+---------------------
2026-10-28 03:25:14 | 2026-10-28 07:25:14
SET
now_tz | now_cast
------------------------+---------------------
2026-10-28 02:25:14-05 | 2026-10-28 02:25:14

AT TIME ZONE returns a plain timestamp in that zone. The catch: now()::timestamp follows the session, so Chicago got an hour earlier. Store that in a timestamp column and two sessions disagree.
That’s why I keep timestamptz columns. For text input, PostgreSQL to_timestamp parses strings into it.
NOW() precision: now(0) fails, CURRENT_TIMESTAMP(0) rounds
now() takes no arguments. The three ways to drop microseconds don’t agree:
SELECT current_timestamp(0) AS rounded,
date_trunc('second', now()) AS truncated,
now()::timestamptz(0) AS cast_0;
SELECT now(0);
Output and error:
rounded | truncated | cast_0
------------------------+------------------------+------------------------
2026-10-28 03:25:14-04 | 2026-10-28 03:25:14-04 | 2026-10-28 03:25:14-04
(1 row)
psql:now-precision-milliseconds.sql:4: ERROR: function now(integer) does not exist
LINE 1: SELECT now(0);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.

Rounding can land on the next second; truncating never does. If a report must never show a time later than the event, use date_trunc.
PostgreSQL current time functions compared
| Function | Returns | Value is fixed at |
|---|---|---|
now() | timestamptz | transaction start |
current_timestamp, transaction_timestamp() | timestamptz | transaction start |
statement_timestamp() | timestamptz | statement start |
clock_timestamp() | timestamptz | never (every call) |
timeofday() | text | never (every call) |
current_date | date | transaction start |
localtimestamp | timestamp | transaction start |
Speed isn’t a reason to pick one. For the SQL-standard spelling, see PostgreSQL CURRENT_TIMESTAMP.
Common mistakes with PostgreSQL NOW()
- Timing a long transaction with now(): the end time equals the start time.
DEFAULT 'now'instead ofDEFAULT now(), so every row gets the CREATE TABLE time.- Casting to
timestampand forgetting the session TimeZone decides the wall clock.
My rule of thumb: use now() when rows in one change must agree, clock_timestamp() when you’re measuring, and timestamptz columns everywhere. To measure the gap between two values, read PostgreSQL difference between two timestamps.
Next step, depending on what you’re building:
- Casting a PostgreSQL timestamp to a date
- Adding days and months to a date
- Pulling the year or hour out with date_part
- On SQL Server instead? CURRENT_TIMESTAMP in SQL Server
Frequently Asked Questions
Is NOW() the same as CURRENT_TIMESTAMP in PostgreSQL?
Yes. Both equal transaction_timestamp(). CURRENT_TIMESTAMP is the SQL-standard name, written without parentheses, and it accepts an optional precision (PostgreSQL 18 Current Date/Time docs).
How do I get the current UTC time in PostgreSQL?
Use now() AT TIME ZONE ‘UTC’, or the function form timezone(‘UTC’, now()). Both return a timestamp without time zone.
Does NOW() use the server clock or the client’s time zone?
The server clock supplies the instant. The session TimeZone, which a client can change with SET TIME ZONE or the PGTZ environment variable, only changes how it’s displayed.
What does timeofday() return in PostgreSQL?
The actual current time as a text string, like clock_timestamp() but not a timestamptz. It’s kept for backward compatibility.

Kumar Saurabh is a freelance software engineer who builds web and mobile apps with Next.js, React, Flutter and Python. From 2021 to 2024, Saurabh worked as an IT analyst and developer at TSInfo Technologies, writing technical articles on SQL and PostgreSQL. Saurabh writes the PostgreSQL, MySQL, MariaDB and MongoDB tutorials on DatabaseFAQs.com and holds a Master of Computer Applications (MCA) from Chandigarh University.