This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-2.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push:
new 02c3157e4c6 [branch-2.1](function) fix wrong floor of function
date_diff when unit less than day (#49429) (#50606)
02c3157e4c6 is described below
commit 02c3157e4c62d768fce7f3faddfbc4ef731583b4
Author: zclllyybb <[email protected]>
AuthorDate: Wed May 7 09:27:37 2025 +0800
[branch-2.1](function) fix wrong floor of function date_diff when unit less
than day (#49429) (#50606)
pick https://github.com/apache/doris/pull/49429
---
be/src/pipeline/pipeline_fragment_context.cpp | 2 +-
be/src/runtime/fragment_mgr.cpp | 2 +-
be/src/runtime/plan_fragment_executor.cpp | 2 +-
be/src/runtime/query_context.h | 4 +-
be/src/util/datetype_cast.hpp | 8 +-
.../aggregate_function_sequence_match.h | 10 +-
.../vec/functions/array/function_array_range.cpp | 8 +-
be/src/vec/functions/date_time_transforms.h | 28 +--
.../function_date_or_datetime_computation.h | 8 +-
.../vec/functions/function_datetime_floor_ceil.cpp | 6 +-
be/src/vec/runtime/vdatetime_value.cpp | 4 +-
be/src/vec/runtime/vdatetime_value.h | 104 ++++++-----
be/test/vec/runtime/vdatetime_value_test.cpp | 203 +++++++++++++++++++++
.../datetime_functions/test_date_function_v2.out | Bin 0 -> 430 bytes
.../array_functions/test_array_functions.out | Bin 28536 -> 28513 bytes
.../test_date_function_v2.groovy | 53 ++++++
16 files changed, 356 insertions(+), 86 deletions(-)
diff --git a/be/src/pipeline/pipeline_fragment_context.cpp
b/be/src/pipeline/pipeline_fragment_context.cpp
index 30a7ce07315..a6a2fb7ab76 100644
--- a/be/src/pipeline/pipeline_fragment_context.cpp
+++ b/be/src/pipeline/pipeline_fragment_context.cpp
@@ -160,7 +160,7 @@ bool PipelineFragmentContext::is_timeout(const
VecDateTimeValue& now) const {
if (_timeout <= 0) {
return false;
}
- if (now.second_diff(_start_time) > _timeout) {
+ if (now.datetime_diff_in_seconds(_start_time) > _timeout) {
return true;
}
return false;
diff --git a/be/src/runtime/fragment_mgr.cpp b/be/src/runtime/fragment_mgr.cpp
index c995a861884..896ddece1f5 100644
--- a/be/src/runtime/fragment_mgr.cpp
+++ b/be/src/runtime/fragment_mgr.cpp
@@ -1535,7 +1535,7 @@ void FragmentMgr::debug(std::stringstream& ss) {
-> Status {
for (auto& it : map) {
ss << it.first << "\t" <<
it.second->start_time().debug_string() << "\t"
- << now.second_diff(it.second->start_time()) << "\n";
+ <<
now.datetime_diff_in_seconds(it.second->start_time()) << "\n";
}
return Status::OK();
});
diff --git a/be/src/runtime/plan_fragment_executor.cpp
b/be/src/runtime/plan_fragment_executor.cpp
index 7bf4f3846dd..bf218f825d9 100644
--- a/be/src/runtime/plan_fragment_executor.cpp
+++ b/be/src/runtime/plan_fragment_executor.cpp
@@ -419,7 +419,7 @@ bool PlanFragmentExecutor::is_timeout(const
VecDateTimeValue& now) const {
if (_timeout_second <= 0) {
return false;
}
- if (now.second_diff(_start_time) > _timeout_second) {
+ if (now.datetime_diff_in_seconds(_start_time) > _timeout_second) {
return true;
}
return false;
diff --git a/be/src/runtime/query_context.h b/be/src/runtime/query_context.h
index 66623a46fbc..5a11905f21a 100644
--- a/be/src/runtime/query_context.h
+++ b/be/src/runtime/query_context.h
@@ -98,13 +98,13 @@ public:
if (timeout_second <= 0) {
return false;
}
- if (now.second_diff(_start_time) > timeout_second) {
+ if (now.datetime_diff_in_seconds(_start_time) > timeout_second) {
return true;
}
return false;
}
- int64_t query_time(VecDateTimeValue& now) { return
now.second_diff(_start_time); }
+ int64_t query_time(VecDateTimeValue& now) { return
now.datetime_diff_in_seconds(_start_time); }
void set_thread_token(int concurrency, bool is_serial) {
_thread_token =
_exec_env->scanner_scheduler()->new_limited_scan_pool_token(
diff --git a/be/src/util/datetype_cast.hpp b/be/src/util/datetype_cast.hpp
index 495631ea7e3..67df42e2568 100644
--- a/be/src/util/datetype_cast.hpp
+++ b/be/src/util/datetype_cast.hpp
@@ -29,8 +29,12 @@
/*
* We use these function family to clarify our types of datelike type. for
example:
* DataTypeDate -------------------> ColumnDate ----------------------->
Int64
- * | TypeToColumn ValueTypeOfColumn
- * | TypeToValueType
+ * | | TypeToColumn ValueTypeOfColumn
|
+ * |
↘--------------------------------------------------------------↗
+ * | | ::FieldType
|
+ * |
↖--------------------------------------------------------------↙
+ * | DateTraits<T>::DateType
+ * ↓ TypeToValueType
* VecDateTimeValue
*/
namespace doris::date_cast {
diff --git a/be/src/vec/aggregate_functions/aggregate_function_sequence_match.h
b/be/src/vec/aggregate_functions/aggregate_function_sequence_match.h
index 101c2c16fd0..d7782488616 100644
--- a/be/src/vec/aggregate_functions/aggregate_function_sequence_match.h
+++ b/be/src/vec/aggregate_functions/aggregate_function_sequence_match.h
@@ -413,7 +413,7 @@ public:
base_it = events_it;
++action_it;
} else if (action_it->type == PatternActionType::TimeLessOrEqual) {
- if (events_it->first.second_diff(base_it->first) <=
action_it->extra) {
+ if (events_it->first.datetime_diff_in_seconds(base_it->first)
<= action_it->extra) {
/// condition satisfied, move onto next action
back_stack.emplace(action_it, events_it, base_it);
base_it = events_it;
@@ -421,28 +421,28 @@ public:
} else if (!do_backtrack())
break;
} else if (action_it->type == PatternActionType::TimeLess) {
- if (events_it->first.second_diff(base_it->first) <
action_it->extra) {
+ if (events_it->first.datetime_diff_in_seconds(base_it->first)
< action_it->extra) {
back_stack.emplace(action_it, events_it, base_it);
base_it = events_it;
++action_it;
} else if (!do_backtrack())
break;
} else if (action_it->type ==
PatternActionType::TimeGreaterOrEqual) {
- if (events_it->first.second_diff(base_it->first) >=
action_it->extra) {
+ if (events_it->first.datetime_diff_in_seconds(base_it->first)
>= action_it->extra) {
back_stack.emplace(action_it, events_it, base_it);
base_it = events_it;
++action_it;
} else if (++events_it == events_end && !do_backtrack())
break;
} else if (action_it->type == PatternActionType::TimeGreater) {
- if (events_it->first.second_diff(base_it->first) >
action_it->extra) {
+ if (events_it->first.datetime_diff_in_seconds(base_it->first)
> action_it->extra) {
back_stack.emplace(action_it, events_it, base_it);
base_it = events_it;
++action_it;
} else if (++events_it == events_end && !do_backtrack())
break;
} else if (action_it->type == PatternActionType::TimeEqual) {
- if (events_it->first.second_diff(base_it->first) ==
action_it->extra) {
+ if (events_it->first.datetime_diff_in_seconds(base_it->first)
== action_it->extra) {
back_stack.emplace(action_it, events_it, base_it);
base_it = events_it;
++action_it;
diff --git a/be/src/vec/functions/array/function_array_range.cpp
b/be/src/vec/functions/array/function_array_range.cpp
index 888d7adc5f9..ecfc4b3f99f 100644
--- a/be/src/vec/functions/array/function_array_range.cpp
+++ b/be/src/vec/functions/array/function_array_range.cpp
@@ -136,9 +136,8 @@ struct RangeImplUtil {
auto dest_array_column_ptr =
ColumnArray::create(return_nested_type->create_column(),
ColumnArray::ColumnOffsets::create());
IColumn* dest_nested_column = &dest_array_column_ptr->get_data();
- ColumnNullable* dest_nested_nullable_col =
- reinterpret_cast<ColumnNullable*>(dest_nested_column);
- dest_nested_column = dest_nested_nullable_col->get_nested_column_ptr();
+ auto* dest_nested_nullable_col =
assert_cast<ColumnNullable*>(dest_nested_column);
+ dest_nested_column =
dest_nested_nullable_col->get_nested_column_ptr().get();
auto& dest_nested_null_map =
dest_nested_nullable_col->get_null_map_column().get_data();
auto args_null_map = ColumnUInt8::create(input_rows_count, 0);
@@ -159,7 +158,8 @@ struct RangeImplUtil {
assert_cast<const
ColumnVector<SourceDataType>*>(argument_columns[0].get());
auto end_column =
assert_cast<const
ColumnVector<SourceDataType>*>(argument_columns[1].get());
- auto step_column = assert_cast<const
ColumnVector<Int32>*>(argument_columns[2].get());
+ const auto* step_column =
+ assert_cast<const
ColumnVector<Int32>*>(argument_columns[2].get());
DCHECK(dest_nested_column != nullptr);
auto& dest_offsets = dest_array_column_ptr->get_offsets();
diff --git a/be/src/vec/functions/date_time_transforms.h
b/be/src/vec/functions/date_time_transforms.h
index 0b564bf1bd6..6541bb1c623 100644
--- a/be/src/vec/functions/date_time_transforms.h
+++ b/be/src/vec/functions/date_time_transforms.h
@@ -38,20 +38,20 @@
namespace doris::vectorized {
-#define TIME_FUNCTION_IMPL(CLASS, UNIT, FUNCTION)
\
- template <typename ArgType>
\
- struct CLASS {
\
- using OpArgType = ArgType;
\
- static constexpr auto name = #UNIT;
\
-
\
- static inline auto execute(const ArgType& t) {
\
- const auto& date_time_value = (typename
DateTraits<ArgType>::T&)(t); \
- return date_time_value.FUNCTION;
\
- }
\
-
\
- static DataTypes get_variadic_argument_types() {
\
- return {std::make_shared<typename
DateTraits<ArgType>::DateType>()}; \
- }
\
+#define TIME_FUNCTION_IMPL(CLASS, UNIT, FUNCTION)
\
+ template <typename NativeType>
\
+ struct CLASS {
\
+ using OpArgType = NativeType;
\
+ static constexpr auto name = #UNIT;
\
+
\
+ static inline auto execute(const NativeType& t) {
\
+ const auto& date_time_value = (typename
DateTraits<NativeType>::T&)(t); \
+ return date_time_value.FUNCTION;
\
+ }
\
+
\
+ static DataTypes get_variadic_argument_types() {
\
+ return {std::make_shared<typename
DateTraits<NativeType>::DateType>()}; \
+ }
\
}
#define TO_TIME_FUNCTION(CLASS, UNIT) TIME_FUNCTION_IMPL(CLASS, UNIT, UNIT())
diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h
b/be/src/vec/functions/function_date_or_datetime_computation.h
index 567209419ff..7086e744869 100644
--- a/be/src/vec/functions/function_date_or_datetime_computation.h
+++ b/be/src/vec/functions/function_date_or_datetime_computation.h
@@ -274,8 +274,8 @@ struct SubtractYearsImpl :
SubtractIntervalImpl<AddYearsImpl<DateType>, DateType
}
\
};
DECLARE_DATE_FUNCTIONS(DateDiffImpl, datediff, DataTypeInt32, (ts0.daynr() -
ts1.daynr()));
-// DECLARE_DATE_FUNCTIONS(TimeDiffImpl, timediff, DataTypeTime,
ts0.second_diff(ts1));
-// Expands to
+// DECLARE_DATE_FUNCTIONS(TimeDiffImpl, timediff, DataTypeTime,
ts0.datetime_diff_in_seconds(ts1));
+// Expands to below here because it use Time type which need some special deal.
template <typename DateType1, typename DateType2>
struct TimeDiffImpl {
using DateValueType1 = date_cast::TypeToValueTypeV<DateType1>;
@@ -297,7 +297,7 @@ struct TimeDiffImpl {
if constexpr (UsingTimev2) {
// refer to https://dev.mysql.com/doc/refman/5.7/en/time.html
// the time type value between '-838:59:59' and '838:59:59', so
the return value should limited
- int64_t diff_m = ts0.microsecond_diff(ts1);
+ int64_t diff_m = ts0.datetime_diff_in_microseconds(ts1);
if (diff_m > limit_value) {
return (double)limit_value;
} else if (diff_m < -1 * limit_value) {
@@ -306,7 +306,7 @@ struct TimeDiffImpl {
return (double)diff_m;
}
} else {
- return (double)((1000 * 1000) * ts0.second_diff(ts1));
+ return (double)((1000 * 1000) * ts0.datetime_diff_in_seconds(ts1));
}
}
static DataTypes get_variadic_argument_types() {
diff --git a/be/src/vec/functions/function_datetime_floor_ceil.cpp
b/be/src/vec/functions/function_datetime_floor_ceil.cpp
index bf74deaed1d..07e35f37b85 100644
--- a/be/src/vec/functions/function_datetime_floor_ceil.cpp
+++ b/be/src/vec/functions/function_datetime_floor_ceil.cpp
@@ -713,7 +713,7 @@ struct TimeRound {
trivial_part_ts1 = ts1.second();
}
if constexpr (Impl::Unit == SECOND) {
- diff = ts2.second_diff(ts1);
+ diff = ts2.datetime_diff_in_seconds(ts1);
trivial_part_ts1 = 0;
trivial_part_ts2 = 0;
}
@@ -752,7 +752,7 @@ struct TimeRound {
trivial_part_ts1 = ts1.second();
}
if constexpr (Impl::Unit == SECOND) {
- diff = ts2.second_diff(ts1);
+ diff = ts2.datetime_diff_in_seconds(ts1);
trivial_part_ts1 = 0;
trivial_part_ts2 = 0;
}
@@ -793,7 +793,7 @@ struct TimeRound {
ts1.to_date_int_val() &
MASK_YEAR_MONTH_DAY_HOUR_MINUTE_FOR_DATETIMEV2;
}
if constexpr (Impl::Unit == SECOND) {
- diff = ts2.second_diff(ts1);
+ diff = ts2.datetime_diff_in_seconds(ts1);
trivial_part_ts2 = ts2.microsecond();
trivial_part_ts1 = ts1.microsecond();
}
diff --git a/be/src/vec/runtime/vdatetime_value.cpp
b/be/src/vec/runtime/vdatetime_value.cpp
index 5cd2e5f4777..d161c64fd1f 100644
--- a/be/src/vec/runtime/vdatetime_value.cpp
+++ b/be/src/vec/runtime/vdatetime_value.cpp
@@ -3942,9 +3942,9 @@ template void
VecDateTimeValue::create_from_date_v2<DateTimeV2ValueType>(
template void VecDateTimeValue::create_from_date_v2<DateTimeV2ValueType>(
DateV2Value<DateTimeV2ValueType>&& value, TimeType type);
-template int64_t VecDateTimeValue::second_diff<DateV2Value<DateV2ValueType>>(
+template int64_t
VecDateTimeValue::datetime_diff_in_seconds<DateV2Value<DateV2ValueType>>(
const DateV2Value<DateV2ValueType>& rhs) const;
-template int64_t
VecDateTimeValue::second_diff<DateV2Value<DateTimeV2ValueType>>(
+template int64_t
VecDateTimeValue::datetime_diff_in_seconds<DateV2Value<DateTimeV2ValueType>>(
const DateV2Value<DateTimeV2ValueType>& rhs) const;
#define DELARE_DATE_ADD_INTERVAL(DateValueType1, DateValueType2)
\
diff --git a/be/src/vec/runtime/vdatetime_value.h
b/be/src/vec/runtime/vdatetime_value.h
index 0c991c14e4a..b337fea46a2 100644
--- a/be/src/vec/runtime/vdatetime_value.h
+++ b/be/src/vec/runtime/vdatetime_value.h
@@ -167,6 +167,7 @@ const int TIME_MAX_VALUE_SECONDS = 3600 * TIME_MAX_HOUR +
60 * TIME_MAX_MINUTE +
constexpr int HOUR_PER_DAY = 24;
constexpr int64_t SECOND_PER_HOUR = 3600;
constexpr int64_t SECOND_PER_MINUTE = 60;
+constexpr int64_t MS_PER_SECOND = 1000 * 1000;
inline constexpr int S_DAYS_IN_MONTH[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31,
30, 31, 30, 31};
@@ -426,7 +427,7 @@ public:
void unchecked_set_time(uint32_t year, uint32_t month, uint32_t day,
uint32_t hour,
uint32_t minute, uint32_t second);
- int64_t daynr() const { return calc_daynr(_year, _month, _day); }
+ uint32_t daynr() const { return calc_daynr(_year, _month, _day); }
int year() const { return _year; }
int month() const { return _month; }
@@ -650,7 +651,7 @@ public:
}
template <typename T>
- int64_t second_diff(const T& rhs) const {
+ int64_t datetime_diff_in_seconds(const T& rhs) const {
return (daynr() - rhs.daynr()) * SECOND_PER_HOUR * HOUR_PER_DAY +
time_part_diff(rhs);
}
@@ -892,6 +893,7 @@ public:
void unchecked_set_time(uint8_t hour, uint8_t minute, uint8_t second,
uint32_t microsecond);
+ // we frequently use this to do arithmetic operation, so use signed
int64_t to avoid overflow.
int64_t daynr() const {
return calc_daynr(date_v2_value_.year_, date_v2_value_.month_,
date_v2_value_.day_);
}
@@ -1136,34 +1138,57 @@ public:
//only calculate the diff of dd:mm:ss
template <typename RHS>
- int64_t time_part_diff(const RHS& rhs) const {
+ int64_t time_part_diff_without_ms(const RHS& rhs) const {
return time_part_to_seconds() - rhs.time_part_to_seconds();
}
//only calculate the diff of dd:mm:ss.SSSSSS
template <typename RHS>
- int64_t time_part_diff_microsecond(const RHS& rhs) const {
+ int64_t time_part_diff_in_ms(const RHS& rhs) const {
return time_part_to_microsecond() - rhs.time_part_to_microsecond();
}
template <typename RHS>
- int64_t second_diff(const RHS& rhs) const {
- return (daynr() - rhs.daynr()) * SECOND_PER_HOUR * HOUR_PER_DAY +
time_part_diff(rhs);
+ int64_t datetime_diff_in_seconds(const RHS& rhs) const {
+ return (daynr() - rhs.daynr()) * SECOND_PER_HOUR * HOUR_PER_DAY +
+ time_part_diff_without_ms(rhs);
+ }
+
+ template <typename RHS>
+ int32_t date_diff_in_days(const RHS& rhs) const {
+ return daynr() - rhs.daynr(); // arithmetic calculation will auto
promote to signed int32
+ }
+
+ int32_t date_diff_in_days_round_to_zero_by_time(const auto& rhs) const {
+ int32_t day = this->date_diff_in_days(rhs);
+ int64_t ms_diff = this->time_part_diff_in_ms(rhs);
+ if (day > 0 && ms_diff < 0) {
+ day--;
+ } else if (day < 0 && ms_diff > 0) {
+ day++;
+ }
+ return day;
}
// used by INT microseconds_diff(DATETIME enddate, DATETIME startdate)
- // return it's int type, so shouldn't have any limit.
+ // return value is int type, so shouldn't have any limit.
// when used by TIME TIMEDIFF(DATETIME expr1, DATETIME expr2), it's return
time type, should have limited.
template <typename RHS>
- int64_t microsecond_diff(const RHS& rhs) const {
- int64_t diff_m = (daynr() - rhs.daynr()) * SECOND_PER_HOUR *
HOUR_PER_DAY * 1000 * 1000 +
- time_part_diff_microsecond(rhs);
+ int64_t datetime_diff_in_microseconds(const RHS& rhs) const {
+ int64_t diff_m = (daynr() - rhs.daynr()) * HOUR_PER_DAY *
SECOND_PER_HOUR * MS_PER_SECOND +
+ time_part_diff_in_ms(rhs);
return diff_m;
}
- bool can_cast_to_date_without_loss_accuracy() {
- return this->hour() == 0 && this->minute() == 0 && this->second() == 0
&&
- this->microsecond() == 0;
+ int64_t datetime_diff_in_seconds_round_to_zero_by_ms(const auto& rhs)
const {
+ int64_t second = this->datetime_diff_in_seconds(rhs);
+ int32_t ms_diff = this->microsecond() - rhs.microsecond();
+ if (second > 0 && ms_diff < 0) {
+ second--;
+ } else if (second < 0 && ms_diff > 0) {
+ second++;
+ }
+ return second;
}
underlying_value to_date_int_val() const { return int_val_; }
@@ -1388,17 +1413,17 @@ int64_t datetime_diff(const VecDateTimeValue&
ts_value1, const VecDateTimeValue&
return day;
}
case HOUR: {
- int64_t second = ts_value2.second_diff(ts_value1);
+ int64_t second = ts_value2.datetime_diff_in_seconds(ts_value1);
int64_t hour = second / 60 / 60;
return hour;
}
case MINUTE: {
- int64_t second = ts_value2.second_diff(ts_value1);
+ int64_t second = ts_value2.datetime_diff_in_seconds(ts_value1);
int64_t minute = second / 60;
return minute;
}
case SECOND: {
- int64_t second = ts_value2.second_diff(ts_value1);
+ int64_t second = ts_value2.datetime_diff_in_seconds(ts_value1);
return second;
}
}
@@ -1406,10 +1431,15 @@ int64_t datetime_diff(const VecDateTimeValue&
ts_value1, const VecDateTimeValue&
return 0;
}
-template <TimeUnit unit, typename T0, typename T1>
+// ROUND the result TO ZERO( not FLOOR). for datetime_diff<year>, everything
less than year is the remainder.
+// "ROUND TO ZERO" means `years_diff('2020-05-05', '2015-06-06')` gets 4 and
+// `years_diff('2015-06-06', '2020-05-05')` gets -4.
+template <TimeUnit UNIT, typename T0, typename T1>
int64_t datetime_diff(const DateV2Value<T0>& ts_value1, const DateV2Value<T1>&
ts_value2) {
constexpr uint64_t uint64_minus_one = -1;
- switch (unit) {
+ switch (UNIT) {
+ // for YEAR and MONTH: calculate the diff of year or month, and use
bitmask to get the remainder of all other
+ // parts. then round to zero by the remainder.
case YEAR: {
int year = (ts_value2.year() - ts_value1.year());
if constexpr (std::is_same_v<T0, T1>) {
@@ -1494,47 +1524,27 @@ int64_t datetime_diff(const DateV2Value<T0>& ts_value1,
const DateV2Value<T1>& t
return month;
}
case WEEK: {
- int day = ts_value2.daynr() - ts_value1.daynr();
- int64_t ms_diff = ts_value2.time_part_diff_microsecond(ts_value1);
- if (day > 0 && ms_diff < 0) {
- day--;
- } else if (day < 0 && ms_diff > 0) {
- day++;
- }
- return day / 7;
+ return ts_value2.date_diff_in_days_round_to_zero_by_time(ts_value1) /
7;
}
case DAY: {
- int day = ts_value2.daynr() - ts_value1.daynr();
- int64_t ms_diff = ts_value2.time_part_diff_microsecond(ts_value1);
- if (day > 0 && ms_diff < 0) {
- day--;
- } else if (day < 0 && ms_diff > 0) {
- day++;
- }
- return day;
+ return ts_value2.date_diff_in_days_round_to_zero_by_time(ts_value1);
}
case HOUR: {
- int64_t second = ts_value2.second_diff(ts_value1);
- int64_t hour = second / 60 / 60;
- return hour;
+ return
ts_value2.datetime_diff_in_seconds_round_to_zero_by_ms(ts_value1) / 60 / 60;
}
case MINUTE: {
- int64_t second = ts_value2.second_diff(ts_value1);
- int64_t minute = second / 60;
- return minute;
+ return
ts_value2.datetime_diff_in_seconds_round_to_zero_by_ms(ts_value1) / 60;
}
case SECOND: {
- int64_t second = ts_value2.second_diff(ts_value1);
- return second;
+ return
ts_value2.datetime_diff_in_seconds_round_to_zero_by_ms(ts_value1);
}
case MILLISECOND: {
- int64_t microsecond = ts_value2.microsecond_diff(ts_value1);
- int64_t millisecond = microsecond / 1000;
- return millisecond;
+ // C++ naturally rounds to zero
+ return ts_value2.datetime_diff_in_microseconds(ts_value1) / 1000;
}
case MICROSECOND: {
- int64_t microsecond = ts_value2.microsecond_diff(ts_value1);
- return microsecond;
+ // no precision loss
+ return ts_value2.datetime_diff_in_microseconds(ts_value1);
}
}
// Rethink the default return value
diff --git a/be/test/vec/runtime/vdatetime_value_test.cpp
b/be/test/vec/runtime/vdatetime_value_test.cpp
index 24d25f80ba7..e91ee626acc 100644
--- a/be/test/vec/runtime/vdatetime_value_test.cpp
+++ b/be/test/vec/runtime/vdatetime_value_test.cpp
@@ -677,4 +677,207 @@ TEST(VDateTimeValueTest,
date_v2_from_date_format_str_with_all_space) {
}
}
+TEST(VDateTimeValueTest, datetime_diff_test) {
+ // Test case 1: DATE to DATE - Different years, months, days
+ {
+ DateV2Value<DateV2ValueType> date1;
+ std::string date_str1 = "2020-01-15";
+ std::string format = "%Y-%m-%d";
+ EXPECT_TRUE(date1.from_date_format_str(format.data(), format.size(),
date_str1.data(),
+ date_str1.size()));
+
+ DateV2Value<DateV2ValueType> date2;
+ std::string date_str2 = "2023-08-20";
+ EXPECT_TRUE(date2.from_date_format_str(format.data(), format.size(),
date_str2.data(),
+ date_str2.size()));
+
+ // Test all time units for DATE to DATE
+ EXPECT_EQ(datetime_diff<TimeUnit::YEAR>(date1, date2), 3);
+ EXPECT_EQ(datetime_diff<TimeUnit::MONTH>(date1, date2), 3 * 12 + 7);
+ EXPECT_EQ(datetime_diff<TimeUnit::WEEK>(date1, date2), 187); //
Approximately
+ EXPECT_EQ(datetime_diff<TimeUnit::DAY>(date1, date2), 1313);
+ EXPECT_EQ(datetime_diff<TimeUnit::HOUR>(date1, date2), 1313 * 24);
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(date1, date2), 1313 * 24 *
60);
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(date1, date2), 1313 * 24 *
60 * 60);
+ EXPECT_EQ(datetime_diff<TimeUnit::MILLISECOND>(date1, date2), 1313 *
24 * 60 * 60 * 1000LL);
+ EXPECT_EQ(datetime_diff<TimeUnit::MICROSECOND>(date1, date2),
+ 1313 * 24 * 60 * 60 * 1000000LL);
+ }
+
+ // Test case 2: DATETIME to DATETIME - Testing rounding consistency across
units
+ {
+ // Test 2.1: Hour rounding - less than 1 hour should truncate to 0
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:00:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:59:59.999999";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::HOUR>(dt1, dt2), 0);
+ }
+
+ // Test 2.2: Hour rounding - exactly 1 hour
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:00:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 11:00:00.000000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::HOUR>(dt1, dt2), 1);
+ }
+
+ // Test 2.3: Minute rounding - less than 1 minute should truncate to 0
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:15:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:15:59.999999";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(dt1, dt2), 0);
+ }
+
+ // Test 2.4: Minute rounding - exactly 1 minute
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:15:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:16:00.000000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(dt1, dt2), 1);
+ }
+
+ // Test 2.5: Second rounding - less than 1 second should truncate to 0
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:15:30.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:15:30.999999";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(dt1, dt2), 0);
+ }
+
+ // Test 2.6: Second rounding - exactly 1 second
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:15:30.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:15:31.000000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(dt1, dt2), 1);
+ }
+
+ // Test 2.7: Mixed unit truncating case - complex example with
multiple units
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:00:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 11:29:45.750000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::HOUR>(dt1, dt2),
+ 1); // 1h 29m 45.75s = 1.496h, truncates to 1
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(dt1, dt2),
+ 89); // 1h 29m 45.75s = 89.7625m, truncates to 89
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(dt1, dt2),
+ 5385); // 1h 29m 45.75s = 5385.75s, truncates to 5385
+ }
+
+ // Test 2.8: Negative differences with truncating - less than 1 unit
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:15:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:14:30.250000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(dt1, dt2), 0); //
-0.5m truncates to 0
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(dt1, dt2), -29); //
-29.75s truncates to -29
+ }
+
+ // Test 2.9: Negative differences with truncating - exact unit
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 10:15:00.000000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:14:00.000000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(dt1, dt2), -1); //
Exactly -1 minute
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(dt1, dt2), -60); //
Exactly -60 seconds
+ }
+
+ // Test 2.10: Negative differences with truncating - complex example
+ {
+ DateV2Value<DateTimeV2ValueType> dt1;
+ std::string dt_str1 = "2023-05-10 11:30:30.750000";
+ std::string format = "%Y-%m-%d %H:%i:%s.%f";
+ EXPECT_TRUE(dt1.from_date_format_str(format.data(), format.size(),
dt_str1.data(),
+ dt_str1.size()));
+
+ DateV2Value<DateTimeV2ValueType> dt2;
+ std::string dt_str2 = "2023-05-10 10:00:00.000000";
+ EXPECT_TRUE(dt2.from_date_format_str(format.data(), format.size(),
dt_str2.data(),
+ dt_str2.size()));
+
+ EXPECT_EQ(datetime_diff<TimeUnit::HOUR>(dt1, dt2),
+ -1); // -1h 30m 30.75s = -1.5085h, truncates to -1
+ EXPECT_EQ(datetime_diff<TimeUnit::MINUTE>(dt1, dt2),
+ -90); // -1h 30m 30.75s = -90.5125m, truncates to -90
+ EXPECT_EQ(datetime_diff<TimeUnit::SECOND>(dt1, dt2),
+ -5430); // -1h 30m 30.75s = -5430.75s, truncates to -5430
+ }
+ }
+}
+
} // namespace doris::vectorized
diff --git
a/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out
b/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out
new file mode 100644
index 00000000000..347216879a5
Binary files /dev/null and
b/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out
differ
diff --git
a/regression-test/data/query_p0/sql_functions/array_functions/test_array_functions.out
b/regression-test/data/query_p0/sql_functions/array_functions/test_array_functions.out
index b2a47bde978..99b98518e9d 100644
Binary files
a/regression-test/data/query_p0/sql_functions/array_functions/test_array_functions.out
and
b/regression-test/data/query_p0/sql_functions/array_functions/test_array_functions.out
differ
diff --git
a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy
new file mode 100644
index 00000000000..25ead06d66f
--- /dev/null
+++
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy
@@ -0,0 +1,53 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_date_function_v2") {
+ sql """
+ admin set frontend config ("enable_date_conversion"="true");
+ """
+
+ qt_sql_diff1 "select days_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select days_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+ qt_sql_diff2 "select days_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select days_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+
+ qt_sql_diff3 "select weeks_diff('2023-10-15 00:00:00', '2023-10-08
00:00:00.1');"
+ testFoldConst("select weeks_diff('2023-10-15 00:00:00', '2023-10-08
00:00:00.1');")
+ qt_sql_diff4 "select weeks_diff('2023-10-15 00:00:00', '2023-10-08
00:00:00');"
+ testFoldConst("select weeks_diff('2023-10-15 00:00:00', '2023-10-08
00:00:00');")
+
+ qt_sql_diff5 "select hours_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select hours_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+ qt_sql_diff6 "select hours_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');"
+ testFoldConst("select hours_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');")
+ qt_sql_diff7 "select minutes_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select minutes_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+ qt_sql_diff8 "select minutes_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');"
+ testFoldConst("select minutes_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');")
+ qt_sql_diff9 "select seconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select seconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+ qt_sql_diff10 "select seconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');"
+ testFoldConst("select seconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');")
+ qt_sql_diff11 "select milliseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select milliseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+ qt_sql_diff12 "select milliseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');"
+ testFoldConst("select milliseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');")
+ qt_sql_diff13 "select microseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');"
+ testFoldConst("select microseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00.1');")
+ qt_sql_diff14 "select microseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');"
+ testFoldConst("select microseconds_diff('2023-10-15 00:00:00', '2023-10-14
00:00:00');")
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]