Skip to content

fix: compare naive timestamps with CURRENT_TIMESTAMP() and count sub-day DATEDIFF of a DATE - #115

Merged
kvlonge merged 19 commits into
mainfrom
fix/ntz-current-timestamp
Nov 7, 2026
Merged

kvlonge merged 19 commits into
mainfrom
fix/ntz-current-timestamp

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Why

Incremental and freshness filters compare a TIMESTAMP_NTZ column with CURRENT_TIMESTAMP(), directly or through DATEADD. dbt_utils.recency compiles to exactly that comparison. These were refused with SQ0003 ("comparing TIMESTAMP_NTZ(9) with TIMESTAMP_LTZ(9) needs an implicit conversion"). DATEDIFF in HOUR, MINUTE or SECOND with a DATE operand was also refused.

Changes

  • A TIMESTAMP_NTZ compared with a TIMESTAMP_LTZ reads the local value as its wall time in the session time zone, at its own precision, which is what Snowflake's implicit conversion does. This relies on session_timezone_utc.
  • DATEADD and TIMESTAMPADD take a TIMESTAMP_LTZ operand, computed on its UTC wall time and read back as local time. The policy's session_timezone_utc now lists these functions.
  • DATEDIFF in HOUR, MINUTE or SECOND reads a DATE operand as its midnight.
  • Above microsecond precision, the comparison also relies on timestamp_range.
  • New case file cases/temporal/naive_local_comparison.toml (comparisons, CURRENT_TIMESTAMP(), DATEADD of a local column, sub-day DATEDIFF of DATEs), recorded on Snowflake.
  • The everyday corpora record the clock filters and timestamp differences that now translate; none goes from translated to refused.

Verification

  • Tests that run the converted SQL on DuckDB: values at +02:00 and -05:00, an NTZ(3) value beside an LTZ(9) one with an extra microsecond, CURRENT_TIMESTAMP() and three-day DATEADD windows on both sides of the boundary, DATEDIFF in HOUR across 1970 and one second before midnight, and DATEDIFF in MINUTE between two DATEs.
  • Strict mode refuses the comparison unless the UTC session is confirmed.
  • make check-ci, make test-rust and make verify-recorded on the merged head: 12,044 cases, 0 failed, 0 translation-result regressions.

…IFF of a DATE

A TIMESTAMP_NTZ beside a TIMESTAMP_LTZ, such as CURRENT_TIMESTAMP() or
DATEADD of it, now reads the local value as its wall time in the UTC
session, at its own precision, as Snowflake does. DATEADD takes a
TIMESTAMP_LTZ the same way. DATEDIFF in HOUR, MINUTE or SECOND reads a
DATE operand as its midnight.
…ub-day DATEDIFF of a DATE

Recorded from fix/ntz-current-timestamp ec34616 (6 cases, utc-v1, no failures).
…tamp

The naive-local comparison's rule id moves to the catalogue constants:
conversion/constants.rs is at its 2,000-line limit until it is split.
…al bound does

TIMESTAMP_NTZ against CONVERT_TIMEZONE('UTC', tz), against a TIMESTAMP_TZ
column at a non-UTC offset, and against CONVERT_TIMEZONE('UTC',
CURRENT_TIMESTAMP()), under utc-v1.
Snowflake results for temporal/naive_zoned_comparison at 05d20a3. A
TIMESTAMP_NTZ beside a TIMESTAMP_TZ compares instants, the naive value read in
the session's time zone (UTC), with or without CONVERT_TIMEZONE('UTC', ...).
…s recorded

Snowflake reads the naive value in the session time zone and compares
instants beside a TIMESTAMP_TZ at any offset, with or without
CONVERT_TIMEZONE, so the naive-local comparison now reads a
TIMESTAMP_TZ as its UTC wall time too. This covers dbt's incremental
upper bound, convert_timezone('UTC', current_timestamp()).

Cases whose outcome now matches Snowflake no longer declare a
divergence, and the zoned columns' NTZ comparison declares
timestamp_tz_submicro for its nanosecond row, as their text
comparisons do.
kvlonge added a commit that referenced this pull request Nov 6, 2026
…econds

A zoned value read as its UTC wall time at a precision above
microseconds is cast to nanoseconds, which DuckDB cannot hold past
2262, where Snowflake compares it. BETWEEN joins the comparisons the
rule and timestamp_range cover.
…TWEEN

TIMESTAMP_NTZ(9) beside TIMESTAMP_TZ and TIMESTAMP_LTZ sentinels past the
nanosecond range, and BETWEEN with mixed naive and zoned bounds, under utc-v1.
…n BETWEEN

Snowflake results for the two cases added at 757ee76. Past the nanosecond
range and in BETWEEN with mixed bounds, a TIMESTAMP_NTZ still compares with
TIMESTAMP_TZ and TIMESTAMP_LTZ values as instants, read in the session's time
zone (UTC).
kvlonge added a commit that referenced this pull request Nov 7, 2026

kvlonge commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Gate on the exact head 559fc80 (a91604e, a merge of main 139fc9e, the test and corpus commits):

  • make check-ci: exit 0
  • make test-rust: exit 0, all test binaries
  • make verify-recorded: 12,044 cases, 0 failed, 0 unproven, 0 pending; 227 of 227 equivalences and rules verified
  • translation results against main's baseline: 0 regressions
  • hygiene scan: clean

@kvlonge
kvlonge marked this pull request as ready for review November 7, 2026 07:17
@kvlonge
kvlonge merged commit 559fc80 into main Nov 7, 2026
1 of 2 checks passed
@kvlonge
kvlonge deleted the fix/ntz-current-timestamp branch November 9, 2026 21:31

This branch was successfully deployed

1 active deployment
github-pages — 559fc80b Deployed Nov 7, 2026 by kvlonge via Deploy to GitHub Pages #164
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant