Dates and time
Why do SYSDATE and CURRENT_DATE return different times in the same SuiteQL query?
Short answer
Two different clocks. SYSDATE follows NetSuite's server clock, which read UTC-07:00 in this run; CURRENT_DATE and CURRENT_TIMESTAMP follow the calling session's timezone. They came back ten hours apart in the same row, with no error.
They are two different clocks. SYSDATE is the database server's own clock, which read UTC-07:00 in this run. CURRENT_DATE and CURRENT_TIMESTAMP resolve against the calling session instead. In one row they were ten hours apart, with no error.
Minimal reproduction
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS') AS db_now,
TO_CHAR(CURRENT_DATE,'YYYY-MM-DD HH24:MI:SS') AS db_curdate,
TO_CHAR(CURRENT_TIMESTAMP,'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS db_ts,
SYSDATE AS raw_sysdate, CURRENT_DATE AS raw_curdate
FROM transaction FETCH FIRST 1 ROWS ONLY
One row back:
db_now: 2026-08-20 11:03:44
db_curdate: 2026-08-20 21:03:44
db_ts: 2026-08-20 21:03:44 +03:00
raw_sysdate: 8/20/2026
raw_curdate: 8/20/2026
True UTC at the moment of that call was 2026-08-20 18:03:32, independently observed. So SYSDATE sat at UTC-07:00 and CURRENT_DATE at UTC+03:00, which was the timezone of the workstation issuing the query.
Look at the last two columns. Selected raw, both pseudo-columns render as the same date-only string with no time at all, and TRUNC(SYSDATE) renders the same again. The ten-hour gap is invisible until you wrap them in TO_CHAR.
The fix
Pick one clock and use it everywhere in a single query. Where the boundary of a window actually matters, use neither: compute the boundary in your own code and pass it in as an explicit date literal.
SELECT COUNT(*) AS n FROM transaction
WHERE trandate >= TO_DATE('2026-01-01','YYYY-MM-DD')
That is the form the run exercised, and it involves no server clock and no session clock, so it returns the same rows for a caller in Europe and a caller in California. If you do need a clock inside the query, CURRENT_TIMESTAMP is the only one of the three that states its own offset, which makes it the only one you can check.
The run never executed date arithmetic inside a predicate. Neither SYSDATE - 1 nor CURRENT_DATE - 1 was tested, so this page recommends neither.
Why this happens
SYSDATE is the database server's own clock, and it read UTC-07:00 here, which is Pacific time in August. CURRENT_DATE and CURRENT_TIMESTAMP resolve against the session context, which is why they matched the caller's own timezone. This is one account at one moment, so treat the specific offset as observed rather than guaranteed; what generalises is that the two pseudo-columns are on different clocks.
The consequence is the part worth remembering. The gap is not a fixed constant, it is whatever separates the server clock from the caller's timezone, so the same query covers a different 24 hour slice for a user in Europe and a user in California. Nothing errors.
Stored datetimes are not on the SYSDATE clock. The most recently modified transaction in this account carried a lastmodifieddate later in the day than SYSDATE read at the same moment, and a stored value cannot sit in the server's future if it is on the server's clock. Comparing a stored datetime against SYSDATE is therefore the specific combination that was off by the full gap here.
Where evidence stops: this run cannot say which NetSuite setting drives the session offset, user preference or request context, because the whole run executed as one user in one session. The employee table has no timezone column to query, and Setup > Company > General Preferences was not read.
How to tell if you are affected
Under a minute, in your own account:
SELECT TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS') AS db_now,
TO_CHAR(CURRENT_DATE,'YYYY-MM-DD HH24:MI:SS') AS db_curdate
FROM transaction FETCH FIRST 1 ROWS ONLY
Two identical strings: that session is on the server clock, and mixing the two pseudo-columns costs you nothing there. Two different strings: every query in that session that mixes SYSDATE and CURRENT_DATE, or that compares a stored datetime against SYSDATE, is shifted by exactly the difference you just measured, and that difference is also the amount by which a nightly incremental window over-selects or under-selects rows.
Read the result as a fact about the session, not about the account. CURRENT_DATE follows the calling session, so a scheduled script, a middleware integration and your interactive query can each land on a different offset, and each one has to be measured where it runs. Selecting the pseudo-columns raw instead of through TO_CHAR will not show you any of this, because both render date-only.
Scope: one production account on NetSuite 2026.1, one session timezone. Entirely through N/query.runSuiteQL; not tested on the REST endpoint.
How this was established
7 probe runs against one live NetSuite 2026.1 account on the Administrator role, through N/query.runSuiteQL. Where this page draws a boundary around a finding, that boundary is the edge of what was actually run.
Related
- Why does my SuiteQL date come back as 8/20/2026 with no time?
Date values serialise as M/D/YYYY in this run and any time component is dropped. The time is still in the column: select TO_CHAR(col,'YYYY-MM-DD HH24:MI:SS'). A TO_DATE mask parses input, it does not control output.
- How do I compare a date column to a date literal in SuiteQL?
TO_DATE('2026-01-01','YYYY-MM-DD'), DATE '2026-01-01' and the bare string '1/1/2026' all work and returned identical counts. The bare ISO string '2026-01-01' fails with "Invalid or unsupported search". BETWEEN with TO_DATE on both sides works.