Skip to content

Commit caf768e

Browse files
committed
Issue duckdb#15246: Negative Nanosecond Timestamps
Round down instead of towards zero when decomposing timestamp_ns_t values. fixes: duckdb#15246 fixes: duckdblabs/duckdb-internal#3720
1 parent 4a8188e commit caf768e

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed

src/common/types/timestamp.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ namespace duckdb {
1717

1818
static_assert(sizeof(timestamp_t) == sizeof(int64_t), "timestamp_t was padded");
1919

20+
// Temporal values need to round down when changing precision,
21+
// but C/C++ rounds towrds 0 when you simply divide.
22+
// This piece of bit banging solves that problem.
23+
template <typename T>
24+
static inline T TemporalRound(T value, T scale) {
25+
const auto negative = int(value < 0);
26+
return UnsafeNumericCast<T>((value + negative) / scale - negative);
27+
}
28+
2029
// timestamp/datetime uses 64 bits, high 32 bits for date and low 32 bits for time
2130
// string format is YYYY-MM-DDThh:mm:ssZ
2231
// T may be a space
@@ -342,7 +351,7 @@ void Timestamp::Convert(timestamp_t timestamp, date_t &out_date, dtime_t &out_ti
342351
}
343352

344353
void Timestamp::Convert(timestamp_ns_t input, date_t &out_date, dtime_t &out_time, int32_t &out_nanos) {
345-
timestamp_t ms(input.value / Interval::NANOS_PER_MICRO);
354+
timestamp_t ms(TemporalRound(input.value, Interval::NANOS_PER_MICRO));
346355
out_date = Timestamp::GetDate(ms);
347356
int64_t days_nanos;
348357
if (!TryMultiplyOperator::Operation<int64_t, int64_t, int64_t>(out_date.days, Interval::NANOS_PER_DAY,

test/sql/types/timestamp/test_timestamp_types.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ AS tbl(s)
7474
2024-06-04 10:17:10.9
7575
2024-06-04 10:17:10
7676

77+
# Negative timestamp_ns
78+
query I
79+
select '1969-01-01T23:59:59.9999999'::timestamp_ns;
80+
----
81+
1969-01-01 23:59:59.9999999
82+
7783
# TIME conversions are now supported
7884
query I
7985
select sec::TIME from timestamp;

0 commit comments

Comments
 (0)