Skip to content

Commit a56f9ec

Browse files
committed
[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 11f4276 commit a56f9ec

File tree

16 files changed

+1236
-248
lines changed

16 files changed

+1236
-248
lines changed

be/src/vec/functions/date_time_transforms.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "vec/data_types/data_type_decimal.h"
4040
#include "vec/data_types/data_type_string.h"
4141
#include "vec/functions/date_format_type.h"
42+
#include "vec/runtime/time_value.h"
4243
#include "vec/runtime/vdatetime_value.h"
4344
#include "vec/utils/util.hpp"
4445

@@ -427,6 +428,76 @@ struct FromUnixTimeDecimalImpl {
427428
}
428429
};
429430

431+
template <PrimitiveType ArgPType>
432+
class FunctionTimeFormat : public IFunction {
433+
public:
434+
using ArgColType = typename PrimitiveTypeTraits<ArgPType>::ColumnType;
435+
using ArgCppType = typename PrimitiveTypeTraits<ArgPType>::CppType;
436+
437+
static constexpr auto name = "time_format";
438+
String get_name() const override { return name; }
439+
static FunctionPtr create() { return std::make_shared<FunctionTimeFormat>(); }
440+
DataTypes get_variadic_argument_types_impl() const override {
441+
return {std::make_shared<typename PrimitiveTypeTraits<ArgPType>::DataType>(),
442+
std::make_shared<vectorized::DataTypeString>()};
443+
}
444+
DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override {
445+
return make_nullable(std::make_shared<DataTypeString>());
446+
}
447+
size_t get_number_of_arguments() const override { return 2; }
448+
449+
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
450+
uint32_t result, size_t input_rows_count) const override {
451+
auto res_col = ColumnString::create();
452+
ColumnString::Chars& res_chars = res_col->get_chars();
453+
ColumnString::Offsets& res_offsets = res_col->get_offsets();
454+
455+
auto null_map = ColumnUInt8::create();
456+
auto& null_map_data = null_map->get_data();
457+
null_map_data.resize_fill(input_rows_count, 0);
458+
459+
res_offsets.reserve(input_rows_count);
460+
461+
ColumnPtr arg_col[2];
462+
bool is_const[2];
463+
for (size_t i = 0; i < 2; ++i) {
464+
const ColumnPtr& col = block.get_by_position(arguments[i]).column;
465+
std::tie(arg_col[i], is_const[i]) = unpack_if_const(col);
466+
}
467+
468+
const auto* datetime_col = assert_cast<const ArgColType*>(arg_col[0].get());
469+
const auto* format_col = assert_cast<const ColumnString*>(arg_col[1].get());
470+
for (size_t i = 0; i < input_rows_count; ++i) {
471+
const auto& datetime_val = datetime_col->get_element(index_check_const(i, is_const[0]));
472+
StringRef format = format_col->get_data_at(index_check_const(i, is_const[1]));
473+
TimeValue::TimeType time = get_time_value(datetime_val);
474+
475+
char buf[100 + SAFE_FORMAT_STRING_MARGIN];
476+
if (!TimeValue::to_format_string_conservative(format.data, format.size, buf,
477+
100 + SAFE_FORMAT_STRING_MARGIN, time)) {
478+
null_map_data[i] = 1;
479+
res_offsets.push_back(res_chars.size());
480+
continue;
481+
}
482+
res_chars.insert(buf, buf + strlen(buf));
483+
res_offsets.push_back(res_chars.size());
484+
}
485+
block.replace_by_position(result,
486+
ColumnNullable::create(std::move(res_col), std::move(null_map)));
487+
return Status::OK();
488+
}
489+
490+
private:
491+
TimeValue::TimeType get_time_value(const ArgCppType& datetime_val) const {
492+
if constexpr (ArgPType == PrimitiveType::TYPE_TIMEV2) {
493+
return static_cast<TimeValue::TimeType>(datetime_val);
494+
} else {
495+
return TimeValue::make_time(datetime_val.hour(), datetime_val.minute(),
496+
datetime_val.second(), datetime_val.microsecond());
497+
}
498+
}
499+
};
500+
430501
#include "common/compile_check_end.h"
431502
} // namespace doris::vectorized
432503

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)