From f2b376e4650728e8754a710709d95412a73c8f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=9F=E6=88=90?= Date: Mon, 21 Sep 2020 18:15:19 +0800 Subject: [PATCH] add time_round builtin functions --- be/src/exprs/timestamp_functions.cpp | 129 ++++++++++++- be/src/exprs/timestamp_functions.h | 174 ++++++++++++++++++ be/src/runtime/datetime_value.h | 16 +- be/test/exprs/timestamp_functions_test.cpp | 117 ++++++++---- docs/.vuepress/sidebar/en.js | 1 + docs/.vuepress/sidebar/zh-CN.js | 1 + .../date-time-functions/time_round.md | 86 +++++++++ .../date-time-functions/time_round.md | 86 +++++++++ gensrc/script/doris_builtins_functions.py | 113 ++++++++++++ 9 files changed, 669 insertions(+), 54 deletions(-) create mode 100644 docs/en/sql-reference/sql-functions/date-time-functions/time_round.md create mode 100644 docs/zh-CN/sql-reference/sql-functions/date-time-functions/time_round.md diff --git a/be/src/exprs/timestamp_functions.cpp b/be/src/exprs/timestamp_functions.cpp index ea12b520e5866e..79faee089c8a9f 100644 --- a/be/src/exprs/timestamp_functions.cpp +++ b/be/src/exprs/timestamp_functions.cpp @@ -398,36 +398,36 @@ BigIntVal TimestampFunctions::timestamp_diff(FunctionContext* ctx, const DateTim switch (unit) { case YEAR: { int year = (ts_value2.year() - ts_value1.year()); - if (year >= 0) { + if (year > 0) { year -= (ts_value2.to_int64() % 10000000000 - ts_value1.to_int64() % 10000000000) < 0; - } else { + } else if (year < 0) { year += (ts_value2.to_int64() % 10000000000 - ts_value1.to_int64() % 10000000000) > 0; } return year; } case MONTH: { int month = (ts_value2.year() - ts_value1.year()) * 12 + (ts_value2.month() - ts_value1.month()); - if (month >= 0) { + if (month > 0) { month -= (ts_value2.to_int64() % 100000000 - ts_value1.to_int64() % 100000000) < 0; - } else { + } else if (month < 0) { month += (ts_value2.to_int64() % 100000000 - ts_value1.to_int64() % 100000000) > 0; } return month; } case WEEK: { int day = ts_value2.daynr() - ts_value1.daynr(); - if (day >= 0) { + if (day > 0) { day -= ts_value2.time_part_diff(ts_value1) < 0; - } else { + } else if (day < 0) { day += ts_value2.time_part_diff(ts_value1) > 0; } return day / 7; } case DAY: { int day = ts_value2.daynr() - ts_value1.daynr(); - if (day >= 0) { + if (day > 0) { day -= ts_value2.time_part_diff(ts_value1) < 0; - } else { + } else if (day < 0) { day += ts_value2.time_part_diff(ts_value1) > 0; } return day; @@ -496,6 +496,119 @@ void TimestampFunctions::format_close( } } +DateTimeVal from_olap_datetime(uint64_t datetime) { + DateTimeValue ts_value; + if (!ts_value.from_olap_datetime(datetime)) { + return DateTimeVal::null(); + } + + DateTimeVal ts_val; + ts_value.to_datetime_val(&ts_val); + return ts_val; +} + +#define _TR_4(TYPE, type, UNIT, unit) \ + DateTimeVal TimestampFunctions::unit##_##type( \ + FunctionContext* ctx, const DateTimeVal& ts_val, \ + const IntVal& period, const DateTimeVal& origin) { \ + return time_round(ctx, ts_val, period, origin); \ + } \ + DateTimeVal TimestampFunctions::unit##_##type( \ + FunctionContext* ctx, const DateTimeVal& ts_val, const DateTimeVal& origin) { \ + return time_round(ctx, ts_val, IntVal(1), origin); \ + } + +#define _TR_5(TYPE, type, UNIT, unit, ORIGIN) \ + DateTimeVal TimestampFunctions::unit##_##type( \ + FunctionContext* ctx, const DateTimeVal& ts_val) { \ + return time_round(ctx, ts_val, IntVal(1), ORIGIN); \ + } \ + DateTimeVal TimestampFunctions::unit##_##type( \ + FunctionContext* ctx, const DateTimeVal& ts_val, const IntVal& period) { \ + return time_round(ctx, ts_val, period, ORIGIN); \ + } + +#define FLOOR 0 +#define CEIL 1 + +static const DateTimeVal FIRST_DAY = from_olap_datetime(19700101000000); +static const DateTimeVal FIRST_SUNDAY = from_olap_datetime(19700104000000); + +#define TIME_ROUND(UNIT, unit, ORIGIN) \ + _TR_4(FLOOR, floor, UNIT, unit) _TR_4(CEIL, ceil, UNIT, unit) \ + _TR_5(FLOOR, floor, UNIT, unit, ORIGIN) _TR_5(CEIL, ceil, UNIT, unit, ORIGIN) + +TIME_ROUND(YEAR, year, FIRST_DAY) +TIME_ROUND(MONTH, month, FIRST_DAY) +TIME_ROUND(WEEK, week, FIRST_SUNDAY) +TIME_ROUND(DAY, day, FIRST_DAY) +TIME_ROUND(HOUR, hour, FIRST_DAY) +TIME_ROUND(MINUTE, minute, FIRST_DAY) +TIME_ROUND(SECOND, second, FIRST_DAY) + +template +DateTimeVal TimestampFunctions::time_round( + FunctionContext* ctx, const DateTimeVal& ts_val, + const IntVal& period, const DateTimeVal& origin) { + if (ts_val.is_null || period.is_null || period.val < 1 || origin.is_null) { + return DateTimeVal::null(); + } + + DateTimeValue ts1 = DateTimeValue::from_datetime_val(origin); + DateTimeValue ts2 = DateTimeValue::from_datetime_val(ts_val); + int64_t diff; + switch (unit) { + case YEAR: { + int year = (ts2.year() - ts1.year()); + diff = year - (ts2.to_int64() % 10000000000 < ts1.to_int64() % 10000000000); + break; + } + case MONTH: { + int month = (ts2.year() - ts1.year()) * 12 + (ts2.month() - ts1.month()); + diff = month - (ts2.to_int64() % 100000000 < ts1.to_int64() % 100000000); + break; + } + case WEEK: { + int week = ts2.daynr() / 7 - ts1.daynr() / 7; + diff = week - (ts2.daynr() % 7 < ts1.daynr() % 7 + (ts2.time_part_diff(ts1) < 0)); + break; + } + case DAY: { + int day = ts2.daynr() - ts1.daynr(); + diff = day - (ts2.time_part_diff(ts1) < 0); + break; + } + case HOUR: { + int hour = (ts2.daynr() - ts1.daynr()) * 24 + (ts2.hour() - ts1.hour()); + diff = hour - ((ts2.minute() * 60 + ts2.second()) < (ts1.minute() * 60 - ts1.second())); + break; + } + case MINUTE: { + int minute = (ts2.daynr() - ts1.daynr()) * 24 * 60 + + (ts2.hour() - ts1.hour()) * 60 + (ts2.minute() - ts1.minute()); + diff = minute - (ts2.second() < ts1.second()); + break; + } + case SECOND: { + diff = ts2.second_diff(ts1); + break; + } + default: + return DateTimeVal::null(); + } + int64_t count = period.val; + int64_t step = diff - (diff % count + count) % count + (type == FLOOR ? 0 : count); + bool is_neg = step < 0; + + TimeInterval interval(unit, is_neg ? -step : step, is_neg); + if (!ts1.date_add_interval(interval, unit)) { + return DateTimeVal::null(); + } + DateTimeVal new_ts_val; + ts1.to_datetime_val(&new_ts_val); + return new_ts_val; +} + StringVal TimestampFunctions::date_format( FunctionContext* ctx, const DateTimeVal& ts_val, const StringVal& format) { if (ts_val.is_null || format.is_null) { diff --git a/be/src/exprs/timestamp_functions.h b/be/src/exprs/timestamp_functions.h index 502436b87316e9..5aeafeec53c4a9 100644 --- a/be/src/exprs/timestamp_functions.h +++ b/be/src/exprs/timestamp_functions.h @@ -166,6 +166,180 @@ class TimestampFunctions { static doris_udf::BigIntVal seconds_diff( doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val1, const doris_udf::DateTimeVal& ts_val2); + // Period functions. + template + static doris_udf::DateTimeVal time_round( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal year_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal year_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal year_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal year_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal year_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal year_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal year_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal year_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal month_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal month_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal month_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal month_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal month_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal month_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal month_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal month_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal week_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal week_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal week_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal week_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal week_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal week_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal week_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal week_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal day_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal day_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal day_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal day_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal day_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal day_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal day_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal day_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal hour_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal hour_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal hour_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal hour_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal hour_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal hour_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal hour_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal hour_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal minute_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal minute_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal minute_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal minute_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal minute_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal minute_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal minute_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal minute_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal second_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal second_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal second_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal second_floor( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + + static doris_udf::DateTimeVal second_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); + static doris_udf::DateTimeVal second_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period); + static doris_udf::DateTimeVal second_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::DateTimeVal& origin); + static doris_udf::DateTimeVal second_ceil( + doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val, + const doris_udf::IntVal& period, const doris_udf::DateTimeVal& origin); + // TimeZone correlation functions. static doris_udf::DateTimeVal timestamp( doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& val); diff --git a/be/src/runtime/datetime_value.h b/be/src/runtime/datetime_value.h index 3310ecaacff0cd..9471ee02385858 100644 --- a/be/src/runtime/datetime_value.h +++ b/be/src/runtime/datetime_value.h @@ -57,13 +57,13 @@ enum TimeUnit { }; struct TimeInterval { - int32_t year; - int32_t month; - int32_t day; - int32_t hour; - int32_t minute; - int32_t second; - int32_t microsecond; + int64_t year; + int64_t month; + int64_t day; + int64_t hour; + int64_t minute; + int64_t second; + int64_t microsecond; bool is_neg; TimeInterval() : @@ -71,7 +71,7 @@ struct TimeInterval { hour(0), minute(0), second(0), microsecond(0), is_neg(false) { } - TimeInterval(TimeUnit unit, int count, bool is_neg_param) : + TimeInterval(TimeUnit unit, int64_t count, bool is_neg_param) : year(0), month(0), day(0), hour(0), minute(0), second(0), microsecond(0), is_neg(is_neg_param) { switch (unit) { diff --git a/be/test/exprs/timestamp_functions_test.cpp b/be/test/exprs/timestamp_functions_test.cpp index cc343acc2126a4..09e7637e1a5873 100644 --- a/be/test/exprs/timestamp_functions_test.cpp +++ b/be/test/exprs/timestamp_functions_test.cpp @@ -31,6 +31,13 @@ namespace doris { class FunctionContextImpl; +doris_udf::DateTimeVal datetime_val(int64_t value) { + DateTimeValue dt(value); + doris_udf::DateTimeVal tv; + dt.to_datetime_val(&tv); + return tv; +} + class TimestampFunctionsTest : public testing::Test { public: TimestampFunctionsTest() { } @@ -136,55 +143,89 @@ TEST_F(TimestampFunctionsTest, convert_tz_test) { delete context; } +#define ASSERT_DIFF(unit, diff, tv1, tv2) \ + ASSERT_EQ(diff, TimestampFunctions::unit##s_diff(context, datetime_val(tv1), datetime_val(tv2)).val); \ + ASSERT_EQ(-(diff), TimestampFunctions::unit##s_diff(context, datetime_val(tv2), datetime_val(tv1)).val); TEST_F(TimestampFunctionsTest, timestampdiff_test) { doris_udf::FunctionContext *context = new doris_udf::FunctionContext(); - DateTimeValue dt1(20120824000001); - doris_udf::DateTimeVal tv1; - dt1.to_datetime_val(&tv1); - DateTimeValue dt2(20120830000000); - doris_udf::DateTimeVal tv2; - dt2.to_datetime_val(&tv2); + int64_t tv1 = 20120824000001; + int64_t tv2 = 20120830000000; + + //YEAR + ASSERT_DIFF(year, 0, tv2, tv1); + ASSERT_DIFF(year, 1, tv1, 20100930000000); + //MONTH + ASSERT_DIFF(month, 0, tv2, tv1); + ASSERT_DIFF(month, 0, tv2, tv1); + ASSERT_DIFF(month, 0, 20120924000000, tv1); + ASSERT_DIFF(month, 1, tv1, 20120631000000); + //WEEK + ASSERT_DIFF(week, 0, tv2, tv1); + //DAY + ASSERT_DIFF(day, 5, tv2, tv1); + ASSERT_DIFF(day, 6, 20120830000001, tv1); + ASSERT_DIFF(day, 8, 20120901000001, tv1); + ASSERT_DIFF(day, 0, 20120823000005, tv1); + ASSERT_DIFF(day, 0, 20120824000001, tv1); + + //HOUR + ASSERT_DIFF(hour, 143, tv2, tv1); + //MINUTE + ASSERT_DIFF(minute, 8639, tv2, tv1); + //SECOND + ASSERT_DIFF(second, 518399, tv2, tv1); + + delete context; +} + +#define ASSERT_ROUND(unit, floor, ceil, ...) \ + ASSERT_EQ(datetime_val(floor).packed_time, TimestampFunctions::unit##_floor(context, __VA_ARGS__).packed_time); \ + ASSERT_EQ(datetime_val(ceil).packed_time, TimestampFunctions::unit##_ceil(context, __VA_ARGS__).packed_time); +TEST_F(TimestampFunctionsTest, time_round_test) { + doris_udf::FunctionContext *context = new doris_udf::FunctionContext(); + doris_udf::DateTimeVal tv = datetime_val(20120824132901); + doris_udf::IntVal three(3); + doris_udf::DateTimeVal wednesday = datetime_val(20200916000000); //YEAR - ASSERT_EQ(0, TimestampFunctions::years_diff(context, tv2, tv1).val); - DateTimeValue dt_year(20100930000000); - doris_udf::DateTimeVal tv_year; - dt_year.to_datetime_val(&tv_year); - ASSERT_EQ(-1, TimestampFunctions::years_diff(context, tv_year, tv1).val); + ASSERT_ROUND(year, 20120101000000, 20130101000000, tv); + ASSERT_ROUND(year, 20110916000000, 20120916000000, tv, wednesday); + ASSERT_ROUND(year, 20110916000000, 20140916000000, tv, three, wednesday); + //MONTH - ASSERT_EQ(0, TimestampFunctions::months_diff(context, tv2, tv1).val); - DateTimeValue dt3(20120924000000); - doris_udf::DateTimeVal tv3; - dt3.to_datetime_val(&tv3); - ASSERT_EQ(0, TimestampFunctions::months_diff(context, tv3, tv1).val); - DateTimeValue dt_month(20120631000000); - doris_udf::DateTimeVal tv_month; - dt_month.to_datetime_val(&tv_month); - ASSERT_EQ(-1, TimestampFunctions::months_diff(context, tv_month, tv1).val); + ASSERT_ROUND(month, 20120801000000, 20120901000000, tv); + ASSERT_ROUND(month, 20120701000000, 20121001000000, tv, three); + //WEEK - ASSERT_EQ(0, TimestampFunctions::weeks_diff(context, tv2, tv1).val); + ASSERT_ROUND(week, 20120819000000, 20120826000000, tv); + ASSERT_ROUND(week, 20120822000000, 20120829000000, tv, wednesday); + ASSERT_ROUND(week, 20120808000000, 20120829000000, tv, three, wednesday); + + doris_udf::DateTimeVal tv1 = datetime_val(20200202130920); + doris_udf::DateTimeVal monday = datetime_val(20200106000000); + ASSERT_ROUND(week, 20200202000000, 20200209000000, tv1); + ASSERT_ROUND(week, 20200127000000, 20200203000000, tv1, monday); + //DAY - ASSERT_EQ(5, TimestampFunctions::days_diff(context, tv2, tv1).val); - DateTimeValue dt4(20120830000001); - doris_udf::DateTimeVal tv4; - dt4.to_datetime_val(&tv4); - ASSERT_EQ(6, TimestampFunctions::days_diff(context, tv4, tv1).val); - DateTimeValue dt5(20120901000001); - doris_udf::DateTimeVal tv5; - dt5.to_datetime_val(&tv5); - ASSERT_EQ(8, TimestampFunctions::days_diff(context, tv5, tv1).val); - - DateTimeValue dt_day(20120823000005); - doris_udf::DateTimeVal tv_day; - dt_day.to_datetime_val(&tv_day); - ASSERT_EQ(0, TimestampFunctions::days_diff(context, tv_day, tv1).val); + doris_udf::DateTimeVal noon = datetime_val(19700101120000); + ASSERT_ROUND(day, 20120824000000, 20120825000000, tv); + ASSERT_ROUND(day, 20120824120000, 20120825120000, tv, noon); //HOUR - ASSERT_EQ(143, TimestampFunctions::hours_diff(context, tv2, tv1).val); + doris_udf::DateTimeVal random = datetime_val(29380329113953); + ASSERT_ROUND(hour, 20120824120000, 20120824150000, tv, three); + ASSERT_ROUND(hour, 20120824113953, 20120824143953, tv, three, random); + //MINUTE - ASSERT_EQ(8639, TimestampFunctions::minutes_diff(context, tv2, tv1).val); + doris_udf::IntVal val90(90); + ASSERT_ROUND(minute, 20120824132900, 20120824133000, tv); + ASSERT_ROUND(minute, 20120824120000, 20120824133000, tv, val90); + ASSERT_ROUND(minute, 20120824130953, 20120824143953, tv, val90, random); + //SECOND - ASSERT_EQ(518399, TimestampFunctions::seconds_diff(context, tv2, tv1).val); + ASSERT_ROUND(second, 20120824132900, 20120824132903, tv, three); + ASSERT_ROUND(second, 20120824132830, 20120824133000, tv, val90); + delete context; } diff --git a/docs/.vuepress/sidebar/en.js b/docs/.vuepress/sidebar/en.js index 0d07657e08b09c..c51ce50dbc81cf 100644 --- a/docs/.vuepress/sidebar/en.js +++ b/docs/.vuepress/sidebar/en.js @@ -241,6 +241,7 @@ module.exports = [ "now", "second", "str_to_date", + "time_round", "timediff", "timestampadd", "timestampdiff", diff --git a/docs/.vuepress/sidebar/zh-CN.js b/docs/.vuepress/sidebar/zh-CN.js index 2d694e79873f36..76038b2c1a3ee3 100644 --- a/docs/.vuepress/sidebar/zh-CN.js +++ b/docs/.vuepress/sidebar/zh-CN.js @@ -248,6 +248,7 @@ module.exports = [ "now", "second", "str_to_date", + "time_round", "timediff", "timestampadd", "timestampdiff", diff --git a/docs/en/sql-reference/sql-functions/date-time-functions/time_round.md b/docs/en/sql-reference/sql-functions/date-time-functions/time_round.md new file mode 100644 index 00000000000000..9fb70f86307e96 --- /dev/null +++ b/docs/en/sql-reference/sql-functions/date-time-functions/time_round.md @@ -0,0 +1,86 @@ +--- +{ + "title": "time_round", + "language": "en" +} +--- + + + +# time_round +## description +### Syntax + +`DATETIME TIME_ROUND(DATETIME expr)` + +`DATETIME TIME_ROUND(DATETIME expr, INT period)` + +`DATETIME TIME_ROUND(DATETIME expr, DATETIME origin)` + +`DATETIME TIME_ROUND(DATETIME expr, INT period, DATETIME origin)` + +The function name `TIME_ROUND` consists of two parts,Each part consists of the following optional values. +- `TIME`: `SECOND`, `MINUTE`, `HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR` +- `ROUND`: `FLOOR`, `CEIL` + +Returns the upper/lower bound of `expr`. + +- `period` specifies how many `TIME` units, the default is `1`. +- `origin` specifies the start time of the period, the default is `1970-01-01T00:00:00`, the start time of `WEEK` is Sunday, which is `1970-01-04T00:00:00`. Could be larger than `expr`. +- Please try to choose common `period`, such as 3 `MONTH`, 90 `MINUTE`. If you set a uncommon `period`, please also specify `origin`. + +## example + +``` + +MySQL> SELECT YEAR_FLOOR('20200202000000'); ++------------------------------+ +| year_floor('20200202000000') | ++------------------------------+ +| 2020-01-01 00:00:00 | ++------------------------------+ + + +MySQL> SELECT MONTH_CEIL(CAST('2020-02-02 13:09:20' AS DATETIME), 3); --quarter ++--------------------------------------------------------+ +| month_ceil(CAST('2020-02-02 13:09:20' AS DATETIME), 3) | ++--------------------------------------------------------+ +| 2020-04-01 00:00:00 | ++--------------------------------------------------------+ + + +MySQL> SELECT WEEK_CEIL('2020-02-02 13:09:20', '2020-01-06'); --monday ++---------------------------------------------------------+ +| week_ceil('2020-02-02 13:09:20', '2020-01-06 00:00:00') | ++---------------------------------------------------------+ +| 2020-02-03 00:00:00 | ++---------------------------------------------------------+ + + +MySQL> SELECT MONTH_CEIL(CAST('2020-02-02 13:09:20' AS DATETIME), 3, CAST('1970-01-09 00:00:00' AS DATETIME)); --next rent day ++-------------------------------------------------------------------------------------------------+ +| month_ceil(CAST('2020-02-02 13:09:20' AS DATETIME), 3, CAST('1970-01-09 00:00:00' AS DATETIME)) | ++-------------------------------------------------------------------------------------------------+ +| 2020-04-09 00:00:00 | ++-------------------------------------------------------------------------------------------------+ + +``` +## keyword +TIME_ROUND diff --git a/docs/zh-CN/sql-reference/sql-functions/date-time-functions/time_round.md b/docs/zh-CN/sql-reference/sql-functions/date-time-functions/time_round.md new file mode 100644 index 00000000000000..2e8d142b7c3c6b --- /dev/null +++ b/docs/zh-CN/sql-reference/sql-functions/date-time-functions/time_round.md @@ -0,0 +1,86 @@ +--- +{ + "title": "time_round", + "language": "zh-CN" +} +--- + + + +# time_round +## description +### Syntax + +`DATETIME TIME_ROUND(DATETIME expr)` + +`DATETIME TIME_ROUND(DATETIME expr, INT period)` + +`DATETIME TIME_ROUND(DATETIME expr, DATETIME origin)` + +`DATETIME TIME_ROUND(DATETIME expr, INT period, DATETIME origin)` + +函数名 `TIME_ROUND` 由两部分组成,每部分由以下可选值组成 +- `TIME`: `SECOND`, `MINUTE`, `HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR` +- `ROUND`: `FLOOR`, `CEIL` + +返回 `expr` 的上/下界。 + +- `period` 指定每个周期有多少个 `TIME` 单位组成,默认为 `1`。 +- `origin` 指定周期的开始时间,默认为 `1970-01-01T00:00:00`,`WEEK` 的默认开始时间为 `1970-01-04T00:00:00`,即周日。可以比 `expr` 大。 +- 请尽量选择常见 `period`,如 3 `MONTH`,90 `MINUTE` 等,如设置了非常用 `period`,请同时指定 `origin`。 + +## example + +``` + +MySQL> SELECT YEAR_FLOOR('20200202000000'); ++------------------------------+ +| year_floor('20200202000000') | ++------------------------------+ +| 2020-01-01 00:00:00 | ++------------------------------+ + + +MySQL> SELECT MONTH_CEIL(CAST('2020-02-02 13:09:20' AS DATETIME), 3); --quarter ++--------------------------------------------------------+ +| month_ceil(CAST('2020-02-02 13:09:20' AS DATETIME), 3) | ++--------------------------------------------------------+ +| 2020-04-01 00:00:00 | ++--------------------------------------------------------+ + + +MySQL> SELECT WEEK_CEIL('2020-02-02 13:09:20', '2020-01-06'); --monday ++---------------------------------------------------------+ +| week_ceil('2020-02-02 13:09:20', '2020-01-06 00:00:00') | ++---------------------------------------------------------+ +| 2020-02-03 00:00:00 | ++---------------------------------------------------------+ + + +MySQL> SELECT MONTH_CEIL(CAST('2020-02-02 13:09:20' AS DATETIME), 3, CAST('1970-01-09 00:00:00' AS DATETIME)); --next rent day ++-------------------------------------------------------------------------------------------------+ +| month_ceil(CAST('2020-02-02 13:09:20' AS DATETIME), 3, CAST('1970-01-09 00:00:00' AS DATETIME)) | ++-------------------------------------------------------------------------------------------------+ +| 2020-04-09 00:00:00 | ++-------------------------------------------------------------------------------------------------+ + +``` +## keyword +TIME_ROUND diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index bd44884a32092c..c281cfedadccc1 100755 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -260,6 +260,119 @@ [['seconds_diff'], 'BIGINT', ['DATETIME', 'DATETIME'], '_ZN5doris18TimestampFunctions12seconds_diffEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['year_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions10year_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['year_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions10year_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['year_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions10year_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['year_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions10year_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['year_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions9year_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['year_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions9year_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['year_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions9year_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['year_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions9year_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['month_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions11month_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['month_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions11month_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['month_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions11month_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['month_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions11month_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['month_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions10month_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['month_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions10month_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['month_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions10month_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['month_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions10month_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['week_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions10week_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['week_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions10week_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['week_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions10week_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['week_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions10week_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['week_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions9week_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['week_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions9week_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['week_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions9week_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['week_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions9week_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['day_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions9day_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['day_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions9day_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['day_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions9day_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['day_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions9day_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['day_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions8day_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['day_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions8day_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['day_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions8day_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['day_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions8day_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['hour_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions10hour_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['hour_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions10hour_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['hour_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions10hour_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['hour_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions10hour_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['hour_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions9hour_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['hour_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions9hour_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['hour_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions9hour_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['hour_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions9hour_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['minute_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions12minute_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['minute_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions12minute_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['minute_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions12minute_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['minute_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions12minute_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['minute_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions11minute_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['minute_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions11minute_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['minute_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions11minute_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['minute_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions11minute_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['second_floor'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions12second_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['second_floor'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions12second_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['second_floor'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions12second_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['second_floor'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions12second_floorEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + [['second_ceil'], 'DATETIME', ['DATETIME'], + '_ZN5doris18TimestampFunctions11second_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE'], + [['second_ceil'], 'DATETIME', ['DATETIME', 'DATETIME'], + '_ZN5doris18TimestampFunctions11second_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValES6_'], + [['second_ceil'], 'DATETIME', ['DATETIME', 'INT'], + '_ZN5doris18TimestampFunctions11second_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValE'], + [['second_ceil'], 'DATETIME', ['DATETIME', 'INT', 'DATETIME'], + '_ZN5doris18TimestampFunctions11second_ceilEPN9doris_udf15FunctionContextERKNS1_11DateTimeValERKNS1_6IntValES6_'], + # Math builtin functions [['pi'], 'DOUBLE', [], '_ZN5doris13MathFunctions2piEPN9doris_udf15FunctionContextE'],