From b05e7ded5094b66f3b81d2306871d08b91039544 Mon Sep 17 00:00:00 2001 From: lyw Date: Sat, 11 Oct 2025 17:28:48 +0800 Subject: [PATCH] [Feature](function) support sec_to_time (#62797) --- be/src/exprs/time_functions.cpp | 33 ++++++++ be/src/exprs/time_functions.h | 8 ++ be/test/exprs/time_functions_test.cpp | 77 ++++++++++++++++++- .../date-time-functions/sec_to_time.md | 37 +++++++++ .../date-time-functions/sec_to_time.md | 35 +++++++++ .../com/starrocks/catalog/FunctionSet.java | 1 + .../sql/analyzer/AnalyzeFunctionTest.java | 11 +++ gensrc/script/functions.py | 1 + test/sql/test_time_fn/R/test_time_fn | 29 ++++++- test/sql/test_time_fn/T/test_time_fn | 9 +++ 10 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 docs/en/sql-reference/sql-functions/date-time-functions/sec_to_time.md create mode 100644 docs/zh/sql-reference/sql-functions/date-time-functions/sec_to_time.md diff --git a/be/src/exprs/time_functions.cpp b/be/src/exprs/time_functions.cpp index 9f2f49ac577..f8b106361c5 100644 --- a/be/src/exprs/time_functions.cpp +++ b/be/src/exprs/time_functions.cpp @@ -3897,6 +3897,39 @@ StatusOr TimeFunctions::time_format(FunctionContext* context, const s return builder.build(ColumnHelper::is_all_const(columns)); } +constexpr static const int64_t MAX_TIME = 3023999L; + +static int64_t from_seconds_with_limit(int64_t time) { + if (time > MAX_TIME) { + return MAX_TIME; + } + if (time < -MAX_TIME) { + return -MAX_TIME; + } + return time; +} + +StatusOr TimeFunctions::sec_to_time(FunctionContext* context, const starrocks::Columns& columns) { + const auto& bigint_column = columns[0]; + + RETURN_IF_COLUMNS_ONLY_NULL(columns); + + auto bigint_viewer = ColumnViewer(bigint_column); + const size_t size = bigint_column->size(); + auto builder = ColumnBuilder(size); + + for (size_t i = 0; i < size; ++i) { + if (bigint_viewer.is_null(i)) { + builder.append_null(); + continue; + } + auto time = static_cast(from_seconds_with_limit(bigint_viewer.value(i))); + builder.append(time); + } + + return builder.build(ColumnHelper::is_all_const(columns)); +} + } // namespace starrocks #include "gen_cpp/opcode/TimeFunctions.inc" diff --git a/be/src/exprs/time_functions.h b/be/src/exprs/time_functions.h index 2a2806c7270..24032bb90b9 100644 --- a/be/src/exprs/time_functions.h +++ b/be/src/exprs/time_functions.h @@ -766,6 +766,14 @@ public: */ DEFINE_VECTORIZED_FN(time_to_sec); + /** + * return time + * @param: [int] + * @paramType columns: [BinaryColumn] + * @return Int64Column + */ + DEFINE_VECTORIZED_FN(sec_to_time); + /** * Returns the date of the first specified DOW (day of week) that occurs after the input date. * @param: [timestamp, dow] diff --git a/be/test/exprs/time_functions_test.cpp b/be/test/exprs/time_functions_test.cpp index 1f7c619bcbe..755fd0b06e7 100644 --- a/be/test/exprs/time_functions_test.cpp +++ b/be/test/exprs/time_functions_test.cpp @@ -4727,4 +4727,79 @@ TEST_F(TimeFunctionsTest, hourFromUnixTime) { } } -} // namespace starrocks +// Tests for sec_to_time function +TEST_F(TimeFunctionsTest, secToTimeTest) { + { + auto int_value = ColumnHelper::create_column(TypeDescriptor(TYPE_BIGINT), false); + + int_value->append_datum(0L); + int_value->append_datum(1L); + int_value->append_datum(60L); + int_value->append_datum(3600L); + int_value->append_datum(36000L); + int_value->append_datum(86399L); + int_value->append_datum(3024000L); + int_value->append_datum(4000000L); + + Columns columns; + columns.emplace_back(int_value); + + ColumnPtr result = TimeFunctions::sec_to_time(_utils->get_fn_ctx(), columns).value(); + auto v = ColumnHelper::cast_to(result); + + EXPECT_EQ(8, result->size()); + EXPECT_EQ(0, v->get_data()[0]); + EXPECT_EQ(1, v->get_data()[1]); + EXPECT_EQ(60, v->get_data()[2]); + EXPECT_EQ(3600, v->get_data()[3]); + EXPECT_EQ(36000, v->get_data()[4]); + EXPECT_EQ(86399, v->get_data()[5]); + EXPECT_EQ(3023999, v->get_data()[6]); + EXPECT_EQ(3023999, v->get_data()[7]); + } + { + auto int_value = ColumnHelper::create_column(TypeDescriptor(TYPE_BIGINT), false); + + int_value->append_datum(-0L); + int_value->append_datum(-1L); + int_value->append_datum(-60L); + int_value->append_datum(-3600L); + int_value->append_datum(-36000L); + int_value->append_datum(-86399L); + int_value->append_datum(-3024000L); + int_value->append_datum(-4000000L); + + Columns columns; + columns.emplace_back(int_value); + + ColumnPtr result = TimeFunctions::sec_to_time(_utils->get_fn_ctx(), columns).value(); + auto v = ColumnHelper::cast_to(result); + + EXPECT_EQ(8, result->size()); + EXPECT_EQ(0, v->get_data()[0]); + EXPECT_EQ(-1, v->get_data()[1]); + EXPECT_EQ(-60, v->get_data()[2]); + EXPECT_EQ(-3600, v->get_data()[3]); + EXPECT_EQ(-36000, v->get_data()[4]); + EXPECT_EQ(-86399, v->get_data()[5]); + EXPECT_EQ(-3023999, v->get_data()[6]); + EXPECT_EQ(-3023999, v->get_data()[7]); + } + + { + // Create null column + auto null_value = ColumnHelper::create_column(TypeDescriptor(TYPE_BIGINT), true); + + (void)null_value->append_nulls(1); + + Columns columns; + columns.emplace_back(null_value); + + ColumnPtr result = TimeFunctions::sec_to_time(_utils->get_fn_ctx(), columns).value(); + + EXPECT_EQ(1, result->size()); + ASSERT_TRUE(result->is_nullable()); + } +} + +} // namespace starrocks \ No newline at end of file diff --git a/docs/en/sql-reference/sql-functions/date-time-functions/sec_to_time.md b/docs/en/sql-reference/sql-functions/date-time-functions/sec_to_time.md new file mode 100644 index 00000000000..b2c413972c7 --- /dev/null +++ b/docs/en/sql-reference/sql-functions/date-time-functions/sec_to_time.md @@ -0,0 +1,37 @@ +--- +displayed_sidebar: docs +--- + +# sec_to_time + +## Description + +The SEC_TO_TIME function converts a value in seconds into a TIME type, returning the result in the format HH:MM:SS. +The input seconds represent the time elapsed since the start of a day (00:00:00). + +## Syntax + +```Haskell +TIME sec_to_time(BIGINT sec) +``` + +## Parameters + +`sec`: It must be of the INT type. + +## Return value + +Returns a TIME value in the format HH:MM:SS, representing the time calculated from the start of a day (00:00:00). +If sec is NULL, the function returns NULL. + +## Examples + +```plain text +select sec_to_time(43994); ++-----------------------------+ +| sec_to_time(43994) | ++-----------------------------+ +| '12:13:14'| ++-----------------------------+ +``` + diff --git a/docs/zh/sql-reference/sql-functions/date-time-functions/sec_to_time.md b/docs/zh/sql-reference/sql-functions/date-time-functions/sec_to_time.md new file mode 100644 index 00000000000..91266a3db26 --- /dev/null +++ b/docs/zh/sql-reference/sql-functions/date-time-functions/sec_to_time.md @@ -0,0 +1,35 @@ +--- +displayed_sidebar: docs +--- + +# sec_to_time + +## 功能 + +SEC_TO_TIME 函数将一个以秒为单位的值转换为 TIME 类型,返回格式为 HH:MM:SS。输入的秒数表示从一天的起点时间(00:00:00)开始计算的时间。 + +## 语法 + +```Haskell +TIME sec_to_time(BIGINT sec) +``` + +## 参数说明 + +`sec`:必填,输入的秒数,表示从一天起点时间(00:00:00)开始计算的秒数,支持正整数或负整数类型。 + +## 返回值说明 + +必填,输入的秒数,表示从一天起点时间(00:00:00)开始计算的秒数,支持正整数或负整数类型。 +如果输入的 sec 为 NULL,函数返回 NULL。 + +## 示例 + +```plain text +select sec_to_time(43994); ++-----------------------------+ +| sec_to_time(43994) | ++-----------------------------+ +| '12:13:14'| ++-----------------------------+ +``` diff --git a/fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java b/fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java index e93c4c99895..8ea7b5c55c8 100644 --- a/fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java +++ b/fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java @@ -131,6 +131,7 @@ public class FunctionSet { public static final String QUARTER = "quarter"; public static final String TIMESTAMP = "timestamp"; public static final String TIME_TO_SEC = "time_to_sec"; + public static final String SEC_TO_TIME = "sec_to_time"; public static final String STR2DATE = "str2date"; public static final String MICROSECONDS_ADD = "microseconds_add"; public static final String MICROSECONDS_SUB = "microseconds_sub"; diff --git a/fe/fe-core/src/test/java/com/starrocks/sql/analyzer/AnalyzeFunctionTest.java b/fe/fe-core/src/test/java/com/starrocks/sql/analyzer/AnalyzeFunctionTest.java index 55a2e90b059..2ae33ba86b8 100644 --- a/fe/fe-core/src/test/java/com/starrocks/sql/analyzer/AnalyzeFunctionTest.java +++ b/fe/fe-core/src/test/java/com/starrocks/sql/analyzer/AnalyzeFunctionTest.java @@ -323,4 +323,15 @@ public class AnalyzeFunctionTest { analyzeFail("select array_generate(1, 5, 'a')"); analyzeFail("select array_generate(1, 2, 3, 4)"); } + + @Test + public void testSecToTime() throws Exception { + analyzeFail("select sec_to_time()"); + analyzeSuccess("select sec_to_time(0)"); + analyzeSuccess("select sec_to_time(1)"); + analyzeSuccess("select sec_to_time(-1)"); + analyzeSuccess("select sec_to_time(3024000)"); + analyzeSuccess("select sec_to_time(-3024000)"); + } + } diff --git a/gensrc/script/functions.py b/gensrc/script/functions.py index f7751b92419..4cee21930fa 100644 --- a/gensrc/script/functions.py +++ b/gensrc/script/functions.py @@ -565,6 +565,7 @@ vectorized_functions = [ [50262, 'to_iso8601', True, False, 'VARCHAR', ['DATETIME'], 'TimeFunctions::datetime_to_iso8601'], [50263, 'to_iso8601', True, False, 'VARCHAR', ['DATE'], 'TimeFunctions::date_to_iso8601'], [50250, 'time_to_sec', True, False, 'BIGINT', ['TIME'], 'TimeFunctions::time_to_sec'], + [50251, 'sec_to_time', True, False, 'TIME', ['BIGINT'], 'TimeFunctions::sec_to_time'], # unix timestamp extended version to int64 # be sure to put before int32 version, so fe will find signature in order. diff --git a/test/sql/test_time_fn/R/test_time_fn b/test/sql/test_time_fn/R/test_time_fn index 916451f652c..ae3a9486969 100644 --- a/test/sql/test_time_fn/R/test_time_fn +++ b/test/sql/test_time_fn/R/test_time_fn @@ -984,4 +984,31 @@ select timestampadd(MILLISECOND, 1, '2019-01-02 00:00:00'); select timestampdiff(MILLISECOND, '2003-02-01 00:00:00', '2003-05-01 12:05:55'); -- result: 7733155000 --- !result \ No newline at end of file +-- !result + +-- name: sec_to_time +select sec_to_time(NULL); +-- result: +NULL +-- !result +select sec_to_time(0); +-- result: +00:00:00 +-- !result +select sec_to_time(1); +-- result: +00:00:01 +-- !result +select sec_to_time(-1); +-- result: +-00:00:01 +-- !result +select sec_to_time(3024000); +-- result: +839:59:59 +-- !result +select sec_to_time(-3024000); +-- result: +-839:59:59 +-- !result + diff --git a/test/sql/test_time_fn/T/test_time_fn b/test/sql/test_time_fn/T/test_time_fn index 832ae157781..fc46f9db811 100644 --- a/test/sql/test_time_fn/T/test_time_fn +++ b/test/sql/test_time_fn/T/test_time_fn @@ -327,3 +327,12 @@ select timestampadd(MILLISECOND, 1, '2019-01-02 00:00:00'); -- name: test_timestamp_diff select timestampdiff(MILLISECOND, '2003-02-01 00:00:00', '2003-05-01 12:05:55'); + + +-- name: sec_to_time +select sec_to_time(NULL); +select sec_to_time(0); +select sec_to_time(1); +select sec_to_time(-1); +select sec_to_time(3024000); +select sec_to_time(-3024000);