From 9d7c498bad7a4c3a39e398942da0de0a964963c3 Mon Sep 17 00:00:00 2001 From: zhaochangle Date: Wed, 20 Dec 2023 02:16:34 +0800 Subject: [PATCH 1/2] 1 --- be/src/exec/es/es_scroll_parser.cpp | 14 +- be/src/vec/functions/date_time_transforms.h | 4 +- be/src/vec/functions/function_convert_tz.h | 13 +- .../function_date_or_datetime_computation.h | 146 ++++++++---------- be/src/vec/runtime/vdatetime_value.cpp | 30 ++-- be/src/vec/runtime/vdatetime_value.h | 17 +- .../date-time-functions/from-second.md | 78 ++++++++++ docs/sidebars.json | 1 + .../date-time-functions/from-second.md | 59 ++++--- .../test_from_millisecond_microsecond.out | 39 +++-- .../test_from_millisecond_microsecond.groovy | 6 + 11 files changed, 242 insertions(+), 165 deletions(-) create mode 100644 docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md diff --git a/be/src/exec/es/es_scroll_parser.cpp b/be/src/exec/es/es_scroll_parser.cpp index d7b16f656c39e6..726e86ea74ffbc 100644 --- a/be/src/exec/es/es_scroll_parser.cpp +++ b/be/src/exec/es/es_scroll_parser.cpp @@ -222,7 +222,8 @@ Status get_date_value_int(const rapidjson::Value& col, PrimitiveType type, bool if (ok) { // The local time zone can change by session variable `time_zone` // We should use the user specified time zone, not the actual system local time zone. - success = dt_val.from_unixtime(std::chrono::system_clock::to_time_t(tp), time_zone); + success = true; + dt_val.from_unixtime(std::chrono::system_clock::to_time_t(tp), time_zone); } } else if (str_length == 19) { // YYYY-MM-DDTHH:MM:SS @@ -231,8 +232,8 @@ Status get_date_value_int(const rapidjson::Value& col, PrimitiveType type, bool const bool ok = cctz::parse("%Y-%m-%dT%H:%M:%S", str_date, cctz::utc_time_zone(), &tp); if (ok) { - success = dt_val.from_unixtime(std::chrono::system_clock::to_time_t(tp), - time_zone); + success = true; + dt_val.from_unixtime(std::chrono::system_clock::to_time_t(tp), time_zone); } } else { // YYYY-MM-DD HH:MM:SS @@ -243,7 +244,8 @@ Status get_date_value_int(const rapidjson::Value& col, PrimitiveType type, bool // string long like "1677895728000" int64_t time_long = std::atol(str_date.c_str()); if (time_long > 0) { - success = dt_val.from_unixtime(time_long / 1000, time_zone); + success = true; + dt_val.from_unixtime(time_long / 1000, time_zone); } } else { // YYYY-MM-DD or others @@ -255,9 +257,7 @@ Status get_date_value_int(const rapidjson::Value& col, PrimitiveType type, bool } } else { - if (!dt_val.from_unixtime(col.GetInt64() / 1000, time_zone)) { - RETURN_ERROR_IF_CAST_FORMAT_ERROR(col, type); - } + dt_val.from_unixtime(col.GetInt64() / 1000, time_zone); } if constexpr (is_datetime_v1) { if (type == TYPE_DATE) { diff --git a/be/src/vec/functions/date_time_transforms.h b/be/src/vec/functions/date_time_transforms.h index 877a66e002acff..a513f9f154dd14 100644 --- a/be/src/vec/functions/date_time_transforms.h +++ b/be/src/vec/functions/date_time_transforms.h @@ -222,10 +222,10 @@ struct FromUnixTimeImpl { static inline auto execute(FromType val, StringRef format, ColumnString::Chars& res_data, size_t& offset, const cctz::time_zone& time_zone) { DateType dt; - if (format.size > 128 || val < 0 || val > TIMESTAMP_VALID_MAX || - !dt.from_unixtime(val, time_zone)) { + if (format.size > 128 || val < 0 || val > TIMESTAMP_VALID_MAX) { return std::pair {offset, true}; } + dt.from_unixtime(val, time_zone); char buf[128]; if (!dt.to_format_string(format.data, format.size, buf)) { diff --git a/be/src/vec/functions/function_convert_tz.h b/be/src/vec/functions/function_convert_tz.h index c8957122abe4da..bfd261640c12b5 100644 --- a/be/src/vec/functions/function_convert_tz.h +++ b/be/src/vec/functions/function_convert_tz.h @@ -201,12 +201,7 @@ class FunctionConvertTZ : public IFunction { result_column->insert_default(); return; } - - if (!ts_value2.from_unixtime(timestamp, to_tz)) { - result_null_map[index_now] = true; - result_column->insert_default(); - return; - } + ts_value2.from_unixtime(timestamp, to_tz); } else { int64_t timestamp; if (!ts_value.unix_timestamp(×tamp, from_tz)) { @@ -215,11 +210,7 @@ class FunctionConvertTZ : public IFunction { return; } - if (!ts_value2.from_unixtime(timestamp, to_tz)) { - result_null_map[index_now] = true; - result_column->insert_default(); - return; - } + ts_value2.from_unixtime(timestamp, to_tz); } result_column->insert(binary_cast(ts_value2)); diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h b/be/src/vec/functions/function_date_or_datetime_computation.h index 87d3fa8268be4c..b68f07292afc58 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.h +++ b/be/src/vec/functions/function_date_or_datetime_computation.h @@ -918,34 +918,30 @@ struct CurrentDateTimeImpl { DateValueType dtv; bool use_const; if constexpr (WithPrecision) { - if (const ColumnConst* const_column = check_and_get_column( + if (const auto* const_column = check_and_get_column( block.get_by_position(arguments[0]).column)) { int scale = const_column->get_int(0); - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->nano_seconds(), - context->state()->timezone_obj(), scale)) { - if constexpr (std::is_same_v) { - reinterpret_cast(&dtv)->set_type(TIME_DATETIME); - } - auto date_packed_int = binary_cast(dtv); - col_to->insert_data( - const_cast(reinterpret_cast(&date_packed_int)), 0); - - } else { - auto invalid_val = 0; - col_to->insert_data( - const_cast(reinterpret_cast(&invalid_val)), 0); + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->nano_seconds(), + context->state()->timezone_obj(), scale); + if constexpr (std::is_same_v) { + reinterpret_cast(&dtv)->set_type(TIME_DATETIME); } + auto date_packed_int = binary_cast(dtv); + col_to->insert_data( + const_cast(reinterpret_cast(&date_packed_int)), 0); + use_const = true; - } else if (const ColumnNullable* nullable_column = check_and_get_column( + } else if (const auto* nullable_column = check_and_get_column( block.get_by_position(arguments[0]).column)) { const auto& null_map = nullable_column->get_null_map_data(); const auto& nested_column = nullable_column->get_nested_column_ptr(); for (int i = 0; i < input_rows_count; i++) { - if (!null_map[i] && dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->nano_seconds(), - context->state()->timezone_obj(), - nested_column->get64(i))) { + if (!null_map[i]) { + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->nano_seconds(), + context->state()->timezone_obj(), + nested_column->get64(i)); if constexpr (std::is_same_v) { reinterpret_cast(&dtv)->set_type(TIME_DATETIME); } @@ -963,38 +959,27 @@ struct CurrentDateTimeImpl { } else { auto& int_column = block.get_by_position(arguments[0]).column; for (int i = 0; i < input_rows_count; i++) { - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->nano_seconds(), - context->state()->timezone_obj(), int_column->get64(i))) { - if constexpr (std::is_same_v) { - reinterpret_cast(&dtv)->set_type(TIME_DATETIME); - } - auto date_packed_int = binary_cast(dtv); - col_to->insert_data( - const_cast(reinterpret_cast(&date_packed_int)), - 0); - } else { - auto invalid_val = 0; - col_to->insert_data( - const_cast(reinterpret_cast(&invalid_val)), 0); + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->nano_seconds(), + context->state()->timezone_obj(), int_column->get64(i)); + if constexpr (std::is_same_v) { + reinterpret_cast(&dtv)->set_type(TIME_DATETIME); } + auto date_packed_int = binary_cast(dtv); + col_to->insert_data( + const_cast(reinterpret_cast(&date_packed_int)), 0); } use_const = false; } } else { - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->timezone_obj())) { - if constexpr (std::is_same_v) { - reinterpret_cast(&dtv)->set_type(TIME_DATETIME); - } - auto date_packed_int = binary_cast(dtv); - col_to->insert_data( - const_cast(reinterpret_cast(&date_packed_int)), 0); - } else { - auto invalid_val = 0; - col_to->insert_data(const_cast(reinterpret_cast(&invalid_val)), - 0); + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->timezone_obj()); + if constexpr (std::is_same_v) { + reinterpret_cast(&dtv)->set_type(TIME_DATETIME); } + auto date_packed_int = binary_cast(dtv); + col_to->insert_data(const_cast(reinterpret_cast(&date_packed_int)), + 0); use_const = true; } @@ -1017,31 +1002,21 @@ struct CurrentDateImpl { auto col_to = ColumnVector::create(); if constexpr (std::is_same_v) { DateV2Value dtv; - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->timezone_obj())) { - auto date_packed_int = binary_cast, uint32_t>( - *reinterpret_cast*>(&dtv)); - col_to->insert_data( - const_cast(reinterpret_cast(&date_packed_int)), 0); - } else { - auto invalid_val = 0; - col_to->insert_data(const_cast(reinterpret_cast(&invalid_val)), - 0); - } + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->timezone_obj()); + auto date_packed_int = binary_cast, uint32_t>( + *reinterpret_cast*>(&dtv)); + col_to->insert_data(const_cast(reinterpret_cast(&date_packed_int)), + 0); } else { VecDateTimeValue dtv; - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->timezone_obj())) { - reinterpret_cast(&dtv)->set_type(TIME_DATE); - auto date_packed_int = binary_cast( - *reinterpret_cast(&dtv)); - col_to->insert_data( - const_cast(reinterpret_cast(&date_packed_int)), 0); - } else { - auto invalid_val = 0; - col_to->insert_data(const_cast(reinterpret_cast(&invalid_val)), - 0); - } + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->timezone_obj()); + reinterpret_cast(&dtv)->set_type(TIME_DATE); + auto date_packed_int = binary_cast( + *reinterpret_cast(&dtv)); + col_to->insert_data(const_cast(reinterpret_cast(&date_packed_int)), + 0); } block.get_by_position(result).column = ColumnConst::create(std::move(col_to), input_rows_count); @@ -1057,15 +1032,11 @@ struct CurrentTimeImpl { size_t result, size_t input_rows_count) { auto col_to = ColumnVector::create(); VecDateTimeValue dtv; - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, - context->state()->timezone_obj())) { - double time = dtv.hour() * 3600l + dtv.minute() * 60l + dtv.second(); - time *= (1000 * 1000); - col_to->insert_data(const_cast(reinterpret_cast(&time)), 0); - } else { - auto invalid_val = 0; - col_to->insert_data(const_cast(reinterpret_cast(&invalid_val)), 0); - } + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->timezone_obj()); + double time = dtv.hour() * 3600l + dtv.minute() * 60l + dtv.second(); + time *= (1000 * 1000); + col_to->insert_data(const_cast(reinterpret_cast(&time)), 0); block.get_by_position(result).column = ColumnConst::create(std::move(col_to), input_rows_count); return Status::OK(); @@ -1144,15 +1115,30 @@ struct TimestampToDateTime : IFunction { auto null_vector = ColumnVector::create(); res_col->get_data().resize_fill(input_rows_count, 0); null_vector->get_data().resize_fill(input_rows_count, false); + NullMap& null_map = null_vector->get_data(); auto& res_data = res_col->get_data(); const cctz::time_zone& time_zone = context->state()->timezone_obj(); + for (int i = 0; i < input_rows_count; ++i) { Int64 value = column_data.get_element(i); auto& dt = reinterpret_cast&>(res_data[i]); - null_map[i] = !dt.from_unixtime(value / Impl::ratio, time_zone); - dt.set_microsecond((value % Impl::ratio) * ratio_to_micro); + + dt.from_unixtime(value / Impl::ratio, time_zone); + // for data less than 1 second with negative ms. we have to fix subtraction borrowing + if (value < 0 && value > -Impl::ratio) { + dt.date_add_interval( + TimeInterval {TimeUnit::SECOND, 1, true}); + value += Impl::ratio; + } + + if (dt.is_valid_date()) [[likely]] { + dt.set_microsecond((value % Impl::ratio) * ratio_to_micro); + } else { + null_map[i] = true; + } } + block.get_by_position(result).column = ColumnNullable::create(std::move(res_col), std::move(null_vector)); return Status::OK(); diff --git a/be/src/vec/runtime/vdatetime_value.cpp b/be/src/vec/runtime/vdatetime_value.cpp index b177164b9efbba..f625631ace9572 100644 --- a/be/src/vec/runtime/vdatetime_value.cpp +++ b/be/src/vec/runtime/vdatetime_value.cpp @@ -1731,10 +1731,11 @@ bool VecDateTimeValue::from_unixtime(int64_t timestamp, const std::string& timez if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { return false; } - return from_unixtime(timestamp, ctz); + from_unixtime(timestamp, ctz); + return true; } -bool VecDateTimeValue::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { +void VecDateTimeValue::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { static const cctz::time_point epoch = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); @@ -1742,6 +1743,8 @@ bool VecDateTimeValue::from_unixtime(int64_t timestamp, const cctz::time_zone& c const auto tp = cctz::convert(t, ctz); + // there's no overflow check since it's hot path + _neg = 0; _type = TIME_DATETIME; _year = tp.year(); @@ -1750,8 +1753,6 @@ bool VecDateTimeValue::from_unixtime(int64_t timestamp, const cctz::time_zone& c _hour = tp.hour(); _minute = tp.minute(); _second = tp.second(); - - return true; } const char* VecDateTimeValue::month_name() const { @@ -3202,20 +3203,21 @@ bool DateV2Value::from_unixtime(int64_t timestamp, const std::string& timezon if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { return false; } - return from_unixtime(timestamp, ctz); + from_unixtime(timestamp, ctz); + return true; } template -bool DateV2Value::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { +void DateV2Value::from_unixtime(int64_t timestamp, const cctz::time_zone& ctz) { static const cctz::time_point epoch = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); cctz::time_point t = epoch + cctz::seconds(timestamp); - const auto tp = cctz::convert(t, ctz); + // there's no overflow check since it's hot path + set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), 0); - return true; } template @@ -3225,11 +3227,12 @@ bool DateV2Value::from_unixtime(std::pair timestamp, if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { return false; } - return from_unixtime(timestamp, ctz); + from_unixtime(timestamp, ctz); + return true; } template -bool DateV2Value::from_unixtime(std::pair timestamp, +void DateV2Value::from_unixtime(std::pair timestamp, const cctz::time_zone& ctz) { static const cctz::time_point epoch = std::chrono::time_point_cast( @@ -3240,7 +3243,6 @@ bool DateV2Value::from_unixtime(std::pair timestamp, set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), timestamp.second); - return true; } template @@ -3250,11 +3252,12 @@ bool DateV2Value::from_unixtime(int64_t timestamp, int32_t nano_seconds, if (!TimezoneUtils::find_cctz_time_zone(timezone, ctz)) { return false; } - return from_unixtime(timestamp, nano_seconds, ctz, scale); + from_unixtime(timestamp, nano_seconds, ctz, scale); + return true; } template -bool DateV2Value::from_unixtime(int64_t timestamp, int32_t nano_seconds, +void DateV2Value::from_unixtime(int64_t timestamp, int32_t nano_seconds, const cctz::time_zone& ctz, int scale) { static const cctz::time_point epoch = std::chrono::time_point_cast( @@ -3269,7 +3272,6 @@ bool DateV2Value::from_unixtime(int64_t timestamp, int32_t nano_seconds, set_time(tp.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second(), nano_seconds / int_exp10(9 - scale) * int_exp10(6 - scale)); - return true; } template diff --git a/be/src/vec/runtime/vdatetime_value.h b/be/src/vec/runtime/vdatetime_value.h index 6fe7104ecdbd8a..1999c230809d4e 100644 --- a/be/src/vec/runtime/vdatetime_value.h +++ b/be/src/vec/runtime/vdatetime_value.h @@ -508,9 +508,10 @@ class VecDateTimeValue { // Now this type is a temp solution with little changes bool unix_timestamp(int64_t* timestamp, const cctz::time_zone& ctz) const; //construct datetime_value from timestamp and timezone - //timestamp is an internal timestamp value representing seconds since '1970-01-01 00:00:00' UTC + //timestamp is an internal timestamp value representing seconds since '1970-01-01 00:00:00' UTC. negative avaliable. + //we don't do any check in it because it's hot path. any usage want ensure the time legality should check itself. bool from_unixtime(int64_t, const std::string& timezone); - bool from_unixtime(int64_t, const cctz::time_zone& ctz); + void from_unixtime(int64_t, const cctz::time_zone& ctz); bool operator==(const VecDateTimeValue& other) const { // NOTE: This is not same with MySQL. @@ -970,14 +971,14 @@ class DateV2Value { bool unix_timestamp(std::pair* timestamp, const cctz::time_zone& ctz) const; //construct datetime_value from timestamp and timezone - //timestamp is an internal timestamp value representing seconds since '1970-01-01 00:00:00' UTC + //timestamp is an internal timestamp value representing seconds since '1970-01-01 00:00:00' UTC. negative avaliable. + //we don't do any check in it because it's hot path. any usage want ensure the time legality should check itself. bool from_unixtime(int64_t, const std::string& timezone); - bool from_unixtime(int64_t, const cctz::time_zone& ctz); + void from_unixtime(int64_t, const cctz::time_zone& ctz); bool from_unixtime(std::pair, const std::string& timezone); - bool from_unixtime(std::pair, const cctz::time_zone& ctz); - - bool from_unixtime(int64_t, int32_t, const std::string& timezone, const int scale); - bool from_unixtime(int64_t, int32_t, const cctz::time_zone& ctz, int scale); + void from_unixtime(std::pair, const cctz::time_zone& ctz); + bool from_unixtime(int64_t, int32_t, const std::string& timezone, int scale); + void from_unixtime(int64_t, int32_t, const cctz::time_zone& ctz, int scale); bool operator==(const DateV2Value& other) const { // NOTE: This is not same with MySQL. diff --git a/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md b/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md new file mode 100644 index 00000000000000..7700d416b3d382 --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md @@ -0,0 +1,78 @@ +--- +{ + "title": "FROM_SECOND", + "language": "en" +} +--- + + + +## from_second +### description +#### syntax + +`DATETIME FROM_SECOND(BIGINT unix_timestamp)` +`DATETIME FROM_MILLISECOND(BIGINT unix_timestamp)` +`DATETIME FROM_MICROSECOND(BIGINT unix_timestamp)` + +Converts a timestamp to its DATETIME represent, with argument as an integer and returned as a DATETIME type. Returns `NULL` if the time is made to be out of the range `[0000-01-01 00:00:00.000000, 9999-12-31 23:59:59.999999]`. + +### example + +``` +mysql> select from_second(-62167246602); ++---------------------------+ +| from_second(-62167246602) | ++---------------------------+ +| 0000-01-01 00:00:00 | ++---------------------------+ + +mysql> select from_second(-62167246603); ++---------------------------+ +| from_second(-62167246603) | ++---------------------------+ +| NULL | ++---------------------------+ + +mysql> select from_millisecond(12345678); ++----------------------------+ +| from_millisecond(12345678) | ++----------------------------+ +| 1970-01-01 11:25:45.678 | ++----------------------------+ + +mysql> select from_microsecond(253402271999999999); ++--------------------------------------+ +| from_microsecond(253402271999999999) | ++--------------------------------------+ +| 9999-12-31 23:59:59.999999 | ++--------------------------------------+ + +mysql> select from_microsecond(253402272000000000); ++--------------------------------------+ +| from_microsecond(253402272000000000) | ++--------------------------------------+ +| NULL | ++--------------------------------------+ +``` + +### keywords + + FROM_SECOND,FROM,SECOND,MILLISECOND,MICROSECOND diff --git a/docs/sidebars.json b/docs/sidebars.json index 0ba8f71b499475..da910610f08645 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -347,6 +347,7 @@ "sql-manual/sql-functions/date-time-functions/from-days", "sql-manual/sql-functions/date-time-functions/last-day", "sql-manual/sql-functions/date-time-functions/to-monday", + "sql-manual/sql-functions/date-time-functions/from-second", "sql-manual/sql-functions/date-time-functions/from-unixtime", "sql-manual/sql-functions/date-time-functions/unix-timestamp", "sql-manual/sql-functions/date-time-functions/utc-timestamp", diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md index 601cb4a70711e3..109632a7129974 100644 --- a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md +++ b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md @@ -32,34 +32,24 @@ under the License. `DATETIME FROM_MILLISECOND(BIGINT unix_timestamp)` `DATETIME FROM_MICROSECOND(BIGINT unix_timestamp)` -将时间戳转化为对应的 DATETIME - -传入的是整型,返回的是DATETIME类型 - +将时间戳转化为对应的 DATETIME,传入的是整型,返回的是DATETIME类型。若使时间超出`[0000-01-01 00:00:00.000000, 9999-12-31 23:59:59.999999]`范围,则返回`NULL`。 ### example ``` -mysql> select from_microsecond(0); -+----------------------------+ -| from_microsecond(0) | -+----------------------------+ -| 1970-01-01 08:00:00.000000 | -+----------------------------+ - -mysql> select from_microsecond(12345678); -+----------------------------+ -| from_microsecond(12345678) | -+----------------------------+ -| 1970-01-01 08:00:12.345678 | -+----------------------------+ - -mysql> select from_millisecond(0); -+-------------------------+ -| from_millisecond(0) | -+-------------------------+ -| 1970-01-01 08:00:00.000 | -+-------------------------+ +mysql> select from_second(-62167246602); ++---------------------------+ +| from_second(-62167246602) | ++---------------------------+ +| 0000-01-01 00:00:00 | ++---------------------------+ + +mysql> select from_second(-62167246603); ++---------------------------+ +| from_second(-62167246603) | ++---------------------------+ +| NULL | ++---------------------------+ mysql> select from_millisecond(12345678); +----------------------------+ @@ -68,14 +58,21 @@ mysql> select from_millisecond(12345678); | 1970-01-01 11:25:45.678 | +----------------------------+ -mysql> select from_second(21474836470); -+--------------------------+ -| from_second(21474836470) | -+--------------------------+ -| 2650-07-06 16:21:10 | -+--------------------------+ +mysql> select from_microsecond(253402271999999999); ++--------------------------------------+ +| from_microsecond(253402271999999999) | ++--------------------------------------+ +| 9999-12-31 23:59:59.999999 | ++--------------------------------------+ + +mysql> select from_microsecond(253402272000000000); ++--------------------------------------+ +| from_microsecond(253402272000000000) | ++--------------------------------------+ +| NULL | ++--------------------------------------+ ``` ### keywords - FROM_SECOND + FROM_SECOND,FROM,SECOND,MILLISECOND,MICROSECOND diff --git a/regression-test/data/correctness/test_from_millisecond_microsecond.out b/regression-test/data/correctness/test_from_millisecond_microsecond.out index 2dc7f81a74db15..877973d253b232 100644 --- a/regression-test/data/correctness/test_from_millisecond_microsecond.out +++ b/regression-test/data/correctness/test_from_millisecond_microsecond.out @@ -15,9 +15,9 @@ 3001-01-19 07:59:59 3001-01-19T07:59:59 \N 3001-01-19T08:00 2650-07-06 16:21:10 2650-07-06T16:21:10 -- !select4 -- -1919810114514 1919810114514 -89417891234789 488885820389 -1235817896941 1235817896941 +1919810114514 \N \N +89417891234789 \N \N +1235817896941 \N \N \N \N \N -- !select5 -- @@ -48,9 +48,9 @@ 2038-01-19 11:14:07 2038-01-19T11:14:07 2038-01-19 11:14:08 2038-01-19T11:14:08 2650-07-06 16:21:10 2650-07-06T16:21:10 -- !select10 -- -1919810114514 1919810114514 -89417891234789 488885820389 -1235817896941 1235817896941 +1919810114514 \N \N +89417891234789 \N \N +1235817896941 \N \N \N \N \N -- !select11 -- @@ -99,9 +99,9 @@ 3001-01-19 07:59:59 3001-01-19T07:59:59 \N 3001-01-19T08:00 2650-07-06 16:21:10 2650-07-06T16:21:10 -- !select4 -- -1919810114514 1919810114514 -89417891234789 488885820389 -1235817896941 1235817896941 +1919810114514 \N \N +89417891234789 \N \N +1235817896941 \N \N \N \N \N -- !select5 -- @@ -132,9 +132,9 @@ 2038-01-19 11:14:07 2038-01-19T11:14:07 2038-01-19 11:14:08 2038-01-19T11:14:08 2650-07-06 16:21:10 2650-07-06T16:21:10 -- !select10 -- -1919810114514 1919810114514 -89417891234789 488885820389 -1235817896941 1235817896941 +1919810114514 \N \N +89417891234789 \N \N +1235817896941 \N \N \N \N \N -- !select11 -- @@ -159,3 +159,18 @@ 2 1672502400 1672502400123 1672502400123000 3 1672502400 1672502400123 1672502400123456 +-- !sql -- +0000-01-01T00:00 + +-- !sql -- +\N + +-- !sql -- +9999-12-31T23:59:59.999999 + +-- !sql -- +\N + +-- !sql -- +1970-01-01T07:59:59.980 + diff --git a/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy b/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy index 39b5ab9c89d048..40f547ea6d113a 100644 --- a/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy +++ b/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy @@ -320,4 +320,10 @@ suite("test_from_millisecond_microsecond") { from millimicro order by id; """ + + qt_sql " select from_second(-62167246602) " + qt_sql " select from_second(-62167246603) " + qt_sql " select from_microsecond(253402271999999999) " + qt_sql " select from_microsecond(253402272000000000) " + qt_sql " select from_millisecond(-20) " } \ No newline at end of file From 9354891f7784a412b8bbb494eb50c91c21476d01 Mon Sep 17 00:00:00 2001 From: zhaochangle Date: Thu, 21 Dec 2023 15:06:41 +0800 Subject: [PATCH 2/2] 2 --- be/src/runtime/runtime_state.h | 1 + .../function_date_or_datetime_computation.h | 12 +++++------- .../date-time-functions/from-second.md | 13 ++++--------- .../date-time-functions/from-second.md | 13 ++++--------- .../test_from_millisecond_microsecond.out | 6 ------ .../test_from_millisecond_microsecond.groovy | 5 ++--- 6 files changed, 16 insertions(+), 34 deletions(-) diff --git a/be/src/runtime/runtime_state.h b/be/src/runtime/runtime_state.h index a4524d846fb376..989655a36c7458 100644 --- a/be/src/runtime/runtime_state.h +++ b/be/src/runtime/runtime_state.h @@ -132,6 +132,7 @@ class RuntimeState { TQueryType::type query_type() const { return _query_options.query_type; } int64_t timestamp_ms() const { return _timestamp_ms; } int32_t nano_seconds() const { return _nano_seconds; } + // if possible, use timezone_obj() rather than timezone() const std::string& timezone() const { return _timezone; } const cctz::time_zone& timezone_obj() const { return _timezone_obj; } const std::string& user() const { return _user; } diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h b/be/src/vec/functions/function_date_or_datetime_computation.h index b68f07292afc58..383fb10e34ad1d 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.h +++ b/be/src/vec/functions/function_date_or_datetime_computation.h @@ -1122,15 +1122,13 @@ struct TimestampToDateTime : IFunction { for (int i = 0; i < input_rows_count; ++i) { Int64 value = column_data.get_element(i); - auto& dt = reinterpret_cast&>(res_data[i]); + if (value < 0) { + null_map[i] = true; + continue; + } + auto& dt = reinterpret_cast&>(res_data[i]); dt.from_unixtime(value / Impl::ratio, time_zone); - // for data less than 1 second with negative ms. we have to fix subtraction borrowing - if (value < 0 && value > -Impl::ratio) { - dt.date_add_interval( - TimeInterval {TimeUnit::SECOND, 1, true}); - value += Impl::ratio; - } if (dt.is_valid_date()) [[likely]] { dt.set_microsecond((value % Impl::ratio) * ratio_to_micro); diff --git a/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md b/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md index 7700d416b3d382..bcbf92521ad4b8 100644 --- a/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md +++ b/docs/en/docs/sql-manual/sql-functions/date-time-functions/from-second.md @@ -32,21 +32,16 @@ under the License. `DATETIME FROM_MILLISECOND(BIGINT unix_timestamp)` `DATETIME FROM_MICROSECOND(BIGINT unix_timestamp)` -Converts a timestamp to its DATETIME represent, with argument as an integer and returned as a DATETIME type. Returns `NULL` if the time is made to be out of the range `[0000-01-01 00:00:00.000000, 9999-12-31 23:59:59.999999]`. +Converts a timestamp to its DATETIME represent, with argument as an integer and returned as a DATETIME type. Returns `NULL` if `unix_timestamp < 0` or if the function result is greater than `9999-12-31 23:59:59.999999`. ### example ``` -mysql> select from_second(-62167246602); -+---------------------------+ -| from_second(-62167246602) | -+---------------------------+ -| 0000-01-01 00:00:00 | -+---------------------------+ +mysql> set time_zone='Asia/Shanghai'; -mysql> select from_second(-62167246603); +mysql> select from_second(-1); +---------------------------+ -| from_second(-62167246603) | +| from_second(-1) | +---------------------------+ | NULL | +---------------------------+ diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md index 109632a7129974..df3c031966ba9a 100644 --- a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md +++ b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/from-second.md @@ -32,21 +32,16 @@ under the License. `DATETIME FROM_MILLISECOND(BIGINT unix_timestamp)` `DATETIME FROM_MICROSECOND(BIGINT unix_timestamp)` -将时间戳转化为对应的 DATETIME,传入的是整型,返回的是DATETIME类型。若使时间超出`[0000-01-01 00:00:00.000000, 9999-12-31 23:59:59.999999]`范围,则返回`NULL`。 +将时间戳转化为对应的 DATETIME,传入的是整型,返回的是DATETIME类型。若`unix_timestamp < 0` 或函数结果大于 `9999-12-31 23:59:59.999999`,则返回`NULL`。 ### example ``` -mysql> select from_second(-62167246602); -+---------------------------+ -| from_second(-62167246602) | -+---------------------------+ -| 0000-01-01 00:00:00 | -+---------------------------+ +mysql> set time_zone='Asia/Shanghai'; -mysql> select from_second(-62167246603); +mysql> select from_second(-1); +---------------------------+ -| from_second(-62167246603) | +| from_second(-1) | +---------------------------+ | NULL | +---------------------------+ diff --git a/regression-test/data/correctness/test_from_millisecond_microsecond.out b/regression-test/data/correctness/test_from_millisecond_microsecond.out index 877973d253b232..b4f5d9623f6ad7 100644 --- a/regression-test/data/correctness/test_from_millisecond_microsecond.out +++ b/regression-test/data/correctness/test_from_millisecond_microsecond.out @@ -159,9 +159,6 @@ 2 1672502400 1672502400123 1672502400123000 3 1672502400 1672502400123 1672502400123456 --- !sql -- -0000-01-01T00:00 - -- !sql -- \N @@ -171,6 +168,3 @@ -- !sql -- \N --- !sql -- -1970-01-01T07:59:59.980 - diff --git a/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy b/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy index 40f547ea6d113a..3e5548f0ac6a1b 100644 --- a/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy +++ b/regression-test/suites/correctness/test_from_millisecond_microsecond.groovy @@ -321,9 +321,8 @@ suite("test_from_millisecond_microsecond") { order by id; """ - qt_sql " select from_second(-62167246602) " - qt_sql " select from_second(-62167246603) " + sql " set time_zone='Asia/Shanghai' " + qt_sql " select from_second(-1) " qt_sql " select from_microsecond(253402271999999999) " qt_sql " select from_microsecond(253402272000000000) " - qt_sql " select from_millisecond(-20) " } \ No newline at end of file