[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
This commit is contained in:
zclllyybb
2025-05-07 09:27:37 +08:00
committed by GitHub
parent 1de9e7f9e8
commit 02c3157e4c
16 changed files with 400 additions and 87 deletions

View File

@ -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;

View File

@ -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();
});

View File

@ -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;

View File

@ -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(

View File

@ -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 {

View File

@ -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;

View File

@ -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();

View File

@ -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())

View File

@ -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() {

View File

@ -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();
}

View File

@ -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) \

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,43 @@
-- This file is automatically generated. You should know what you did if you want to edit this
-- !sql_diff1 --
0
-- !sql_diff2 --
0
-- !sql_diff3 --
0
-- !sql_diff4 --
1
-- !sql_diff5 --
23
-- !sql_diff6 --
24
-- !sql_diff7 --
1439
-- !sql_diff8 --
1440
-- !sql_diff9 --
86399
-- !sql_diff10 --
86400
-- !sql_diff11 --
86399900
-- !sql_diff12 --
86400000
-- !sql_diff13 --
86399900000
-- !sql_diff14 --
86400000000

View File

@ -1766,7 +1766,7 @@
2022-05-18T12:00:00.123 2022-05-18T23:10:00.123456 4 ["2022-05-18 12:00:00", "2022-05-18 16:00:00", "2022-05-18 20:00:00"]
-- !table_select --
2022-05-18T12:00:00.123 2022-05-18T12:16 5 ["2022-05-18 12:00:00", "2022-05-18 12:05:00", "2022-05-18 12:10:00", "2022-05-18 12:15:00"]
2022-05-18T12:00:00.123 2022-05-18T12:16 5 ["2022-05-18 12:00:00", "2022-05-18 12:05:00", "2022-05-18 12:10:00"]
-- !table_select --
2022-05-18T12:00:10 2022-05-18T12:00:30 6 ["2022-05-18 12:00:10", "2022-05-18 12:00:16", "2022-05-18 12:00:22", "2022-05-18 12:00:28"]

View File

@ -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');")
}