Skip to content

Commit 3070773

Browse files
authored
[Feature](function) Support function TIME_FORMAT (#58592)
doc: apache/doris-website#3135 The TIME_FORMAT function is used to convert a time value into a string according to the specified format string. It supports formatting for TIME and DATETIME types, and the output is a string that conforms to the format requirements. ```sql SELECT TIME_FORMAT('2025-11-25 15:30:45', '%Y-%m-%d %H:%i:%s') AS date_with_time; ``` ```text +---------------------+ | date_with_time | +---------------------+ | 0000-00-00 15:30:45 | +---------------------+ ```
1 parent 7f6b387 commit 3070773

File tree

16 files changed

+1237
-248
lines changed

16 files changed

+1237
-248
lines changed

be/src/vec/functions/date_time_transforms.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "runtime/primitive_type.h"
3131
#include "udf/udf.h"
3232
#include "util/binary_cast.hpp"
33+
#include "vec/columns/column.h"
3334
#include "vec/columns/column_decimal.h"
3435
#include "vec/columns/column_nullable.h"
3536
#include "vec/columns/column_string.h"
@@ -43,6 +44,7 @@
4344
#include "vec/data_types/data_type_decimal.h"
4445
#include "vec/data_types/data_type_string.h"
4546
#include "vec/functions/date_format_type.h"
47+
#include "vec/runtime/time_value.h"
4648
#include "vec/runtime/vdatetime_value.h"
4749
#include "vec/utils/util.hpp"
4850

@@ -564,6 +566,76 @@ struct MicrosecondFromUnixtimeImpl {
564566
}
565567
};
566568

569+
template <PrimitiveType ArgPType>
570+
class FunctionTimeFormat : public IFunction {
571+
public:
572+
using ArgColType = typename PrimitiveTypeTraits<ArgPType>::ColumnType;
573+
using ArgCppType = typename PrimitiveTypeTraits<ArgPType>::CppType;
574+
575+
static constexpr auto name = "time_format";
576+
String get_name() const override { return name; }
577+
static FunctionPtr create() { return std::make_shared<FunctionTimeFormat>(); }
578+
DataTypes get_variadic_argument_types_impl() const override {
579+
return {std::make_shared<typename PrimitiveTypeTraits<ArgPType>::DataType>(),
580+
std::make_shared<vectorized::DataTypeString>()};
581+
}
582+
DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override {
583+
return make_nullable(std::make_shared<DataTypeString>());
584+
}
585+
size_t get_number_of_arguments() const override { return 2; }
586+
587+
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
588+
uint32_t result, size_t input_rows_count) const override {
589+
auto res_col = ColumnString::create();
590+
ColumnString::Chars& res_chars = res_col->get_chars();
591+
ColumnString::Offsets& res_offsets = res_col->get_offsets();
592+
593+
auto null_map = ColumnUInt8::create();
594+
auto& null_map_data = null_map->get_data();
595+
null_map_data.resize_fill(input_rows_count, 0);
596+
597+
res_offsets.reserve(input_rows_count);
598+
599+
ColumnPtr arg_col[2];
600+
bool is_const[2];
601+
for (size_t i = 0; i < 2; ++i) {
602+
const ColumnPtr& col = block.get_by_position(arguments[i]).column;
603+
std::tie(arg_col[i], is_const[i]) = unpack_if_const(col);
604+
}
605+
606+
const auto* datetime_col = assert_cast<const ArgColType*>(arg_col[0].get());
607+
const auto* format_col = assert_cast<const ColumnString*>(arg_col[1].get());
608+
for (size_t i = 0; i < input_rows_count; ++i) {
609+
const auto& datetime_val = datetime_col->get_element(index_check_const(i, is_const[0]));
610+
StringRef format = format_col->get_data_at(index_check_const(i, is_const[1]));
611+
TimeValue::TimeType time = get_time_value(datetime_val);
612+
613+
char buf[100 + SAFE_FORMAT_STRING_MARGIN];
614+
if (!TimeValue::to_format_string_conservative(format.data, format.size, buf,
615+
100 + SAFE_FORMAT_STRING_MARGIN, time)) {
616+
null_map_data[i] = 1;
617+
res_offsets.push_back(res_chars.size());
618+
continue;
619+
}
620+
res_chars.insert(buf, buf + strlen(buf));
621+
res_offsets.push_back(res_chars.size());
622+
}
623+
block.replace_by_position(result,
624+
ColumnNullable::create(std::move(res_col), std::move(null_map)));
625+
return Status::OK();
626+
}
627+
628+
private:
629+
TimeValue::TimeType get_time_value(const ArgCppType& datetime_val) const {
630+
if constexpr (ArgPType == PrimitiveType::TYPE_TIMEV2) {
631+
return static_cast<TimeValue::TimeType>(datetime_val);
632+
} else {
633+
return TimeValue::make_time(datetime_val.hour(), datetime_val.minute(),
634+
datetime_val.second(), datetime_val.microsecond());
635+
}
636+
}
637+
};
638+
567639
#include "common/compile_check_end.h"
568640
} // namespace doris::vectorized
569641

be/src/vec/functions/function_datetime_string_to_string.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ using FunctionFromUnixTimeNewDecimalOneArg =
3737
FunctionDateTimeStringToString<FromUnixTimeDecimalImpl<false>>;
3838
using FunctionFromUnixTimeNewDecimalTwoArg =
3939
FunctionDateTimeStringToString<FromUnixTimeDecimalImpl<true>>;
40+
using FunctionTimeFormatDate = FunctionTimeFormat<TYPE_DATEV2>;
41+
using FunctionTimeFormatDateTime = FunctionTimeFormat<TYPE_DATETIMEV2>;
42+
using FunctionTimeFormatTime = FunctionTimeFormat<TYPE_TIMEV2>;
4043

4144
void register_function_date_time_string_to_string(SimpleFunctionFactory& factory) {
4245
factory.register_function<FunctionDateFormatV2>();
@@ -47,6 +50,9 @@ void register_function_date_time_string_to_string(SimpleFunctionFactory& factory
4750
factory.register_function<FunctionFromUnixTimeNewDecimalOneArg>();
4851
factory.register_function<FunctionFromUnixTimeNewDecimalTwoArg>();
4952
factory.register_function<FunctionDateTimeV2DateFormat>();
53+
factory.register_function<FunctionTimeFormatDate>();
54+
factory.register_function<FunctionTimeFormatDateTime>();
55+
factory.register_function<FunctionTimeFormatTime>();
5056
}
5157

5258
} // namespace doris::vectorized

be/src/vec/runtime/time_value.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "runtime/define_primitive_type.h"
2929
#include "runtime/primitive_type.h"
3030
#include "util/date_func.h"
31+
#include "vec/runtime/vdatetime_value.h"
3132

3233
namespace doris {
3334
#include "common/compile_check_begin.h"
@@ -150,6 +151,21 @@ class TimeValue {
150151
}
151152

152153
static bool valid(double time) { return time <= MAX_TIME && time >= -MAX_TIME; }
154+
155+
static bool to_format_string_conservative(const char* format, size_t len, char* to,
156+
size_t max_valid_length, TimeType time) {
157+
// If time is negative, we here only add a '-' to the begining of res
158+
// This behavior is consistent with MySQL
159+
if (time < 0) {
160+
memcpy(to, "-", 1);
161+
++to;
162+
time = -time;
163+
}
164+
165+
return DatetimeValueUtil::to_format_string_without_check<true>(
166+
format, len, to, max_valid_length, 0, 0, 0, TimeValue::hour(time),
167+
TimeValue::minute(time), TimeValue::second(time), TimeValue::microsecond(time));
168+
}
153169
};
154170
} // namespace doris
155171
#include "common/compile_check_end.h"

0 commit comments

Comments
 (0)