From 4ea836b87e53e23f4fa0d55f30eae1ac4dc636a3 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Fri, 13 Jan 2023 14:00:16 +0000 Subject: [PATCH 1/2] Revert "Revert "update function DAYOFWEEK and add new function WEEKDAY for mysql/spark compatiability"" This reverts commit e37f572c341658c7d6d25b3e9126092a818e6026. --- .../functions/date-time-functions.md | 11 +- src/Common/DateLUTImpl.h | 32 +++++ src/Functions/DateTimeTransforms.h | 16 +-- src/Functions/dateDiff.cpp | 4 +- src/Functions/dateName.cpp | 2 +- src/Functions/formatDateTime.cpp | 8 +- src/Functions/toDayOfWeek.cpp | 5 +- .../0_stateless/00189_time_zones_long.sql | 10 +- ...00921_datetime64_compatibility_long.python | 136 +++++++++--------- ...21_datetime64_compatibility_long.reference | 2 +- .../02521_to_custom_day_of_week.reference | 7 + .../02521_to_custom_day_of_week.sql | 10 ++ 12 files changed, 153 insertions(+), 90 deletions(-) create mode 100644 tests/queries/0_stateless/02521_to_custom_day_of_week.reference create mode 100644 tests/queries/0_stateless/02521_to_custom_day_of_week.sql diff --git a/docs/en/sql-reference/functions/date-time-functions.md b/docs/en/sql-reference/functions/date-time-functions.md index 225f2b162ab..2fb01dfc54b 100644 --- a/docs/en/sql-reference/functions/date-time-functions.md +++ b/docs/en/sql-reference/functions/date-time-functions.md @@ -207,9 +207,16 @@ Converts a date or date with time to a UInt8 number containing the number of the Aliases: `DAYOFMONTH`, `DAY`. -## toDayOfWeek +## toDayOfWeek(date\[,mode\]) -Converts a date or date with time to a UInt8 number containing the number of the day of the week (Monday is 1, and Sunday is 7). +Converts a date or date with time to a UInt8 number containing the number of the day of the week. The two-argument form of toDayOfWeek() enables you to specify whether the week starts on Monday or Sunday, and whether the return value should be in the range from 0 to 6 or from 1-7. If the mode argument is ommited, the default mode is 0. + +| Mode | First day of week | Range | +|------|-------------------|------------------------------------------------| +| 0 | Monday | 1-7, Monday = 1, Tuesday = 2, ..., Sunday = 7 | +| 1 | Monday | 0-6, Monday = 0, Tuesday = 1, ..., Sunday = 6 | +| 2 | Sunday | 0-6, Sunday = 0, Monday = 1, ..., Saturday = 6 | +| 3 | Sunday | 1-7, Sunday = 1, Monday = 2, ..., Saturday = 7 | Alias: `DAYOFWEEK`. diff --git a/src/Common/DateLUTImpl.h b/src/Common/DateLUTImpl.h index 84f063f9555..3d496e088bb 100644 --- a/src/Common/DateLUTImpl.h +++ b/src/Common/DateLUTImpl.h @@ -39,6 +39,15 @@ enum class WeekModeFlag : UInt8 }; using YearWeek = std::pair; +/// Modes for toDayOfWeek() function. +enum class WeekDayMode +{ + WeekStartsMonday1 = 0, + WeekStartsMonday0 = 1, + WeekStartsSunday0 = 2, + WeekStartsSunday1 = 3 +}; + /** Lookup table to conversion of time to date, and to month / year / day of week / day of month and so on. * First time was implemented for OLAPServer, that needed to do billions of such transformations. */ @@ -619,9 +628,25 @@ public: template inline Int16 toYear(DateOrTime v) const { return lut[toLUTIndex(v)].year; } + /// 1-based, starts on Monday template inline UInt8 toDayOfWeek(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_week; } + template + inline UInt8 toDayOfWeek(DateOrTime v, UInt8 week_day_mode) const + { + WeekDayMode mode = check_week_day_mode(week_day_mode); + UInt8 res = toDayOfWeek(v); + + bool start_from_sunday = (mode == WeekDayMode::WeekStartsSunday0 || mode == WeekDayMode::WeekStartsSunday1); + bool zero_based = (mode == WeekDayMode::WeekStartsMonday0 || mode == WeekDayMode::WeekStartsSunday0); + if (start_from_sunday) + res = res % 7 + 1; + if (zero_based) + --res; + return res; + } + template inline UInt8 toDayOfMonth(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_month; } @@ -844,6 +869,13 @@ public: return week_format; } + /// Check and change mode to effective. + inline WeekDayMode check_week_day_mode(UInt8 mode) const /// NOLINT + { + return static_cast(mode & 3); + } + + /** Calculate weekday from d. * Returns 0 for monday, 1 for tuesday... */ diff --git a/src/Functions/DateTimeTransforms.h b/src/Functions/DateTimeTransforms.h index f4163a336ef..56a7a960ac9 100644 --- a/src/Functions/DateTimeTransforms.h +++ b/src/Functions/DateTimeTransforms.h @@ -786,21 +786,21 @@ struct ToDayOfWeekImpl { static constexpr auto name = "toDayOfWeek"; - static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone) + static inline UInt8 execute(Int64 t, UInt8 week_day_mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(t); + return time_zone.toDayOfWeek(t, week_day_mode); } - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + static inline UInt8 execute(UInt32 t, UInt8 week_day_mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(t); + return time_zone.toDayOfWeek(t, week_day_mode); } - static inline UInt8 execute(Int32 d, const DateLUTImpl & time_zone) + static inline UInt8 execute(Int32 d, UInt8 week_day_mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(ExtendedDayNum(d)); + return time_zone.toDayOfWeek(ExtendedDayNum(d), week_day_mode); } - static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) + static inline UInt8 execute(UInt16 d, UInt8 week_day_mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(DayNum(d)); + return time_zone.toDayOfWeek(DayNum(d), week_day_mode); } using FactorTransform = ToMondayImpl; diff --git a/src/Functions/dateDiff.cpp b/src/Functions/dateDiff.cpp index 60668f81edf..d43ef2d4caf 100644 --- a/src/Functions/dateDiff.cpp +++ b/src/Functions/dateDiff.cpp @@ -225,8 +225,8 @@ public: } else if constexpr (std::is_same_v>>) { - auto x_day_of_week = TransformDateTime64(transform_x.getScaleMultiplier()).execute(x, timezone_x); - auto y_day_of_week = TransformDateTime64(transform_y.getScaleMultiplier()).execute(y, timezone_y); + auto x_day_of_week = TransformDateTime64(transform_x.getScaleMultiplier()).execute(x, 0, timezone_x); + auto y_day_of_week = TransformDateTime64(transform_y.getScaleMultiplier()).execute(y, 0, timezone_y); if ((x_day_of_week > y_day_of_week) || ((x_day_of_week == y_day_of_week) && (a_comp.time.hour > b_comp.time.hour)) || ((a_comp.time.hour == b_comp.time.hour) && ((a_comp.time.minute > b_comp.time.minute) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 36c0be49190..bfb190b9a08 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -276,7 +276,7 @@ private: { static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) { - const auto day = ToDayOfWeekImpl::execute(source, timezone); + const auto day = ToDayOfWeekImpl::execute(source, 0, timezone); static constexpr std::string_view day_names[] = { "Monday", diff --git a/src/Functions/formatDateTime.cpp b/src/Functions/formatDateTime.cpp index e7c9a1b5103..c01f32f68ae 100644 --- a/src/Functions/formatDateTime.cpp +++ b/src/Functions/formatDateTime.cpp @@ -344,13 +344,13 @@ private: static size_t mysqlDayOfWeek(char * dest, Time source, UInt64, UInt32, const DateLUTImpl & timezone) { - *dest = '0' + ToDayOfWeekImpl::execute(source, timezone); + *dest = '0' + ToDayOfWeekImpl::execute(source, 0, timezone); return 1; } static size_t mysqlDayOfWeek0To6(char * dest, Time source, UInt64, UInt32, const DateLUTImpl & timezone) { - auto day = ToDayOfWeekImpl::execute(source, timezone); + auto day = ToDayOfWeekImpl::execute(source, 0, timezone); *dest = '0' + (day == 7 ? 0 : day); return 1; } @@ -499,13 +499,13 @@ private: static size_t jodaDayOfWeek1Based(size_t min_represent_digits, char * dest, Time source, UInt64, UInt32, const DateLUTImpl & timezone) { - auto week_day = ToDayOfWeekImpl::execute(source, timezone); + auto week_day = ToDayOfWeekImpl::execute(source, 0, timezone); return writeNumberWithPadding(dest, week_day, min_represent_digits); } static size_t jodaDayOfWeekText(size_t min_represent_digits, char * dest, Time source, UInt64, UInt32, const DateLUTImpl & timezone) { - auto week_day = ToDayOfWeekImpl::execute(source, timezone); + auto week_day = ToDayOfWeekImpl::execute(source, 0, timezone); if (week_day == 7) week_day = 0; diff --git a/src/Functions/toDayOfWeek.cpp b/src/Functions/toDayOfWeek.cpp index 354d4dea894..06343714b9d 100644 --- a/src/Functions/toDayOfWeek.cpp +++ b/src/Functions/toDayOfWeek.cpp @@ -1,13 +1,12 @@ #include #include -#include #include - +#include namespace DB { -using FunctionToDayOfWeek = FunctionDateOrDateTimeToSomething; +using FunctionToDayOfWeek = FunctionCustomWeekToSomething; REGISTER_FUNCTION(ToDayOfWeek) { diff --git a/tests/queries/0_stateless/00189_time_zones_long.sql b/tests/queries/0_stateless/00189_time_zones_long.sql index cf1b9e9ae1d..5760f6c0447 100644 --- a/tests/queries/0_stateless/00189_time_zones_long.sql +++ b/tests/queries/0_stateless/00189_time_zones_long.sql @@ -120,11 +120,11 @@ SELECT toDayOfMonth(toDateTime(1412106600), 'Pacific/Pitcairn'); /* toDayOfWeek */ SELECT 'toDayOfWeek'; -SELECT toDayOfWeek(toDateTime(1412106600), 'Asia/Istanbul'); -SELECT toDayOfWeek(toDateTime(1412106600), 'Europe/Paris'); -SELECT toDayOfWeek(toDateTime(1412106600), 'Europe/London'); -SELECT toDayOfWeek(toDateTime(1412106600), 'Asia/Tokyo'); -SELECT toDayOfWeek(toDateTime(1412106600), 'Pacific/Pitcairn'); +SELECT toDayOfWeek(toDateTime(1412106600), 0, 'Asia/Istanbul'); +SELECT toDayOfWeek(toDateTime(1412106600), 0, 'Europe/Paris'); +SELECT toDayOfWeek(toDateTime(1412106600), 0, 'Europe/London'); +SELECT toDayOfWeek(toDateTime(1412106600), 0, 'Asia/Tokyo'); +SELECT toDayOfWeek(toDateTime(1412106600), 0, 'Pacific/Pitcairn'); /* toHour */ diff --git a/tests/queries/0_stateless/00921_datetime64_compatibility_long.python b/tests/queries/0_stateless/00921_datetime64_compatibility_long.python index e3cd7ee6d36..2706c0f5b12 100644 --- a/tests/queries/0_stateless/00921_datetime64_compatibility_long.python +++ b/tests/queries/0_stateless/00921_datetime64_compatibility_long.python @@ -7,14 +7,14 @@ import sys import argparse # Create SQL statement to verify dateTime64 is accepted as argument to functions taking DateTime. -FUNCTIONS=""" +FUNCTIONS = """ toTimeZone(N, 'UTC') toYear(N, 'Asia/Istanbul') toQuarter(N, 'Asia/Istanbul') toMonth(N, 'Asia/Istanbul') toDayOfYear(N, 'Asia/Istanbul') toDayOfMonth(N, 'Asia/Istanbul') -toDayOfWeek(N, 'Asia/Istanbul') +toDayOfWeek(N, 0, 'Asia/Istanbul') toHour(N, 'Asia/Istanbul') toMinute(N, 'Asia/Istanbul') toSecond(N, 'Asia/Istanbul') @@ -90,68 +90,51 @@ formatDateTime(N, '%C %d %D %e %F %H %I %j %m %M %p %R %S %T %u %V %w %y %Y %%', extra_ops = [ # With same type: ( - ['N {op} N'], + ["N {op} N"], { - 'op': - [ - '- ', # does not work, but should it? - '+ ', # does not work, but should it? - '!=', '==', # equality and inequality supposed to take sub-second part in account - '< ', - '<=', - '> ', - '>=' + "op": [ + "- ", # does not work, but should it? + "+ ", # does not work, but should it? + "!=", + "==", # equality and inequality supposed to take sub-second part in account + "< ", + "<=", + "> ", + ">=", ] - } + }, ), # With other DateTime types: ( - [ - 'N {op} {arg}', - '{arg} {op} N' - ], + ["N {op} {arg}", "{arg} {op} N"], { - 'op': - [ - '-', # does not work, but should it? - '!=', '==', + "op": [ + "-", # does not work, but should it? + "!=", + "==", # these are naturally expected to work, but they don't: - '< ', - '<=', - '> ', - '>=' + "< ", + "<=", + "> ", + ">=", ], - 'arg': ['DT', 'D', 'DT64'], - } + "arg": ["DT", "D", "DT64"], + }, ), # With arithmetic types ( - [ - 'N {op} {arg}', - '{arg} {op} N' - ], + ["N {op} {arg}", "{arg} {op} N"], { - 'op': - [ - '+ ', - '- ', - '==', - '!=', - '< ', - '<=', - '> ', - '>=' - ], - 'arg': - [ - 'toUInt8(1)', - 'toInt8(-1)', - 'toUInt16(1)', - 'toInt16(-1)', - 'toUInt32(1)', - 'toInt32(-1)', - 'toUInt64(1)', - 'toInt64(-1)' + "op": ["+ ", "- ", "==", "!=", "< ", "<=", "> ", ">="], + "arg": [ + "toUInt8(1)", + "toInt8(-1)", + "toUInt16(1)", + "toInt16(-1)", + "toUInt32(1)", + "toInt32(-1)", + "toUInt64(1)", + "toInt64(-1)", ], }, ), @@ -167,14 +150,17 @@ for funcs, args in extra_ops: # filter out empty lines and commented out lines COMMENTED_OUT_LINE_RE = re.compile(r"^\s*#") -FUNCTIONS = list([f for f in FUNCTIONS if len(f) != 0 and COMMENTED_OUT_LINE_RE.match(f) == None]) -TYPES = ['D', 'DT', 'DT64'] +FUNCTIONS = list( + [f for f in FUNCTIONS if len(f) != 0 and COMMENTED_OUT_LINE_RE.match(f) == None] +) +TYPES = ["D", "DT", "DT64"] + def escape_string(s): if sys.version_info[0] > 2: - return s.encode('unicode_escape').decode('utf-8').replace("'", "\\'") + return s.encode("unicode_escape").decode("utf-8").replace("'", "\\'") else: - return s.encode('string-escape').decode('utf-8') + return s.encode("string-escape").decode("utf-8") def execute_functions_for_types(functions, types): @@ -186,18 +172,39 @@ def execute_functions_for_types(functions, types): WITH \ toDateTime64('2019-09-16 19:20:11.234', 3, 'Europe/Minsk') as DT64, \ toDateTime('2019-09-16 19:20:11', 'Europe/Minsk') as DT, \ -toDate('2019-09-16') as D, {X} as N".format(X=dt) - print(("""{prologue} SELECT toTypeName(r), {func} as r FORMAT CSV;""".format(prologue=prologue, func=func))) +toDate('2019-09-16') as D, {X} as N".format( + X=dt + ) + print( + ( + """{prologue} SELECT toTypeName(r), {func} as r FORMAT CSV;""".format( + prologue=prologue, func=func + ) + ) + ) print("""SELECT '------------------------------------------';""") + def main(): def parse_args(): parser = argparse.ArgumentParser() - parser.add_argument('--functions_re', type=re.compile, help="RE to enable functions", default=None) - parser.add_argument('--types_re', - type=lambda s: re.compile('^(' + s + ')$'), - help="RE to enable types, supported types: " + ",".join(TYPES), default=None) - parser.add_argument('--list_functions', action='store_true', help="List all functions to be tested and exit") + parser.add_argument( + "--functions_re", + type=re.compile, + help="RE to enable functions", + default=None, + ) + parser.add_argument( + "--types_re", + type=lambda s: re.compile("^(" + s + ")$"), + help="RE to enable types, supported types: " + ",".join(TYPES), + default=None, + ) + parser.add_argument( + "--list_functions", + action="store_true", + help="List all functions to be tested and exit", + ) return parser.parse_args() args = parse_args() @@ -223,5 +230,6 @@ def main(): execute_functions_for_types(functions, types) -if __name__ == '__main__': + +if __name__ == "__main__": exit(main()) diff --git a/tests/queries/0_stateless/00921_datetime64_compatibility_long.reference b/tests/queries/0_stateless/00921_datetime64_compatibility_long.reference index 8d28a69ff3d..8a168ed0e9e 100644 --- a/tests/queries/0_stateless/00921_datetime64_compatibility_long.reference +++ b/tests/queries/0_stateless/00921_datetime64_compatibility_long.reference @@ -28,7 +28,7 @@ SELECT toDayOfMonth(N, \'Asia/Istanbul\') "UInt8",16 "UInt8",16 ------------------------------------------ -SELECT toDayOfWeek(N, \'Asia/Istanbul\') +SELECT toDayOfWeek(N, 0, \'Asia/Istanbul\') "UInt8",1 "UInt8",1 "UInt8",1 diff --git a/tests/queries/0_stateless/02521_to_custom_day_of_week.reference b/tests/queries/0_stateless/02521_to_custom_day_of_week.reference new file mode 100644 index 00000000000..660dff37b72 --- /dev/null +++ b/tests/queries/0_stateless/02521_to_custom_day_of_week.reference @@ -0,0 +1,7 @@ +1 7 +1 7 +0 6 +1 0 +2 1 +1 7 +0 6 diff --git a/tests/queries/0_stateless/02521_to_custom_day_of_week.sql b/tests/queries/0_stateless/02521_to_custom_day_of_week.sql new file mode 100644 index 00000000000..5475e15a984 --- /dev/null +++ b/tests/queries/0_stateless/02521_to_custom_day_of_week.sql @@ -0,0 +1,10 @@ + +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon), toDayOfWeek(date_sun); +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon, 0), toDayOfWeek(date_sun, 0); +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon, 1), toDayOfWeek(date_sun, 1); +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon, 2), toDayOfWeek(date_sun, 2); +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon, 3), toDayOfWeek(date_sun, 3); +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon, 4), toDayOfWeek(date_sun, 4); +with toDate('2023-01-09') as date_mon, date_mon - 1 as date_sun select toDayOfWeek(date_mon, 5), toDayOfWeek(date_sun, 5); + +select toDayOfWeek(today(), -1); -- { serverError 43 } From bd41c74ddf22d131e712c568447a06cf40381ddc Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Fri, 13 Jan 2023 14:01:13 +0000 Subject: [PATCH 2/2] Various test, code and docs fixups --- .../functions/date-time-functions.md | 50 ++++++++++++++----- src/Common/DateLUTImpl.h | 10 ++-- src/Functions/DateTimeTransforms.h | 16 +++--- src/Functions/IFunctionCustomWeek.h | 12 ++--- tests/performance/date_time_long.xml | 33 ++++++++---- tests/performance/date_time_short.xml | 23 +++++---- 6 files changed, 94 insertions(+), 50 deletions(-) diff --git a/docs/en/sql-reference/functions/date-time-functions.md b/docs/en/sql-reference/functions/date-time-functions.md index 2fb01dfc54b..6a81a0a0c07 100644 --- a/docs/en/sql-reference/functions/date-time-functions.md +++ b/docs/en/sql-reference/functions/date-time-functions.md @@ -207,9 +207,11 @@ Converts a date or date with time to a UInt8 number containing the number of the Aliases: `DAYOFMONTH`, `DAY`. -## toDayOfWeek(date\[,mode\]) +## toDayOfWeek -Converts a date or date with time to a UInt8 number containing the number of the day of the week. The two-argument form of toDayOfWeek() enables you to specify whether the week starts on Monday or Sunday, and whether the return value should be in the range from 0 to 6 or from 1-7. If the mode argument is ommited, the default mode is 0. +Converts a date or date with time to a UInt8 number containing the number of the day of the week. + +The two-argument form of `toDayOfWeek()` enables you to specify whether the week starts on Monday or Sunday, and whether the return value should be in the range from 0 to 6 or 1 to 7. If the mode argument is ommited, the default mode is 0. The time zone of the date can be specified as the third argument. | Mode | First day of week | Range | |------|-------------------|------------------------------------------------| @@ -220,6 +222,12 @@ Converts a date or date with time to a UInt8 number containing the number of the Alias: `DAYOFWEEK`. +**Syntax** + +``` sql +toDayOfWeek(t[, mode[, timezone]]) +``` + ## toHour Converts a date with time to a UInt8 number containing the number of the hour in 24-hour time (0-23). @@ -323,11 +331,17 @@ If `toLastDayOfMonth` is called with an argument of type `Date` greater then 214 Rounds down a date, or date with time, to the nearest Monday. Returns the date. -## toStartOfWeek(t\[,mode\]) +## toStartOfWeek -Rounds down a date, or date with time, to the nearest Sunday or Monday by mode. +Rounds a date or date with time down to the nearest Sunday or Monday. Returns the date. -The mode argument works exactly like the mode argument to toWeek(). For the single-argument syntax, a mode value of 0 is used. +The mode argument works exactly like the mode argument in function `toWeek()`. If no mode is specified, mode is assumed as 0. + +**Syntax** + +``` sql +toStartOfWeek(t[, mode[, timezone]]) +``` ## toStartOfDay @@ -462,10 +476,12 @@ Converts a date, or date with time, to a UInt16 number containing the ISO Year n Converts a date, or date with time, to a UInt8 number containing the ISO Week number. -## toWeek(date\[,mode\]) +## toWeek + +This function returns the week number for date or datetime. The two-argument form of `toWeek()` enables you to specify whether the week starts on Sunday or Monday and whether the return value should be in the range from 0 to 53 or from 1 to 53. If the mode argument is omitted, the default mode is 0. + +`toISOWeek()` is a compatibility function that is equivalent to `toWeek(date,3)`. -This function returns the week number for date or datetime. The two-argument form of toWeek() enables you to specify whether the week starts on Sunday or Monday and whether the return value should be in the range from 0 to 53 or from 1 to 53. If the mode argument is omitted, the default mode is 0. -`toISOWeek()`is a compatibility function that is equivalent to `toWeek(date,3)`. The following table describes how the mode argument works. | Mode | First day of week | Range | Week 1 is the first week … | @@ -489,13 +505,15 @@ For mode values with a meaning of “with 4 or more days this year,” weeks are For mode values with a meaning of “contains January 1”, the week contains January 1 is week 1. It does not matter how many days in the new year the week contained, even if it contained only one day. +**Syntax** + ``` sql -toWeek(date, [, mode][, Timezone]) +toWeek(t[, mode[, time_zone]]) ``` **Arguments** -- `date` – Date or DateTime. +- `t` – Date or DateTime. - `mode` – Optional parameter, Range of values is \[0,9\], default is 0. - `Timezone` – Optional parameter, it behaves like any other conversion function. @@ -511,13 +529,19 @@ SELECT toDate('2016-12-27') AS date, toWeek(date) AS week0, toWeek(date,1) AS we └────────────┴───────┴───────┴───────┘ ``` -## toYearWeek(date\[,mode\]) +## toYearWeek Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year. -The mode argument works exactly like the mode argument to toWeek(). For the single-argument syntax, a mode value of 0 is used. +The mode argument works exactly like the mode argument to `toWeek()`. For the single-argument syntax, a mode value of 0 is used. -`toISOYear()`is a compatibility function that is equivalent to `intDiv(toYearWeek(date,3),100)`. +`toISOYear()` is a compatibility function that is equivalent to `intDiv(toYearWeek(date,3),100)`. + +**Syntax** + +``` sql +toYearWeek(t[, mode[, timezone]]) +``` **Example** diff --git a/src/Common/DateLUTImpl.h b/src/Common/DateLUTImpl.h index 3d496e088bb..b40b4d7c65b 100644 --- a/src/Common/DateLUTImpl.h +++ b/src/Common/DateLUTImpl.h @@ -636,14 +636,17 @@ public: inline UInt8 toDayOfWeek(DateOrTime v, UInt8 week_day_mode) const { WeekDayMode mode = check_week_day_mode(week_day_mode); - UInt8 res = toDayOfWeek(v); - bool start_from_sunday = (mode == WeekDayMode::WeekStartsSunday0 || mode == WeekDayMode::WeekStartsSunday1); - bool zero_based = (mode == WeekDayMode::WeekStartsMonday0 || mode == WeekDayMode::WeekStartsSunday0); + UInt8 res = toDayOfWeek(v); + using enum WeekDayMode; + bool start_from_sunday = (mode == WeekStartsSunday0 || mode == WeekStartsSunday1); + bool zero_based = (mode == WeekStartsMonday0 || mode == WeekStartsSunday0); + if (start_from_sunday) res = res % 7 + 1; if (zero_based) --res; + return res; } @@ -875,7 +878,6 @@ public: return static_cast(mode & 3); } - /** Calculate weekday from d. * Returns 0 for monday, 1 for tuesday... */ diff --git a/src/Functions/DateTimeTransforms.h b/src/Functions/DateTimeTransforms.h index 56a7a960ac9..ffa039f9463 100644 --- a/src/Functions/DateTimeTransforms.h +++ b/src/Functions/DateTimeTransforms.h @@ -786,21 +786,21 @@ struct ToDayOfWeekImpl { static constexpr auto name = "toDayOfWeek"; - static inline UInt8 execute(Int64 t, UInt8 week_day_mode, const DateLUTImpl & time_zone) + static inline UInt8 execute(Int64 t, UInt8 mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(t, week_day_mode); + return time_zone.toDayOfWeek(t, mode); } - static inline UInt8 execute(UInt32 t, UInt8 week_day_mode, const DateLUTImpl & time_zone) + static inline UInt8 execute(UInt32 t, UInt8 mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(t, week_day_mode); + return time_zone.toDayOfWeek(t, mode); } - static inline UInt8 execute(Int32 d, UInt8 week_day_mode, const DateLUTImpl & time_zone) + static inline UInt8 execute(Int32 d, UInt8 mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(ExtendedDayNum(d), week_day_mode); + return time_zone.toDayOfWeek(ExtendedDayNum(d), mode); } - static inline UInt8 execute(UInt16 d, UInt8 week_day_mode, const DateLUTImpl & time_zone) + static inline UInt8 execute(UInt16 d, UInt8 mode, const DateLUTImpl & time_zone) { - return time_zone.toDayOfWeek(DayNum(d), week_day_mode); + return time_zone.toDayOfWeek(DayNum(d), mode); } using FactorTransform = ToMondayImpl; diff --git a/src/Functions/IFunctionCustomWeek.h b/src/Functions/IFunctionCustomWeek.h index e620793ea6c..8f34108dcb4 100644 --- a/src/Functions/IFunctionCustomWeek.h +++ b/src/Functions/IFunctionCustomWeek.h @@ -82,8 +82,8 @@ protected: arguments[0].type->getName(), getName()); if (!isUInt8(arguments[1].type)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal type of 2nd (optional) argument of function {}. Must be constant UInt8 (week mode).", - getName()); + "Illegal type {} of 2nd (optional) argument of function {}. Must be constant UInt8 (week mode).", + arguments[1].type->getName(), getName()); } else if (arguments.size() == 3) { @@ -93,12 +93,12 @@ protected: arguments[0].type->getName(), getName()); if (!isUInt8(arguments[1].type)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal type of 2nd (optional) argument of function {}. Must be constant UInt8 (week mode).", - getName()); + "Illegal type {} of 2nd (optional) argument of function {}. Must be constant UInt8 (week mode).", + arguments[1].type->getName(), getName()); if (!isString(arguments[2].type)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Illegal type of 3rd (optional) argument of function {}. Must be constant string (timezone name).", - getName()); + "Illegal type {} of 3rd (optional) argument of function {}. Must be constant string (timezone name).", + arguments[2].type->getName(), getName()); if ((isDate(arguments[0].type) || isDate32(arguments[0].type)) && is_result_type_date_or_date32) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The timezone argument of function {} is allowed only when the 1st argument is DateTime or DateTime64.", diff --git a/tests/performance/date_time_long.xml b/tests/performance/date_time_long.xml index 240481969a8..0543698ae61 100644 --- a/tests/performance/date_time_long.xml +++ b/tests/performance/date_time_long.xml @@ -6,7 +6,6 @@ toSecond toMinute toHour - toDayOfWeek toDayOfMonth toDayOfYear toMonth @@ -47,21 +46,33 @@ toUnixTimestamp + + datetime_transform_with_mode + + toDayOfWeek + toStartOfWeek + toWeek + toYearWeek + + date_transform - toDayOfWeek toDayOfMonth + toDayOfWeek toDayOfYear + toWeek toMonth toQuarter toYear + toYearWeek toISOWeek toISOYear toDate toMonday toStartOfDay + toStartOfWeek toStartOfMonth toStartOfQuarter toStartOfYear @@ -79,14 +90,6 @@ toRelativeQuarterNum - - time_zone - - UTC - Asia/Istanbul - Asia/Kolkata - - binary_function @@ -116,11 +119,21 @@ subtractYears + + time_zone + + UTC + Asia/Istanbul + Asia/Kolkata + + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, {datetime_transform}(t, '{time_zone}')) + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, {datetime_transform_with_mode}(t, 0, '{time_zone}')) SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDate('2017-01-01') + number % 1000 + rand() % 10 AS t, {date_transform}(t)) SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, {binary_function}(t, 1)) + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, toStartOfInterval(t, INTERVAL 1 month)) SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, date_trunc('month', t)) diff --git a/tests/performance/date_time_short.xml b/tests/performance/date_time_short.xml index de859710670..2bd0598b41a 100644 --- a/tests/performance/date_time_short.xml +++ b/tests/performance/date_time_short.xml @@ -1,15 +1,12 @@ + - date_transform - toDayOfWeek toMonday toRelativeDayNum toYYYYMMDDhhmmss @@ -32,16 +29,24 @@ - + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, {date_transform}(t, '{time_zone}')) SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDate('2017-01-01') + number % 1000 + rand() % 10 AS t, {date_transform}(t)) SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, toUnixTimestamp(t, '{time_zone}')) - + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDate('2017-01-01') + number % 1000 + rand() % 10 AS t, toUnixTimestamp(toUInt16(t))) + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, {binary_function}(t, 1)) + + + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, toStartOfInterval(t, INTERVAL 1 month)) + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, date_trunc('month', t)) + + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDateTime('2017-01-01 00:00:00') + number % 100000000 + rand() % 100000 AS t, toDayOfWeek(t, 0, '{time_zone}')) + SELECT count() FROM numbers(50000000) WHERE NOT ignore(toDate('2017-01-01') + number % 1000 + rand() % 10 AS t, toDayOfWeek(t))