diff --git a/be/src/vec/functions/date_time_transforms.h b/be/src/vec/functions/date_time_transforms.h index 96b8738a38..0b564bf1bd 100644 --- a/be/src/vec/functions/date_time_transforms.h +++ b/be/src/vec/functions/date_time_transforms.h @@ -57,6 +57,7 @@ namespace doris::vectorized { #define TO_TIME_FUNCTION(CLASS, UNIT) TIME_FUNCTION_IMPL(CLASS, UNIT, UNIT()) TO_TIME_FUNCTION(ToYearImpl, year); +TO_TIME_FUNCTION(ToYearOfWeekImpl, year_of_week); TO_TIME_FUNCTION(ToQuarterImpl, quarter); TO_TIME_FUNCTION(ToMonthImpl, month); TO_TIME_FUNCTION(ToDayImpl, day); @@ -363,8 +364,8 @@ struct Transformer { } }; -template -struct Transformer> { +template typename Impl> +struct TransformerYear { static void vector(const PaddedPODArray& vec_from, PaddedPODArray& vec_to, NullMap& null_map) { size_t size = vec_from.size(); @@ -376,7 +377,7 @@ struct Transformer> { auto* __restrict null_map_ptr = null_map.data(); for (size_t i = 0; i < size; ++i) { - to_ptr[i] = ToYearImpl::execute(from_ptr[i]); + to_ptr[i] = Impl::execute(from_ptr[i]); } for (size_t i = 0; i < size; ++i) { @@ -392,11 +393,19 @@ struct Transformer> { auto* __restrict from_ptr = vec_from.data(); for (size_t i = 0; i < size; ++i) { - to_ptr[i] = ToYearImpl::execute(from_ptr[i]); + to_ptr[i] = Impl::execute(from_ptr[i]); } } }; +template +struct Transformer> + : public TransformerYear {}; + +template +struct Transformer> + : public TransformerYear {}; + template struct DateTimeTransformImpl { static Status execute(Block& block, const ColumnNumbers& arguments, size_t result, diff --git a/be/src/vec/functions/to_time_function.cpp b/be/src/vec/functions/to_time_function.cpp index 126296fc40..f160ad531a 100644 --- a/be/src/vec/functions/to_time_function.cpp +++ b/be/src/vec/functions/to_time_function.cpp @@ -16,15 +16,8 @@ // specific language governing permissions and limitations // under the License. -#include -#include -#include -#include - #include "vec/core/types.h" -#include "vec/data_types/data_type.h" #include "vec/data_types/data_type_date_time.h" -#include "vec/data_types/data_type_nullable.h" #include "vec/data_types/data_type_number.h" #include "vec/data_types/data_type_time_v2.h" #include "vec/functions/date_time_transforms.h" @@ -35,6 +28,8 @@ namespace doris::vectorized { using FunctionYear = FunctionDateOrDateTimeToSomething>; using FunctionYearV2 = FunctionDateOrDateTimeToSomething>; +using FunctionYearOfWeek = + FunctionDateOrDateTimeToSomething>; using FunctionQuarter = FunctionDateOrDateTimeToSomething>; using FunctionQuarterV2 = FunctionDateOrDateTimeToSomething>; using FunctionMonth = FunctionDateOrDateTimeToSomething>; @@ -102,6 +97,7 @@ void register_function_to_time_function(SimpleFunctionFactory& factory) { factory.register_function(); factory.register_function(); factory.register_function(); + factory.register_function(); factory.register_function(); factory.register_function(); factory.register_function(); diff --git a/be/src/vec/runtime/vdatetime_value.cpp b/be/src/vec/runtime/vdatetime_value.cpp index 7305def114..5cd2e5f477 100644 --- a/be/src/vec/runtime/vdatetime_value.cpp +++ b/be/src/vec/runtime/vdatetime_value.cpp @@ -3806,6 +3806,32 @@ bool DateV2Value::from_date_int64(int64_t value) { } } +// An ISO week-numbering year (also called ISO year informally) has 52 or 53 full weeks. That is 364 or 371 days instead of the usual 365 or 366 days. These 53-week years occur on all years that have Thursday as 1 January and on leap years that start on Wednesday. The extra week is sometimes referred to as a leap week, although ISO 8601 does not use this term. https://en.wikipedia.org/wiki/ISO_week_date +template +uint16_t DateV2Value::year_of_week() const { + constexpr uint8_t THURSDAY = 3; + + if (date_v2_value_.month_ == 1) { + constexpr uint8_t MAX_DISTANCE_WITH_THURSDAY = 6 - THURSDAY; + if (date_v2_value_.day_ <= MAX_DISTANCE_WITH_THURSDAY) { + auto weekday = calc_weekday(daynr(), false); + // if the current day is after Thursday and Thursday is in the previous year, return the previous year + return date_v2_value_.year_ - + (weekday > THURSDAY && weekday - THURSDAY > date_v2_value_.day_ - 1); + } + } else if (date_v2_value_.month_ == 12) { + constexpr uint8_t MAX_DISTANCE_WITH_THURSDAY = THURSDAY - 0; + if (S_DAYS_IN_MONTH[12] - date_v2_value_.day_ <= MAX_DISTANCE_WITH_THURSDAY) { + auto weekday = calc_weekday(daynr(), false); + // if the current day is before Thursday and Thursday is in the next year, return the next year + return date_v2_value_.year_ + + (weekday < THURSDAY && + (THURSDAY - weekday) > S_DAYS_IN_MONTH[12] - date_v2_value_.day_); + } + } + return date_v2_value_.year_; +} + template uint8_t DateV2Value::calc_week(const uint32_t& day_nr, const uint16_t& year, const uint8_t& month, const uint8_t& day, uint8_t mode, diff --git a/be/src/vec/runtime/vdatetime_value.h b/be/src/vec/runtime/vdatetime_value.h index ebda0e60ec..0c991c14e4 100644 --- a/be/src/vec/runtime/vdatetime_value.h +++ b/be/src/vec/runtime/vdatetime_value.h @@ -937,6 +937,7 @@ public: } uint16_t year() const { return date_v2_value_.year_; } + uint16_t year_of_week() const; uint8_t month() const { return date_v2_value_.month_; } int quarter() const { return (date_v2_value_.month_ - 1) / 3 + 1; } int week() const { return week(mysql_week_mode(0)); } //00-53 diff --git a/be/test/vec/function/function_time_test.cpp b/be/test/vec/function/function_time_test.cpp index 0ba7a3398a..d0e698029a 100644 --- a/be/test/vec/function/function_time_test.cpp +++ b/be/test/vec/function/function_time_test.cpp @@ -1759,4 +1759,16 @@ TEST(VTimestampFunctionsTest, seconds_sub_v2_test) { } } +TEST(VTimestampFunctionsTest, year_of_week_test) { + std::string func_name = "year_of_week"; + { + InputTypeSet input_types = {TypeIndex::DateV2}; + DataSet data_set = {{{std::string("2005-01-01")}, int16_t(2004)}, + {{std::string("2008-12-30")}, int16_t(2009)}, + {{std::string("12008-12-30")}, Null()}, + {{Null()}, Null()}}; + static_cast(check_function(func_name, input_types, data_set)); + } +} + } // namespace doris::vectorized diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java index 9b2a1bfa56..228170a2fb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java @@ -458,6 +458,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.XxHash64; import org.apache.doris.nereids.trees.expressions.functions.scalar.Year; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor; +import org.apache.doris.nereids.trees.expressions.functions.scalar.YearOfWeek; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearWeek; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff; @@ -935,6 +936,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(Year.class, "year"), scalar(YearCeil.class, "year_ceil"), scalar(YearFloor.class, "year_floor"), + scalar(YearOfWeek.class, "year_of_week", "yow"), scalar(YearWeek.class, "yearweek"), scalar(YearsAdd.class, "years_add"), scalar(YearsDiff.class, "years_diff"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java index 780c5922c6..5e93864304 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java @@ -86,6 +86,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.ToDays; import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekOfYear; import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff; import org.apache.doris.nereids.trees.expressions.functions.scalar.Year; +import org.apache.doris.nereids.trees.expressions.functions.scalar.YearOfWeek; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsSub; @@ -416,6 +417,21 @@ public class ExpressionEstimation extends ExpressionVisitor SIGNATURES = ImmutableList.of( + FunctionSignature.ret(SmallIntType.INSTANCE).args(DateV2Type.INSTANCE)); + + /** + * constructor with 1 argument. + */ + public YearOfWeek(Expression arg) { + super("year_of_week", arg); + } + + /** + * withChildren. + */ + @Override + public YearOfWeek withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new YearOfWeek(children.get(0)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitYearOfWeek(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java index 30ce433c07..cba019f5b6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java @@ -455,6 +455,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.XxHash64; import org.apache.doris.nereids.trees.expressions.functions.scalar.Year; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor; +import org.apache.doris.nereids.trees.expressions.functions.scalar.YearOfWeek; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearWeek; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff; @@ -2168,6 +2169,10 @@ public interface ScalarFunctionVisitor { return visitScalarFunction(year, context); } + default R visitYearOfWeek(YearOfWeek yearOfWeek, C context) { + return visitScalarFunction(yearOfWeek, context); + } + default R visitYearCeil(YearCeil yearCeil, C context) { return visitScalarFunction(yearCeil, context); } diff --git a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out index 6fba462aff..619bc2c143 100644 --- a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out +++ b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out @@ -415,6 +415,68 @@ February 2019-08-01T13:21:03 2019 9999-08-01T13:21:03 9999 +-- !sql -- +1987 + +-- !sql -- +2049 + +-- !sql -- +0000-08-01T13:21:03 0 +2019-08-01T13:21:03 2019 +9999-08-01T13:21:03 9999 + +-- !sql -- +1987 + +-- !sql -- +2004 + +-- !sql -- +2004 + +-- !sql -- +2005 + +-- !sql -- +2007 + +-- !sql -- +2007 + +-- !sql -- +2008 + +-- !sql -- +2008 + +-- !sql -- +2008 + +-- !sql -- +2009 + +-- !sql -- +2009 + +-- !sql -- +2009 + +-- !sql -- +2009 + +-- !sql -- +2009 + +-- !sql -- +2009 + +-- !sql -- +2009 + +-- !sql -- +2009 + -- !sql -- 202052 diff --git a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy index 8650beb378..346ff5c3e3 100644 --- a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy @@ -475,6 +475,30 @@ suite("test_date_function") { qt_sql """ select year('2050-01-01') """ qt_sql """ select test_datetime, year(test_datetime) from ${tableName} order by test_datetime """ + // YEAROFWEEK + qt_sql """ select year_of_week('1987-01-01') """ + qt_sql """ select year_of_week('2050-01-01') """ + qt_sql """ select test_datetime, year_of_week(test_datetime) from ${tableName} order by test_datetime """ + + qt_sql """ select yow('1987-01-01') """ + + qt_sql "select year_of_week('2005-01-01')" // 2004-W53-6 + qt_sql "select year_of_week('2005-01-02')" // 2004-W53-7 + qt_sql "select year_of_week('2005-12-31')" // 2005-W52-6 + qt_sql "select year_of_week('2007-01-01')" // 2007-W01-1 + qt_sql "select year_of_week('2007-12-30')" // 2007-W52-7 + qt_sql "select year_of_week('2007-12-31')" // 2008-W01-1 + qt_sql "select year_of_week('2008-01-01')" // 2008-W01-2 + qt_sql "select year_of_week('2008-12-28')" // 2008-W52-7 + qt_sql "select year_of_week('2008-12-29')" // 2009-W01-1 + qt_sql "select year_of_week('2008-12-30')" // 2009-W01-2 + qt_sql "select year_of_week('2008-12-31')" // 2009-W01-3 + qt_sql "select year_of_week('2009-01-01')" // 2009-W01-4 + qt_sql "select year_of_week('2009-12-31')" // 2009-W53-4 + qt_sql "select year_of_week('2010-01-01')" // 2009-W53-5 + qt_sql "select year_of_week('2010-01-02')" // 2009-W53-6 + qt_sql "select year_of_week('2010-01-03')" // 2009-W53-7 + // YEARWEEK qt_sql """ select yearweek('2021-1-1') """ qt_sql """ select yearweek('2020-7-1') """