Skip to content

DX-105463: [C++][Gandiva] Add TimestampIR for unit-aware timestamp[us/ns] support#134

Merged
lriggs merged 6 commits intodremio:dremio_27.0_20from
lriggs:nanos
Apr 17, 2026
Merged

DX-105463: [C++][Gandiva] Add TimestampIR for unit-aware timestamp[us/ns] support#134
lriggs merged 6 commits intodremio:dremio_27.0_20from
lriggs:nanos

Conversation

@lriggs
Copy link
Copy Markdown
Collaborator

@lriggs lriggs commented Apr 16, 2026

Summary

Adds TimestampIR — an LLVM IR builder class modeled after DecimalIR — that generates unit-aware wrapper functions at module-load time, enabling Gandiva functions registered for timestamp[ms] to automatically handle timestamp[us] and timestamp[ns] inputs without explicit per-unit registry entries.

What changes are included in this PR?

  • timestamp_ir.h/cc (new): TimestampIR class with wrapper patterns: pure IR arithmetic, calendar split/recombine, extract, trunc, diff, cast, and timezone wrappers. Includes FloorDiv/FloorDivRem helpers for correct floor-toward-negative-infinity semantics on pre-epoch timestamps.
  • CMakeLists.txt + engine.cc: Wire TimestampIR::AddFunctions() at module load alongside DecimalIR.
  • function_signature.cc: DataTypeEquals for TIMESTAMP ignores TimeUnit; only timezone is significant for matching. This allows the existing timestamp[ms] registry entries to match calls with timestamp[us]/timestamp[ns] parameters.
  • llvm_generator.cc/h: BuildFunctionCall inspects function descriptor for non-ms timestamp params and remaps to _us/_ns suffixed IR functions. Propagates Status errors from the visitor back to the caller (previously silently dropped).
  • precompiled/time.cc: Floor-division fix in DATE_TRUNC_FIXED_UNIT for pre-epoch (negative) timestamps; fix sub-second millis sign in castVARCHAR_timestamp_int64 for negative timestamps.
  • tests/date_time_test.cc: End-to-end C++ tests for timestamp[us] and timestamp[ns] through extract, trunc, arithmetic, and cast functions.

Are these changes tested?

Yes — tests/date_time_test.cc covers the new unit-aware paths. Also validated end-to-end through arrow-java ProjectorTest and Dremio integration tests (separate PRs).

Are there any user-facing changes?

No API changes. Gandiva functions that previously only accepted timestamp[ms] now transparently accept timestamp[us] and timestamp[ns].

Safe degradation: If a caller uses an old native lib (pre-TimestampIR), DataTypeEquals falls back to strict matching, function lookup fails at validation time, and the caller gets a clean error rather than silent wrong results.

@lriggs lriggs changed the title Nanos DX-105463: [C++][Gandiva] Add TimestampIR for unit-aware timestamp[us/ns] support Apr 16, 2026
@lriggs
Copy link
Copy Markdown
Collaborator Author

lriggs commented Apr 16, 2026

TimestampIR Generated Functions

All functions produced by cpp/src/gandiva/timestamp_ir.cc. Two units: _us (microsecond) and _ns
(nanosecond). Total: 174 entries.


kFixedAdds — 5 functions × 4 arg patterns × 2 units = 40

Pure arithmetic: ts ± count * constant.

Function _int32_timestamp _int64_timestamp _timestamp_int32 _timestamp_int64
timestampaddSecond timestampaddSecond_int32_timestamp_us / _ns timestampaddSecond_int64_timestamp_us / _ns timestampaddSecond_timestamp_int32_us / _ns timestampaddSecond_timestamp_int64_us / _ns
timestampaddMinute timestampaddMinute_int32_timestamp_us / _ns timestampaddMinute_int64_timestamp_us / _ns timestampaddMinute_timestamp_int32_us / _ns timestampaddMinute_timestamp_int64_us / _ns
timestampaddHour timestampaddHour_int32_timestamp_us / _ns timestampaddHour_int64_timestamp_us / _ns timestampaddHour_timestamp_int32_us / _ns timestampaddHour_timestamp_int64_us / _ns
timestampaddDay timestampaddDay_int32_timestamp_us / _ns timestampaddDay_int64_timestamp_us / _ns timestampaddDay_timestamp_int32_us / _ns timestampaddDay_timestamp_int64_us / _ns
timestampaddWeek timestampaddWeek_int32_timestamp_us / _ns timestampaddWeek_int64_timestamp_us / _ns timestampaddWeek_timestamp_int32_us / _ns timestampaddWeek_timestamp_int64_us / _ns

kCalendarAdds — 3 functions × 4 arg patterns × 2 units = 24

Split/recombine around precompiled millis function.

Function _int32_timestamp _int64_timestamp _timestamp_int32 _timestamp_int64
timestampaddMonth timestampaddMonth_int32_timestamp_us / _ns timestampaddMonth_int64_timestamp_us / _ns timestampaddMonth_timestamp_int32_us / _ns timestampaddMonth_timestamp_int64_us / _ns
timestampaddQuarter timestampaddQuarter_int32_timestamp_us / _ns timestampaddQuarter_int64_timestamp_us / _ns timestampaddQuarter_timestamp_int32_us / _ns timestampaddQuarter_timestamp_int64_us / _ns
timestampaddYear timestampaddYear_int32_timestamp_us / _ns timestampaddYear_int64_timestamp_us / _ns timestampaddYear_timestamp_int32_us / _ns timestampaddYear_timestamp_int64_us / _ns

kExtracts — 14 functions × 1 signature × 2 units = 28

Pattern: {name}_timestamp_{us|ns}. Convert to millis then call precompiled.

extractMillennium_timestamp_us     extractMillennium_timestamp_ns
extractCentury_timestamp_us        extractCentury_timestamp_ns
extractDecade_timestamp_us         extractDecade_timestamp_ns
extractYear_timestamp_us           extractYear_timestamp_ns
extractQuarter_timestamp_us        extractQuarter_timestamp_ns
extractMonth_timestamp_us          extractMonth_timestamp_ns
extractWeek_timestamp_us           extractWeek_timestamp_ns
extractDay_timestamp_us            extractDay_timestamp_ns
extractHour_timestamp_us           extractHour_timestamp_ns
extractMinute_timestamp_us         extractMinute_timestamp_ns
extractSecond_timestamp_us         extractSecond_timestamp_ns
extractDoy_timestamp_us            extractDoy_timestamp_ns
extractDow_timestamp_us            extractDow_timestamp_ns
extractEpoch_timestamp_us          extractEpoch_timestamp_ns

kTruncs — 11 functions × 1 signature × 2 units = 22

Pattern: {name}_timestamp_{us|ns}. Truncate millis then scale back.

date_trunc_Millennium_timestamp_us    date_trunc_Millennium_timestamp_ns
date_trunc_Century_timestamp_us       date_trunc_Century_timestamp_ns
date_trunc_Decade_timestamp_us        date_trunc_Decade_timestamp_ns
date_trunc_Year_timestamp_us          date_trunc_Year_timestamp_ns
date_trunc_Quarter_timestamp_us       date_trunc_Quarter_timestamp_ns
date_trunc_Month_timestamp_us         date_trunc_Month_timestamp_ns
date_trunc_Week_timestamp_us          date_trunc_Week_timestamp_ns
date_trunc_Day_timestamp_us           date_trunc_Day_timestamp_ns
date_trunc_Hour_timestamp_us          date_trunc_Hour_timestamp_ns
date_trunc_Minute_timestamp_us        date_trunc_Minute_timestamp_ns
date_trunc_Second_timestamp_us        date_trunc_Second_timestamp_ns

kDiffs — 8 functions × 1 signature × 2 units = 16

Pattern: {name}_timestamp_timestamp_{us|ns}. Floor-divide both inputs to millis.

timestampdiffSecond_timestamp_timestamp_us    timestampdiffSecond_timestamp_timestamp_ns
timestampdiffMinute_timestamp_timestamp_us    timestampdiffMinute_timestamp_timestamp_ns
timestampdiffHour_timestamp_timestamp_us      timestampdiffHour_timestamp_timestamp_ns
timestampdiffDay_timestamp_timestamp_us       timestampdiffDay_timestamp_timestamp_ns
timestampdiffWeek_timestamp_timestamp_us      timestampdiffWeek_timestamp_timestamp_ns
timestampdiffMonth_timestamp_timestamp_us     timestampdiffMonth_timestamp_timestamp_ns
timestampdiffQuarter_timestamp_timestamp_us   timestampdiffQuarter_timestamp_timestamp_ns
timestampdiffYear_timestamp_timestamp_us      timestampdiffYear_timestamp_timestamp_ns

kTwoTsScalars — 2 functions × 1 signature × 2 units = 4

Two-timestamp → scalar (float64 for months_between, int32 for datediff).

months_between_timestamp_timestamp_us    months_between_timestamp_timestamp_ns
datediff_timestamp_timestamp_us          datediff_timestamp_timestamp_ns

kCastsFromTs — 3 functions × 1 signature × 2 units = 6

Cast from timestamp to another type.

castDATE_timestamp_us        castDATE_timestamp_ns
castTIME_timestamp_us        castTIME_timestamp_ns
last_day_from_timestamp_us   last_day_from_timestamp_ns

kDateArithEntries — 7 entries × 2 int-types × 2 units = 28

Fixed 1-day arithmetic. date_add and add appear with both count_first=true
and count_first=false, generating different names.

Entry (count_first) int32 variant (_us) int64 variant (_us) same with _ns
date_add (count first) date_add_int32_timestamp_us date_add_int64_timestamp_us ..._ns
add (count first) add_int32_timestamp_us add_int64_timestamp_us ..._ns
date_add (ts first) date_add_timestamp_int32_us date_add_timestamp_int64_us ..._ns
add (ts first) add_timestamp_int32_us add_timestamp_int64_us ..._ns
date_sub (ts first) date_sub_timestamp_int32_us date_sub_timestamp_int64_us ..._ns
subtract (ts first) subtract_timestamp_int32_us subtract_timestamp_int64_us ..._ns
date_diff (ts first) date_diff_timestamp_int32_us date_diff_timestamp_int64_us ..._ns

Timezone — 2 functions × 2 units = 4

Split-recombine: timezone offset is whole-second, sub-ms survives.

to_utc_timezone_timestamp_us      to_utc_timezone_timestamp_ns
from_utc_timezone_timestamp_us    from_utc_timezone_timestamp_ns

castVARCHAR — 1 function × 2 units = 2

Appends sub-ms digits to the millis formatter output.

castVARCHAR_timestamp_int64_us    castVARCHAR_timestamp_int64_ns

@lriggs
Copy link
Copy Markdown
Collaborator Author

lriggs commented Apr 16, 2026

TimestampIR Test Coverage Table

Source: cpp/src/gandiva/tests/date_time_test.cc
Reference: cpp/src/gandiva/timestamp_ir.cc::BuildAllFunctionNames()

Helper functions used throughout (defined in the test file):

  • EvalExtract(name, unit, ts, pool) — unary ts → int64
  • EvalTrunc(name, unit, ts, pool) — unary ts → ts
  • EvalTimestampadd(name, unit, count_i32, ts, pool)(int32, ts) → ts (count-first)
  • EvalCountFirstI64(name, unit, count_i64, ts, pool)(int64, ts) → ts (count-first)
  • EvalDateArith(name, unit, ts, count_i32, pool)(ts, int32) → ts (ts-first)
  • EvalTsFirstI64(name, unit, ts, count_i64, pool)(ts, int64) → ts (ts-first)
  • EvalDiff(name, unit, ts1, ts2, pool)(ts, ts) → int32
  • EvalMonthsBetween(unit, ts1, ts2, pool)(ts, ts) → float64
  • EvalLastDayFrom(unit, ts, pool)ts → date64 (millis)
  • EvalTimezone(name, unit, ts, tz, pool)(ts, utf8) → ts

Test constants (kTestMillis = 2021-06-15 14:30:45.123 UTC):

kTestMillis = 1623767445123           (ms since epoch)
kSubMs      = 456                     (sub-ms micros)
kSubUs      = 789                     (sub-us nanos)
kTestMicros = kTestMillis * 1000 + 456
kTestNanos  = kTestMillis * 1000000 + 456000 + 789

Non-precision tests (ms only / date types)

Test Function(s) Input Expected output Notes
TestIsNull isnull, isnotnull date64, time32[ms] values bool arrays Not timestamp precision related
TestDate32IsNull isnull date32 values bool array Not timestamp precision related
TestDateTime extractYear, extractMonth, extractDay date64 (2000–2015), date32 (via castDATE), timestamp[ms] {2000,1999,2015,2015}, {1,12,6,7}, etc. Only ms; 4 dates per extract
TestTime extractMinute, extractHour time32[ms]: 5:35:25, 0:59:00, 12:30:00, 23:00:00 {35,59,30,0}, {5,0,12,23} time32 only
TestTimestampDiff timestampdiffSecond/Minute/Hour/Day/datediff/Week/Month/Quarter/Year ts[ms] pairs: 2015-09-10 → 2017-03-30 {48996077,−48996077,0,−82800}, etc. ms only; 4 row-pairs
TestTimestampDiffMonth timestampdiffMonth ts[ms] end-of-month pairs {1,0,1,1,−1,4} ms only; 6 row-pairs; leap-year edge cases
TestMonthsBetween months_between date64 pairs {1.0,−1.0,1.0,1.0} date64 only; float64 result
TestCastTimestampFromInt64 castTIMESTAMP int64 millis values same values as timestamp[ms] identity cast
TestLastDay last_day date64 (5 dates in 2015–2017) last-day-of-month for each date64 only
TestToTimestampFromInt to_timestamp int32, int64, float32, float64 timestamp[ms] integer=epoch seconds, float=epoch seconds with fractional part
TestToUtcTimestamp to_utc_timestamp ts[ms] + timezone strings (Asia/Kolkata, America/Los_Angeles) UTC-shifted timestamps ms only
TestFromUtcTimestamp from_utc_timestamp UTC ts[ms] + timezone strings local-shifted timestamps ms only; inverse of above

Precision tests (ms / us / ns)

Extract functions

Test Function(s) Units Input Expected Notes
TestExtractAcrossPrecisions extractMonth, extractDay, extractHour, extractYear ms / us / ns kTestMillis / kTestMicros / kTestNanos 6, 15, 14, 2021 respectively All three units must agree
TestExtractRemainingFunctions extractMillennium, extractCentury, extractDecade, extractQuarter, extractWeek, extractMinute, extractSecond, extractDoy, extractDow, extractEpoch ms / us / ns kTest* Loop: us/ns == ms baseline; spot-checks: Minute=30, Second=45, Doy=166, Epoch=1623767445, Quarter=2 No explicit expected for Millennium/Century/Decade/Week/Dow — only cross-unit equality
TestNegativeTimestampPrecisions extractSecond, extractYear, extractMonth ms / us / ns 1960-03-15 06:30:00 (neg_millis) ± sub-ms us/ns == ms baseline Tests floor-div sign on pre-epoch timestamps
TestNegativeTimestampBoundaryCrossing extractHour, extractYear, extractDay us / ns −456 µs, −456789 ns Hour=23, Year=1969, Day=31 Verifies FloorDiv correctness near epoch
TestFixedAddCrossesSecondBoundary extractSecond (after timestampaddSecond) us / ns kTestMicros + 1s, kTestNanos + 1s Second=46; sub-ms preserved (r % 1000 == kSubMs/kSubUs) Composed test

Coverage: all 14 kExtracts × 2 units covered.


Trunc functions

Test Function(s) Units Input Expected Notes
TestDateTruncAcrossPrecisions date_trunc_Day, date_trunc_Hour ms / us / ns kTest* Day result = 1623715200000 ms; Hour result = 1623765600000 ms (× unit scale) ms_result × 1000 for us; × 1000000 for ns
TestDateTruncRemainingFunctions date_trunc_Millennium, _Century, _Decade, _Year, _Quarter, _Month, _Week, _Minute, _Second ms / us / ns kTest* Loop: us/ns == ms_result × scale; Second must ≠ kTestMicros and aligned to 1s; Minute must be aligned to 60s No explicit expected for most; Second and Minute alignment verified
TestNegativeTimestampPrecisions date_trunc_Day ms / us / ns neg_millis (1960-03-15) us/ns == day_ms × scale
TestNegativeTimestampBoundaryCrossing date_trunc_Second, date_trunc_Day us / ns −456 µs, −456789 ns trunc_Second: −1000000 µs / −1000000000 ns; trunc_Day: −86400000000 µs / −86400000000000 ns Explicit expected values for pre-epoch boundary

Coverage: all 11 kTruncs × 2 units covered.


Fixed-add functions (kFixedAdds)

Test Function(s) Units Arg patterns Input Expected Notes
TestTimestampaddSecondPreservesSubMs timestampaddSecond us / ns count-first i32 kTest* + 10 kTest* + 10_000_000 µs / 10_000_000_000 ns; sub-ms/sub-us preserved
TestTimestampaddDayPreservesSubMs timestampaddDay us / ns count-first i32 kTest* + 1 kTest* + 86400×scale; sub-ms preserved
TestFixedAddAllArgVariants timestampaddSecond/Minute/Hour/Day/Week us / ns all 4 (i32/i64 × count-first/ts-first) kTest*, count=+3 and count=−3 kTest* ± 3×seconds×scale for each; sub-ms preserved for both signs Outer loop over {3, −3} covers all 5 functions × all 4 patterns × both signs
TestFixedAddCrossesSecondBoundary timestampaddSecond us / ns count-first i32 kTest* + 1s Second crosses 45→46; sub-ms unchanged Composed with EvalExtract

Coverage: all 5 kFixedAdds × 4 arg patterns × 2 units covered.


Calendar-add functions (kCalendarAdds)

Test Function(s) Units Arg patterns Input Expected Notes
TestTimestampaddMonthPreservesSubMs timestampaddMonth ms / us / ns count-first i32 kTest* + 2 months ms baseline × scale + sub-ms fragment
TestTimestampaddMonthReversedArgMicros timestampaddMonth ms / us count-first i32 kTest* + 2 months millis_result × 1000 + kSubMs Same helper as above — this test name is misleading (uses EvalTimestampadd which is count-first, not ts-first)
TestCalendarAddAllArgVariants timestampaddMonth, timestampaddQuarter, timestampaddYear us / ns all 4 (i32/i64 × count-first/ts-first) kTest*, count=2 ms_baseline × scale + sub-ms fragment for each ms baseline used as ground truth

Coverage: all 3 kCalendarAdds × 4 arg patterns × 2 units covered.
Note: Calendar add only tested with count=2. No end-of-month or leap-year edge cases for us/ns.


Diff functions (kDiffs)

Test Function(s) Units Input Expected Notes
TestTimestampdiffAcrossPrecisions timestampdiffDay ms / us / ns kTest* and kTest* + 3 days 3
TestTimestampdiffAllFunctions timestampdiffSecond/Minute/Hour/Week/Month/Quarter/Year ms / us / ns kTest* pairs with chosen deltas {60, 180, 48, 2, 3, 2, 2} Deltas: 60s, 3h, 48h, 14d, 92d, 183d, 730d
TestTimestampdiffSubSecondSensitivity timestampdiffSecond us / ns kTest* ± 999ms (0) and kTest* ± 1.000001s (1) 0, 1 Verifies floor-div in millis space

Coverage: all 8 kDiffs × 2 units covered.
Note: Month/Quarter/Year deltas chosen as fixed day offsets (92d=3mo, 183d=2q, 730d=2y) — these are exact for 2021-06-15 but not general calendar math tests.


Two-timestamp scalars (kTwoTsScalars)

Test Function Units Input Expected Notes
TestMonthsBetweenAcrossPrecisions months_between ms / us / ns kTest* and kTest* + 61 days (~2 months) us/ns ≈ ms baseline (tolerance 0.001) Uses NEAR comparison, not exact
TestMonthsBetween months_between date64 only 4 date64 pairs {1.0, −1.0, 1.0, 1.0} Original test; date64 not timestamp
TestDatediffTwoTimestamps datediff (two-ts) ms / us / ns kTest* and kTest* + 5 days −5 (note: datediff = ts1−ts2 in days)

Coverage: months_between_timestamp_timestamp_us/ns and datediff_timestamp_timestamp_us/ns covered.


Cast functions (kCastsFromTs)

Test Function Units Input Expected Notes
TestCastDateAcrossPrecisions castDATE ms / us / ns kTest* 1623715200000 (2021-06-15 00:00:00 UTC in ms) Sub-day truncated
TestCastTimeAcrossPrecisions castTIME ms / us / ns kTest* 52245123 (14:30:45.123 in millis-of-day) Sub-ms truncated to millis
TestLastDayAllPrecisions last_day (= last_day_from) ms / us / ns kTest* (2021-06-15) MillisSince(2021-06-30 00:00:00) Result is date64 millis

Coverage: castDATE_timestamp_us/ns, castTIME_timestamp_us/ns, last_day_from_timestamp_us/ns all covered.


Date arithmetic (kDateArithEntries)

Test Function(s) Units Arg patterns Input Expected Notes
TestDateAddSubtractAcrossPrecisions date_add, subtract us / ns ts-first i32 kTest*, n=3 / n=1 kTest* ± n×86400×scale
TestDateArithAllVariants date_add, add (count-first i32/i64, ts-first i32/i64); date_sub, subtract, date_diff (ts-first i32/i64) us / ns all applicable patterns kTest*, n=3 kTest* + 3×86400×scale (add); kTest* − 3×86400×scale (sub/diff) Sub-ms verified for date_add

Coverage: all 7 kDateArithEntries × 2 int-types × 2 units covered.


Timezone functions

Test Function(s) Units Input Expected Notes
TestToUtcTimestamp to_utc_timestamp ms ts[ms] + Asia/Kolkata / America/Los_Angeles UTC-shifted values ms only
TestFromUtcTimestamp from_utc_timestamp ms UTC ts[ms] + same timezones local-shifted values ms only
TestTimezoneAllPrecisions from_utc_timestamp, to_utc_timestamp us / ns kTest* + Asia/Kolkata (UTC+5:30) kTest* ± 5:30×scale; sub-ms preserved; round-trip recovers original Asia/Kolkata only (fixed offset, no DST)

Coverage: to_utc_timezone_timestamp_us/ns and from_utc_timezone_timestamp_us/ns covered.
Note: Only fixed-offset timezone tested for us/ns. DST transitions not exercised.


castVARCHAR

Test Function Units Input Expected Notes
TestCastVARCHARAcrossPrecisions castVARCHAR ms / us / ns kTest*, len=100 "2021-06-15 14:30:45.123" / "...123456" / "...123456789" Full sub-ms digit output
TestCastVARCHARTruncation castVARCHAR ns kTest*, len=23/26/20 "...123" / "...123456" / "...45." Length-clamping of sub-ms digits
TestCastVARCHARNegativeTimestamp castVARCHAR us / ns −1 µs, −456 µs, −1 ns, −456789 ns "1969-12-31 23:59:59.999999", "...999544", "...999999999", "...999543211" FloorDivRem must give non-negative remainder

Coverage: castVARCHAR_timestamp_int64_us/ns covered.


Coverage Summary

Category Total functions Tests covering Status
kExtracts 28 TestExtractAcrossPrecisions, TestExtractRemainingFunctions, TestNegative* ✓ All covered
kTruncs 22 TestDateTruncAcrossPrecisions, TestDateTruncRemainingFunctions, TestNegative* ✓ All covered
kFixedAdds 40 TestTimestampaddSecond/DayPreservesSubMs, TestFixedAddAllArgVariants ✓ All covered
kCalendarAdds 24 TestTimestampaddMonth*, TestCalendarAddAllArgVariants ✓ All covered
kDiffs 16 TestTimestampdiffAcrossPrecisions, TestTimestampdiffAllFunctions ✓ All covered
kTwoTsScalars 4 TestMonthsBetweenAcrossPrecisions, TestDatediffTwoTimestamps ✓ All covered
kCastsFromTs 6 TestCastDate/Time/LastDayAllPrecisions ✓ All covered
kDateArithEntries 28 TestDateAddSubtractAcrossPrecisions, TestDateArithAllVariants ✓ All covered
Timezone 4 TestTimezoneAllPrecisions ✓ All covered
castVARCHAR 2 TestCastVARCHARAcrossPrecisions, TestCastVARCHARTruncation ✓ All covered
Total 174

Known Gaps / Thin Areas

  1. extractMillennium/Century/Decade/Week/Dow — only verified against the ms baseline (cross-unit equality), never against a known absolute expected value. A bug that shifts the millis computation for all three units equally would pass.

  2. Calendar adds with edge cases (us/ns) — Month/Quarter/Year only tested with count=2 on 2021-06-15. End-of-month behavior (e.g., Jan 31 + 1 month → Feb 28) and leap-year semantics are not tested for us/ns. The ms path has TestTimestampDiffMonth for diffs but calendar-add edge cases are absent.

  3. months_between uses approximate comparison (NEAR(…, 0.001)) — a small rounding error in the us/ns conversion would not fail the test.

  4. Timezone for us/ns uses only Asia/Kolkata (fixed UTC+5:30 offset). A DST-observing timezone (e.g., America/Los_Angeles) is tested for ms only. The split-recombine correctness during a DST transition is not exercised for us/ns.

  5. timestampdiffMonth/Quarter/Year deltas are chosen as fixed-day offsets (92d, 183d, 730d) that happen to be exact calendar intervals from 2021-06-15. This does not test the calendar-month counting logic directly — it tests that floor-div scales correctly, which is correct but narrow.

  6. castVARCHAR with len < 23TestCastVARCHARTruncation tests len=20, which gives "2021-06-15 14:30:45." (truncates fractional seconds). The behavior of len < 20 (truncating into the date/time itself) is untested for us/ns.

  7. kDateArithEntries: date_add count-first uses EvalTimestampaddEvalTimestampadd calls the function as func(count, ts), which matches date_add_int32_timestamp_us. However TestDateArithAllVariants also calls EvalCountFirstI64 for the int64 count-first variant. Both are tested.

  8. castTIME sub-ms truncation — verified only that castTIME(kTestMicros) == 52245123 (millis). There is no test confirming that a us-precision timestamp with sub-ms data rounds down (not up) in the time-of-day value.

Copy link
Copy Markdown

@akravchukdremio akravchukdremio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I've found that we can implement one more function: next_day, but it's not a popular one I think. Just created a commit with adding this func on top of this branch: d256409. But I think also fine to merge as is without it

@lriggs lriggs merged commit 28932a0 into dremio:dremio_27.0_20 Apr 17, 2026
6 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants