diff --git a/docs/en/sql-reference/functions/date-time-functions.md b/docs/en/sql-reference/functions/date-time-functions.md index cc8469e82ef..8f128f13de0 100644 --- a/docs/en/sql-reference/functions/date-time-functions.md +++ b/docs/en/sql-reference/functions/date-time-functions.md @@ -1290,6 +1290,8 @@ Rounds the time to the half hour. Converts a date or date with time to a UInt32 number containing the year and month number (YYYY \* 100 + MM). Accepts a second optional timezone argument. If provided, the timezone must be a string constant. +This functions is the opposite of function `YYYYMMDDToDate()`. + **Example** ``` sql @@ -1312,8 +1314,7 @@ Converts a date or date with time to a UInt32 number containing the year and mon **Example** ```sql -SELECT - toYYYYMMDD(now(), 'US/Eastern') +SELECT toYYYYMMDD(now(), 'US/Eastern') ``` Result: @@ -1331,8 +1332,7 @@ Converts a date or date with time to a UInt64 number containing the year and mon **Example** ```sql -SELECT - toYYYYMMDDhhmmss(now(), 'US/Eastern') +SELECT toYYYYMMDDhhmmss(now(), 'US/Eastern') ``` Result: @@ -1343,6 +1343,93 @@ Result: └───────────────────────────────────────┘ ``` +## YYYYMMDDToDate + +Converts a number containing the year, month and day number to a [Date](../../sql-reference/data-types/date.md). + +This functions is the opposite of function `toYYYYMMDD()`. + +The output is undefined if the input does not encode a valid Date value. + +**Syntax** + +```sql +YYYYMMDDToDate(yyyymmdd); +``` + +**Arguments** + +- `yyyymmdd` - A number representing the year, month and day. [Integer](../../sql-reference/data-types/int-uint.md), [Float](../../sql-reference/data-types/float.md) or [Decimal](../../sql-reference/data-types/decimal.md). + +**Returned value** + +- a date created from the arguments. + +Type: [Date](../../sql-reference/data-types/date.md). + +**Example** + +```sql +SELECT YYYYMMDDToDate(20230911); +``` + +Result: + +```response +┌─toYYYYMMDD(20230911)─┐ +│ 2023-09-11 │ +└──────────────────────┘ +``` + +## YYYYMMDDToDate32 + +Like function `YYYYMMDDToDate()` but produces a [Date32](../../sql-reference/data-types/date32.md). + +## YYYYMMDDhhmmssToDateTime + +Converts a number containing the year, month and day number to a [DateTime](../../sql-reference/data-types/datetime.md). + +The output is undefined if the input does not encode a valid DateTime value. + +This functions is the opposite of function `toYYYYMMDD()`. + +**Syntax** + +```sql +YYYYMMDDhhmmssToDateTime(yyyymmddhhmmss[, timezone]); +``` + +**Arguments** + +- `yyyymmddhhmmss` - A number representing the year, month and day. [Integer](../../sql-reference/data-types/int-uint.md), [Float](../../sql-reference/data-types/float.md) or [Decimal](../../sql-reference/data-types/decimal.md). +- `timezone` - [Timezone](../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-timezone) for the returned value (optional). + +**Returned value** + +- a date with time created from the arguments. + +Type: [DateTime](../../sql-reference/data-types/datetime.md). + +**Example** + +```sql +SELECT YYYYMMDDToDateTime(20230911131415); +``` + +Result: + +```response +┌────toYYYYMMDDhhmmssToDateTime(20230911131415)─┐ +│ 2023-09-11 13:14:15 │ +└───────────────────────────────────────────────┘ +``` + +## YYYYMMDDhhmmssToDateTime64 + +Like function `YYYYMMDDhhmmssToDate()` but produces a [DateTime64](../../sql-reference/data-types/datetime64.md). + +Accepts an additional, optional `precision` parameter after the `timezone` parameter. + ## addYears, addMonths, addWeeks, addDays, addHours, addMinutes, addSeconds, addQuarters Function adds a Date/DateTime interval to a Date/DateTime and then return the Date/DateTime. For example: diff --git a/src/Common/DateLUTImpl.h b/src/Common/DateLUTImpl.h index 6d0ba718057..0e72b489ace 100644 --- a/src/Common/DateLUTImpl.h +++ b/src/Common/DateLUTImpl.h @@ -71,14 +71,14 @@ private: // Same as above but select different function overloads for zero saturation. STRONG_TYPEDEF(UInt32, LUTIndexWithSaturation) - static inline LUTIndex normalizeLUTIndex(UInt32 index) + static LUTIndex normalizeLUTIndex(UInt32 index) { if (index >= DATE_LUT_SIZE) return LUTIndex(DATE_LUT_SIZE - 1); return LUTIndex{index}; } - static inline LUTIndex normalizeLUTIndex(Int64 index) + static LUTIndex normalizeLUTIndex(Int64 index) { if (unlikely(index < 0)) return LUTIndex(0); @@ -88,59 +88,59 @@ private: } template - friend inline LUTIndex operator+(const LUTIndex & index, const T v) + friend LUTIndex operator+(const LUTIndex & index, const T v) { return normalizeLUTIndex(index.toUnderType() + UInt32(v)); } template - friend inline LUTIndex operator+(const T v, const LUTIndex & index) + friend LUTIndex operator+(const T v, const LUTIndex & index) { return normalizeLUTIndex(static_cast(v + index.toUnderType())); } - friend inline LUTIndex operator+(const LUTIndex & index, const LUTIndex & v) + friend LUTIndex operator+(const LUTIndex & index, const LUTIndex & v) { return normalizeLUTIndex(static_cast(index.toUnderType() + v.toUnderType())); } template - friend inline LUTIndex operator-(const LUTIndex & index, const T v) + friend LUTIndex operator-(const LUTIndex & index, const T v) { return normalizeLUTIndex(static_cast(index.toUnderType() - UInt32(v))); } template - friend inline LUTIndex operator-(const T v, const LUTIndex & index) + friend LUTIndex operator-(const T v, const LUTIndex & index) { return normalizeLUTIndex(static_cast(v - index.toUnderType())); } - friend inline LUTIndex operator-(const LUTIndex & index, const LUTIndex & v) + friend LUTIndex operator-(const LUTIndex & index, const LUTIndex & v) { return normalizeLUTIndex(static_cast(index.toUnderType() - v.toUnderType())); } template - friend inline LUTIndex operator*(const LUTIndex & index, const T v) + friend LUTIndex operator*(const LUTIndex & index, const T v) { return normalizeLUTIndex(index.toUnderType() * UInt32(v)); } template - friend inline LUTIndex operator*(const T v, const LUTIndex & index) + friend LUTIndex operator*(const T v, const LUTIndex & index) { return normalizeLUTIndex(v * index.toUnderType()); } template - friend inline LUTIndex operator/(const LUTIndex & index, const T v) + friend LUTIndex operator/(const LUTIndex & index, const T v) { return normalizeLUTIndex(index.toUnderType() / UInt32(v)); } template - friend inline LUTIndex operator/(const T v, const LUTIndex & index) + friend LUTIndex operator/(const T v, const LUTIndex & index) { return normalizeLUTIndex(UInt32(v) / index.toUnderType()); } @@ -172,12 +172,12 @@ public: Int8 amount_of_offset_change_value; /// Usually -4 or 4, but look at Lord Howe Island. Multiply by OffsetChangeFactor UInt8 time_at_offset_change_value; /// In seconds from beginning of the day. Multiply by OffsetChangeFactor - inline Int32 amount_of_offset_change() const /// NOLINT + Int32 amount_of_offset_change() const /// NOLINT { return static_cast(amount_of_offset_change_value) * OffsetChangeFactor; } - inline UInt32 time_at_offset_change() const /// NOLINT + UInt32 time_at_offset_change() const /// NOLINT { return static_cast(time_at_offset_change_value) * OffsetChangeFactor; } @@ -221,7 +221,7 @@ private: /// Time zone name. std::string time_zone; - inline LUTIndex findIndex(Time t) const + LUTIndex findIndex(Time t) const { /// First guess. Time guess = (t / 86400) + daynum_offset_epoch; @@ -248,34 +248,34 @@ private: return LUTIndex(guess ? static_cast(guess) - 1 : 0); } - static inline LUTIndex toLUTIndex(DayNum d) + static LUTIndex toLUTIndex(DayNum d) { return normalizeLUTIndex(d + daynum_offset_epoch); } - static inline LUTIndex toLUTIndex(ExtendedDayNum d) + static LUTIndex toLUTIndex(ExtendedDayNum d) { return normalizeLUTIndex(static_cast(d + daynum_offset_epoch)); } - inline LUTIndex toLUTIndex(Time t) const + LUTIndex toLUTIndex(Time t) const { return findIndex(t); } - static inline LUTIndex toLUTIndex(LUTIndex i) + static LUTIndex toLUTIndex(LUTIndex i) { return i; } template - inline const Values & find(DateOrTime v) const + const Values & find(DateOrTime v) const { return lut[toLUTIndex(v)]; } template - inline DateOrTime roundDown(DateOrTime x, Divisor divisor) const + DateOrTime roundDown(DateOrTime x, Divisor divisor) const { static_assert(std::is_integral_v && std::is_integral_v); assert(divisor > 0); @@ -336,7 +336,7 @@ public: } template - inline auto toDayNum(DateOrTime v) const + auto toDayNum(DateOrTime v) const { if constexpr (std::is_unsigned_v || std::is_same_v) return DayNum{static_cast(saturateMinus(toLUTIndex(v).toUnderType(), daynum_offset_epoch))}; @@ -346,7 +346,7 @@ public: /// Round down to start of monday. template - inline Time toFirstDayOfWeek(DateOrTime v) const + Time toFirstDayOfWeek(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -356,7 +356,7 @@ public: } template - inline auto toFirstDayNumOfWeek(DateOrTime v) const + auto toFirstDayNumOfWeek(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -367,7 +367,7 @@ public: /// Round up to the last day of week. template - inline Time toLastDayOfWeek(DateOrTime v) const + Time toLastDayOfWeek(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -377,7 +377,7 @@ public: } template - inline auto toLastDayNumOfWeek(DateOrTime v) const + auto toLastDayNumOfWeek(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -388,7 +388,7 @@ public: /// Round down to start of month. template - inline Time toFirstDayOfMonth(DateOrTime v) const + Time toFirstDayOfMonth(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -398,7 +398,7 @@ public: } template - inline auto toFirstDayNumOfMonth(DateOrTime v) const + auto toFirstDayNumOfMonth(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -409,7 +409,7 @@ public: /// Round up to last day of month. template - inline Time toLastDayOfMonth(DateOrTime v) const + Time toLastDayOfMonth(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -419,7 +419,7 @@ public: } template - inline auto toLastDayNumOfMonth(DateOrTime v) const + auto toLastDayNumOfMonth(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); if constexpr (std::is_unsigned_v || std::is_same_v) @@ -430,7 +430,7 @@ public: /// Round down to start of quarter. template - inline auto toFirstDayNumOfQuarter(DateOrTime v) const + auto toFirstDayNumOfQuarter(DateOrTime v) const { if constexpr (std::is_unsigned_v || std::is_same_v) return toDayNum(LUTIndexWithSaturation(toFirstDayOfQuarterIndex(v))); @@ -439,7 +439,7 @@ public: } template - inline LUTIndex toFirstDayOfQuarterIndex(DateOrTime v) const + LUTIndex toFirstDayOfQuarterIndex(DateOrTime v) const { LUTIndex index = toLUTIndex(v); size_t month_inside_quarter = (lut[index].month - 1) % 3; @@ -455,25 +455,25 @@ public: } template - inline Time toFirstDayOfQuarter(DateOrTime v) const + Time toFirstDayOfQuarter(DateOrTime v) const { return toDate(toFirstDayOfQuarterIndex(v)); } /// Round down to start of year. - inline Time toFirstDayOfYear(Time t) const + Time toFirstDayOfYear(Time t) const { return lut[years_lut[lut[findIndex(t)].year - DATE_LUT_MIN_YEAR]].date; } template - inline LUTIndex toFirstDayNumOfYearIndex(DateOrTime v) const + LUTIndex toFirstDayNumOfYearIndex(DateOrTime v) const { return years_lut[lut[toLUTIndex(v)].year - DATE_LUT_MIN_YEAR]; } template - inline auto toFirstDayNumOfYear(DateOrTime v) const + auto toFirstDayNumOfYear(DateOrTime v) const { if constexpr (std::is_unsigned_v || std::is_same_v) return toDayNum(LUTIndexWithSaturation(toFirstDayNumOfYearIndex(v))); @@ -481,14 +481,14 @@ public: return toDayNum(LUTIndex(toFirstDayNumOfYearIndex(v))); } - inline Time toFirstDayOfNextMonth(Time t) const + Time toFirstDayOfNextMonth(Time t) const { LUTIndex index = findIndex(t); index += 32 - lut[index].day_of_month; return lut[index - (lut[index].day_of_month - 1)].date; } - inline Time toFirstDayOfPrevMonth(Time t) const + Time toFirstDayOfPrevMonth(Time t) const { LUTIndex index = findIndex(t); index -= lut[index].day_of_month; @@ -496,13 +496,13 @@ public: } template - inline UInt8 daysInMonth(DateOrTime value) const + UInt8 daysInMonth(DateOrTime value) const { const LUTIndex i = toLUTIndex(value); return lut[i].days_in_month; } - inline UInt8 daysInMonth(Int16 year, UInt8 month) const + UInt8 daysInMonth(Int16 year, UInt8 month) const { UInt16 idx = year - DATE_LUT_MIN_YEAR; if (unlikely(idx >= DATE_LUT_YEARS)) @@ -515,12 +515,12 @@ public: /** Round to start of day, then shift for specified amount of days. */ - inline Time toDateAndShift(Time t, Int32 days) const + Time toDateAndShift(Time t, Int32 days) const { return lut[findIndex(t) + days].date; } - inline Time toTime(Time t) const + Time toTime(Time t) const { const LUTIndex index = findIndex(t); @@ -532,7 +532,7 @@ public: return res - offset_at_start_of_epoch; /// Starting at 1970-01-01 00:00:00 local time. } - inline unsigned toHour(Time t) const + unsigned toHour(Time t) const { const LUTIndex index = findIndex(t); @@ -552,7 +552,7 @@ public: * then subtract the former from the latter to get the offset result. * The boundaries when meets DST(daylight saving time) change should be handled very carefully. */ - inline Time timezoneOffset(Time t) const + Time timezoneOffset(Time t) const { const LUTIndex index = findIndex(t); @@ -574,7 +574,7 @@ public: } - inline unsigned toSecond(Time t) const + unsigned toSecond(Time t) const { if (likely(offset_is_whole_number_of_minutes_during_epoch)) { @@ -593,7 +593,7 @@ public: return time % 60; } - inline unsigned toMinute(Time t) const + unsigned toMinute(Time t) const { if (t >= 0 && offset_is_whole_number_of_hours_during_epoch) return (t / 60) % 60; @@ -630,11 +630,11 @@ public: * because the same calendar day starts/ends at different timestamps in different time zones) */ - inline Time fromDayNum(DayNum d) const { return lut_saturated[toLUTIndex(d)].date; } - inline Time fromDayNum(ExtendedDayNum d) const { return lut[toLUTIndex(d)].date; } + Time fromDayNum(DayNum d) const { return lut_saturated[toLUTIndex(d)].date; } + Time fromDayNum(ExtendedDayNum d) const { return lut[toLUTIndex(d)].date; } template - inline Time toDate(DateOrTime v) const + Time toDate(DateOrTime v) const { if constexpr (std::is_unsigned_v || std::is_same_v) return lut_saturated[toLUTIndex(v)].date; @@ -643,20 +643,20 @@ public: } template - inline UInt8 toMonth(DateOrTime v) const { return lut[toLUTIndex(v)].month; } + UInt8 toMonth(DateOrTime v) const { return lut[toLUTIndex(v)].month; } template - inline UInt8 toQuarter(DateOrTime v) const { return (lut[toLUTIndex(v)].month - 1) / 3 + 1; } + UInt8 toQuarter(DateOrTime v) const { return (lut[toLUTIndex(v)].month - 1) / 3 + 1; } template - inline Int16 toYear(DateOrTime v) const { return lut[toLUTIndex(v)].year; } + 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; } + UInt8 toDayOfWeek(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_week; } template - inline UInt8 toDayOfWeek(DateOrTime v, UInt8 week_day_mode) const + UInt8 toDayOfWeek(DateOrTime v, UInt8 week_day_mode) const { WeekDayMode mode = check_week_day_mode(week_day_mode); @@ -674,10 +674,10 @@ public: } template - inline UInt8 toDayOfMonth(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_month; } + UInt8 toDayOfMonth(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_month; } template - inline UInt16 toDayOfYear(DateOrTime v) const + UInt16 toDayOfYear(DateOrTime v) const { // TODO: different overload for ExtendedDayNum const LUTIndex i = toLUTIndex(v); @@ -688,7 +688,7 @@ public: /// (round down to monday and divide DayNum by 7; we made an assumption, /// that in domain of the function there was no weeks with any other number of days than 7) template - inline Int32 toRelativeWeekNum(DateOrTime v) const + Int32 toRelativeWeekNum(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); /// We add 8 to avoid underflow at beginning of unix epoch. @@ -697,7 +697,7 @@ public: /// Get year that contains most of the current week. Week begins at monday. template - inline Int16 toISOYear(DateOrTime v) const + Int16 toISOYear(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); /// That's effectively the year of thursday of current week. @@ -708,7 +708,7 @@ public: /// Example: ISO year 2019 begins at 2018-12-31. And ISO year 2017 begins at 2017-01-02. /// https://en.wikipedia.org/wiki/ISO_week_date template - inline LUTIndex toFirstDayNumOfISOYearIndex(DateOrTime v) const + LUTIndex toFirstDayNumOfISOYearIndex(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); auto iso_year = toISOYear(i); @@ -722,7 +722,7 @@ public: } template - inline auto toFirstDayNumOfISOYear(DateOrTime v) const + auto toFirstDayNumOfISOYear(DateOrTime v) const { if constexpr (std::is_unsigned_v || std::is_same_v) return toDayNum(LUTIndexWithSaturation(toFirstDayNumOfISOYearIndex(v))); @@ -730,7 +730,7 @@ public: return toDayNum(LUTIndex(toFirstDayNumOfISOYearIndex(v))); } - inline Time toFirstDayOfISOYear(Time t) const + Time toFirstDayOfISOYear(Time t) const { return lut[toFirstDayNumOfISOYearIndex(t)].date; } @@ -738,7 +738,7 @@ public: /// ISO 8601 week number. Week begins at monday. /// The week number 1 is the first week in year that contains 4 or more days (that's more than half). template - inline UInt8 toISOWeek(DateOrTime v) const + UInt8 toISOWeek(DateOrTime v) const { return 1 + (toFirstDayNumOfWeek(v) - toDayNum(toFirstDayNumOfISOYearIndex(v))) / 7; } @@ -777,7 +777,7 @@ public: next week is week 1. */ template - inline YearWeek toYearWeek(DateOrTime v, UInt8 week_mode) const + YearWeek toYearWeek(DateOrTime v, UInt8 week_mode) const { const bool newyear_day_mode = week_mode & static_cast(WeekModeFlag::NEWYEAR_DAY); week_mode = check_week_mode(week_mode); @@ -836,7 +836,7 @@ public: /// Calculate week number of WeekModeFlag::NEWYEAR_DAY mode /// The week number 1 is the first week in year that contains January 1, template - inline YearWeek toYearWeekOfNewyearMode(DateOrTime v, bool monday_first_mode) const + YearWeek toYearWeekOfNewyearMode(DateOrTime v, bool monday_first_mode) const { YearWeek yw(0, 0); UInt16 offset_day = monday_first_mode ? 0U : 1U; @@ -870,7 +870,7 @@ public: /// Get first day of week with week_mode, return Sunday or Monday template - inline auto toFirstDayNumOfWeek(DateOrTime v, UInt8 week_mode) const + auto toFirstDayNumOfWeek(DateOrTime v, UInt8 week_mode) const { bool monday_first_mode = week_mode & static_cast(WeekModeFlag::MONDAY_FIRST); if (monday_first_mode) @@ -889,7 +889,7 @@ public: /// Get last day of week with week_mode, return Saturday or Sunday template - inline auto toLastDayNumOfWeek(DateOrTime v, UInt8 week_mode) const + auto toLastDayNumOfWeek(DateOrTime v, UInt8 week_mode) const { bool monday_first_mode = week_mode & static_cast(WeekModeFlag::MONDAY_FIRST); if (monday_first_mode) @@ -908,7 +908,7 @@ public: } /// Check and change mode to effective. - inline UInt8 check_week_mode(UInt8 mode) const /// NOLINT + UInt8 check_week_mode(UInt8 mode) const /// NOLINT { UInt8 week_format = (mode & 7); if (!(week_format & static_cast(WeekModeFlag::MONDAY_FIRST))) @@ -917,7 +917,7 @@ public: } /// Check and change mode to effective. - inline WeekDayMode check_week_day_mode(UInt8 mode) const /// NOLINT + WeekDayMode check_week_day_mode(UInt8 mode) const /// NOLINT { return static_cast(mode & 3); } @@ -926,7 +926,7 @@ public: * Returns 0 for monday, 1 for tuesday... */ template - inline UInt8 calc_weekday(DateOrTime v, bool sunday_first_day_of_week) const /// NOLINT + UInt8 calc_weekday(DateOrTime v, bool sunday_first_day_of_week) const /// NOLINT { const LUTIndex i = toLUTIndex(v); if (!sunday_first_day_of_week) @@ -936,28 +936,28 @@ public: } /// Calculate days in one year. - inline UInt16 calc_days_in_year(Int32 year) const /// NOLINT + UInt16 calc_days_in_year(Int32 year) const /// NOLINT { return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366 : 365); } /// Number of month from some fixed moment in the past (year * 12 + month) template - inline Int32 toRelativeMonthNum(DateOrTime v) const + Int32 toRelativeMonthNum(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); return lut[i].year * 12 + lut[i].month; } template - inline Int32 toRelativeQuarterNum(DateOrTime v) const + Int32 toRelativeQuarterNum(DateOrTime v) const { const LUTIndex i = toLUTIndex(v); return lut[i].year * 4 + (lut[i].month - 1) / 3; } /// We count all hour-length intervals, unrelated to offset changes. - inline Time toRelativeHourNum(Time t) const + Time toRelativeHourNum(Time t) const { if (t >= 0 && offset_is_whole_number_of_hours_during_epoch) return t / 3600; @@ -968,37 +968,37 @@ public: } template - inline Time toRelativeHourNum(DateOrTime v) const + Time toRelativeHourNum(DateOrTime v) const { return toRelativeHourNum(lut[toLUTIndex(v)].date); } /// The same formula is used for positive time (after Unix epoch) and negative time (before Unix epoch). /// It’s needed for correct work of dateDiff function. - inline Time toStableRelativeHourNum(Time t) const + Time toStableRelativeHourNum(Time t) const { return (t + DATE_LUT_ADD + 86400 - offset_at_start_of_epoch) / 3600 - (DATE_LUT_ADD / 3600); } template - inline Time toStableRelativeHourNum(DateOrTime v) const + Time toStableRelativeHourNum(DateOrTime v) const { return toStableRelativeHourNum(lut[toLUTIndex(v)].date); } - inline Time toRelativeMinuteNum(Time t) const /// NOLINT + Time toRelativeMinuteNum(Time t) const /// NOLINT { return (t + DATE_LUT_ADD) / 60 - (DATE_LUT_ADD / 60); } template - inline Time toRelativeMinuteNum(DateOrTime v) const + Time toRelativeMinuteNum(DateOrTime v) const { return toRelativeMinuteNum(lut[toLUTIndex(v)].date); } template - inline auto toStartOfYearInterval(DateOrTime v, UInt64 years) const + auto toStartOfYearInterval(DateOrTime v, UInt64 years) const { if (years == 1) return toFirstDayNumOfYear(v); @@ -1019,7 +1019,7 @@ public: template requires std::is_same_v || std::is_same_v - inline auto toStartOfQuarterInterval(Date d, UInt64 quarters) const + auto toStartOfQuarterInterval(Date d, UInt64 quarters) const { if (quarters == 1) return toFirstDayNumOfQuarter(d); @@ -1028,7 +1028,7 @@ public: template requires std::is_same_v || std::is_same_v - inline auto toStartOfMonthInterval(Date d, UInt64 months) const + auto toStartOfMonthInterval(Date d, UInt64 months) const { if (months == 1) return toFirstDayNumOfMonth(d); @@ -1042,7 +1042,7 @@ public: template requires std::is_same_v || std::is_same_v - inline auto toStartOfWeekInterval(Date d, UInt64 weeks) const + auto toStartOfWeekInterval(Date d, UInt64 weeks) const { if (weeks == 1) return toFirstDayNumOfWeek(d); @@ -1056,7 +1056,7 @@ public: template requires std::is_same_v || std::is_same_v - inline Time toStartOfDayInterval(Date d, UInt64 days) const + Time toStartOfDayInterval(Date d, UInt64 days) const { if (days == 1) return toDate(d); @@ -1152,7 +1152,7 @@ public: return static_cast(roundDown(t, seconds)); } - inline LUTIndex makeLUTIndex(Int16 year, UInt8 month, UInt8 day_of_month) const + LUTIndex makeLUTIndex(Int16 year, UInt8 month, UInt8 day_of_month) const { if (unlikely(year < DATE_LUT_MIN_YEAR || month < 1 || month > 12 || day_of_month < 1 || day_of_month > 31)) return LUTIndex(0); @@ -1167,7 +1167,7 @@ public: } /// Create DayNum from year, month, day of month. - inline ExtendedDayNum makeDayNum(Int16 year, UInt8 month, UInt8 day_of_month, Int32 default_error_day_num = 0) const + ExtendedDayNum makeDayNum(Int16 year, UInt8 month, UInt8 day_of_month, Int32 default_error_day_num = 0) const { if (unlikely(year < DATE_LUT_MIN_YEAR || month < 1 || month > 12 || day_of_month < 1 || day_of_month > 31)) return ExtendedDayNum(default_error_day_num); @@ -1175,14 +1175,14 @@ public: return toDayNum(makeLUTIndex(year, month, day_of_month)); } - inline Time makeDate(Int16 year, UInt8 month, UInt8 day_of_month) const + Time makeDate(Int16 year, UInt8 month, UInt8 day_of_month) const { return lut[makeLUTIndex(year, month, day_of_month)].date; } /** Does not accept daylight saving time as argument: in case of ambiguity, it choose greater timestamp. */ - inline Time makeDateTime(Int16 year, UInt8 month, UInt8 day_of_month, UInt8 hour, UInt8 minute, UInt8 second) const + Time makeDateTime(Int16 year, UInt8 month, UInt8 day_of_month, UInt8 hour, UInt8 minute, UInt8 second) const { size_t index = makeLUTIndex(year, month, day_of_month); Time time_offset = hour * 3600 + minute * 60 + second; @@ -1194,28 +1194,28 @@ public: } template - inline const Values & getValues(DateOrTime v) const { return lut[toLUTIndex(v)]; } + const Values & getValues(DateOrTime v) const { return lut[toLUTIndex(v)]; } template - inline UInt32 toNumYYYYMM(DateOrTime v) const + UInt32 toNumYYYYMM(DateOrTime v) const { const Values & values = getValues(v); return values.year * 100 + values.month; } template - inline UInt32 toNumYYYYMMDD(DateOrTime v) const + UInt32 toNumYYYYMMDD(DateOrTime v) const { const Values & values = getValues(v); return values.year * 10000 + values.month * 100 + values.day_of_month; } - inline Time YYYYMMDDToDate(UInt32 num) const /// NOLINT + Time YYYYMMDDToDate(UInt32 num) const /// NOLINT { return makeDate(num / 10000, num / 100 % 100, num % 100); } - inline ExtendedDayNum YYYYMMDDToDayNum(UInt32 num) const /// NOLINT + ExtendedDayNum YYYYMMDDToDayNum(UInt32 num) const /// NOLINT { return makeDayNum(num / 10000, num / 100 % 100, num % 100); } @@ -1241,13 +1241,13 @@ public: TimeComponents time; }; - inline DateComponents toDateComponents(Time t) const + DateComponents toDateComponents(Time t) const { const Values & values = getValues(t); return { values.year, values.month, values.day_of_month }; } - inline DateTimeComponents toDateTimeComponents(Time t) const + DateTimeComponents toDateTimeComponents(Time t) const { const LUTIndex index = findIndex(t); const Values & values = lut[index]; @@ -1283,12 +1283,12 @@ public: } template - inline DateTimeComponents toDateTimeComponents(DateOrTime v) const + DateTimeComponents toDateTimeComponents(DateOrTime v) const { return toDateTimeComponents(lut[toLUTIndex(v)].date); } - inline UInt64 toNumYYYYMMDDhhmmss(Time t) const + UInt64 toNumYYYYMMDDhhmmss(Time t) const { DateTimeComponents components = toDateTimeComponents(t); @@ -1301,7 +1301,7 @@ public: + UInt64(components.date.year) * 10000000000; } - inline Time YYYYMMDDhhmmssToTime(UInt64 num) const /// NOLINT + Time YYYYMMDDhhmmssToTime(UInt64 num) const /// NOLINT { return makeDateTime( num / 10000000000, @@ -1315,7 +1315,7 @@ public: /// Adding calendar intervals. /// Implementation specific behaviour when delta is too big. - inline NO_SANITIZE_UNDEFINED Time addDays(Time t, Int64 delta) const + NO_SANITIZE_UNDEFINED Time addDays(Time t, Int64 delta) const { const LUTIndex index = findIndex(t); const Values & values = lut[index]; @@ -1332,12 +1332,12 @@ public: return lut[new_index].date + time; } - inline NO_SANITIZE_UNDEFINED Time addWeeks(Time t, Int64 delta) const + NO_SANITIZE_UNDEFINED Time addWeeks(Time t, Int64 delta) const { return addDays(t, delta * 7); } - inline UInt8 saturateDayOfMonth(Int16 year, UInt8 month, UInt8 day_of_month) const + UInt8 saturateDayOfMonth(Int16 year, UInt8 month, UInt8 day_of_month) const { if (likely(day_of_month <= 28)) return day_of_month; @@ -1351,7 +1351,7 @@ public: } template - inline LUTIndex NO_SANITIZE_UNDEFINED addMonthsIndex(DateOrTime v, Int64 delta) const + LUTIndex NO_SANITIZE_UNDEFINED addMonthsIndex(DateOrTime v, Int64 delta) const { const Values & values = lut[toLUTIndex(v)]; @@ -1375,11 +1375,11 @@ public: } } - /// If resulting month has less deys than source month, then saturation can happen. + /// If resulting month has less days than source month, then saturation can happen. /// Example: 31 Aug + 1 month = 30 Sep. template requires std::is_same_v || std::is_same_v || std::is_same_v - inline Time NO_SANITIZE_UNDEFINED addMonths(DateTime t, Int64 delta) const + Time NO_SANITIZE_UNDEFINED addMonths(DateTime t, Int64 delta) const { const auto result_day = addMonthsIndex(t, delta); @@ -1405,7 +1405,7 @@ public: template requires std::is_same_v || std::is_same_v - inline auto NO_SANITIZE_UNDEFINED addMonths(Date d, Int64 delta) const + auto NO_SANITIZE_UNDEFINED addMonths(Date d, Int64 delta) const { if constexpr (std::is_same_v) return toDayNum(LUTIndexWithSaturation(addMonthsIndex(d, delta))); @@ -1414,13 +1414,13 @@ public: } template - inline auto NO_SANITIZE_UNDEFINED addQuarters(DateOrTime d, Int64 delta) const + auto NO_SANITIZE_UNDEFINED addQuarters(DateOrTime d, Int64 delta) const { return addMonths(d, delta * 3); } template - inline LUTIndex NO_SANITIZE_UNDEFINED addYearsIndex(DateOrTime v, Int64 delta) const + LUTIndex NO_SANITIZE_UNDEFINED addYearsIndex(DateOrTime v, Int64 delta) const { const Values & values = lut[toLUTIndex(v)]; @@ -1438,7 +1438,7 @@ public: /// Saturation can occur if 29 Feb is mapped to non-leap year. template requires std::is_same_v || std::is_same_v || std::is_same_v - inline Time addYears(DateTime t, Int64 delta) const + Time addYears(DateTime t, Int64 delta) const { auto result_day = addYearsIndex(t, delta); @@ -1464,7 +1464,7 @@ public: template requires std::is_same_v || std::is_same_v - inline auto addYears(Date d, Int64 delta) const + auto addYears(Date d, Int64 delta) const { if constexpr (std::is_same_v) return toDayNum(LUTIndexWithSaturation(addYearsIndex(d, delta))); @@ -1473,7 +1473,7 @@ public: } - inline std::string timeToString(Time t) const + std::string timeToString(Time t) const { DateTimeComponents components = toDateTimeComponents(t); @@ -1498,7 +1498,7 @@ public: return s; } - inline std::string dateToString(Time t) const + std::string dateToString(Time t) const { const Values & values = getValues(t); @@ -1516,7 +1516,7 @@ public: return s; } - inline std::string dateToString(ExtendedDayNum d) const + std::string dateToString(ExtendedDayNum d) const { const Values & values = getValues(d); diff --git a/src/Functions/makeDate.cpp b/src/Functions/makeDate.cpp index 1e4f3604c94..bb8c302d139 100644 --- a/src/Functions/makeDate.cpp +++ b/src/Functions/makeDate.cpp @@ -28,7 +28,9 @@ namespace ErrorCodes namespace { -/// Functions common to makeDate, makeDate32, makeDateTime, makeDateTime64 +/// Functionality common to +/// - makeDate, makeDate32, makeDateTime, makeDateTime64, +/// - YYYYMMDDToDate, YYYYMMDDToDate32, YYYYMMDDhhmmssToDateTime, YYYYMMDDhhmmssToDateTime64 class FunctionWithNumericParamsBase : public IFunction { public: @@ -48,11 +50,11 @@ public: size_t getNumberOfArguments() const override { return 0; } protected: - template + template Columns convertMandatoryArguments(const ColumnsWithTypeAndName & arguments, const ArgumentNames & argument_names) const { Columns converted_arguments; - const DataTypePtr converted_argument_type = std::make_shared(); + const DataTypePtr converted_argument_type = std::make_shared(); for (size_t i = 0; i < argument_names.size(); ++i) { ColumnPtr argument_column = castColumn(arguments[i], converted_argument_type); @@ -63,7 +65,7 @@ protected: } }; -/// Common implementation for makeDate, makeDate32 +/// Implementation of makeDate, makeDate32 template class FunctionMakeDate : public FunctionWithNumericParamsBase { @@ -72,7 +74,7 @@ private: static constexpr std::array mandatory_argument_names_year_dayofyear = {"year", "dayofyear"}; public: - static constexpr auto name = Traits::name; + static constexpr auto name = Traits::makeDateName; static FunctionPtr create(ContextPtr) { return std::make_shared(); } @@ -80,9 +82,9 @@ public: DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { - const bool isYearMonthDayVariant = (arguments.size() == 3); + const bool is_year_month_variant = (arguments.size() == 3); - if (isYearMonthDayVariant) + if (is_year_month_variant) { FunctionArgumentDescriptors args{ {mandatory_argument_names_year_month_day[0], &isNumber, nullptr, "Number"}, @@ -105,10 +107,10 @@ public: ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { - const bool isYearMonthDayVariant = (arguments.size() == 3); + const bool is_year_month_day_variant = (arguments.size() == 3); Columns converted_arguments; - if (isYearMonthDayVariant) + if (is_year_month_day_variant) converted_arguments = convertMandatoryArguments(arguments, mandatory_argument_names_year_month_day); else converted_arguments = convertMandatoryArguments(arguments, mandatory_argument_names_year_dayofyear); @@ -119,7 +121,7 @@ public: const auto & date_lut = DateLUT::instance(); const Int32 max_days_since_epoch = date_lut.makeDayNum(Traits::MAX_DATE[0], Traits::MAX_DATE[1], Traits::MAX_DATE[2]); - if (isYearMonthDayVariant) + if (is_year_month_day_variant) { const auto & year_data = typeid_cast(*converted_arguments[0]).getData(); const auto & month_data = typeid_cast(*converted_arguments[1]).getData(); @@ -133,8 +135,7 @@ public: Int32 day_num = 0; - if (year >= Traits::MIN_YEAR && - year <= Traits::MAX_YEAR && + if (year >= Traits::MIN_YEAR && year <= Traits::MAX_YEAR && month >= 1 && month <= 12 && day >= 1 && day <= 31) { @@ -158,8 +159,7 @@ public: Int32 day_num = 0; - if (year >= Traits::MIN_YEAR && - year <= Traits::MAX_YEAR && + if (year >= Traits::MIN_YEAR && year <= Traits::MAX_YEAR && dayofyear >= 1 && dayofyear <= 365) { Int32 days_since_epoch = date_lut.makeDayNum(static_cast(year), 1, 1) + static_cast(dayofyear) - 1; @@ -175,20 +175,87 @@ public: } }; -struct MakeDateTraits +/// Implementation of YYYYMMDDToDate, YYYYMMDDToDate32 +template +class FunctionYYYYYMMDDToDate : public FunctionWithNumericParamsBase { - static constexpr auto name = "makeDate"; +private: + static constexpr std::array mandatory_argument_names = { "YYYYMMDD" }; + +public: + static constexpr auto name = Traits::YYYYMMDDName; + + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + + String getName() const override { return name; } + + bool isVariadic() const override { return false; } + size_t getNumberOfArguments() const override { return mandatory_argument_names.size(); } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + FunctionArgumentDescriptors args{ + {mandatory_argument_names[0], &isNumber, nullptr, "Number"} + }; + + validateFunctionArgumentTypes(*this, arguments, args); + + return std::make_shared(); + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + Columns converted_arguments = convertMandatoryArguments(arguments, mandatory_argument_names); + + auto res_column = Traits::ReturnDataType::ColumnType::create(input_rows_count); + auto & result_data = res_column->getData(); + + const auto & yyyymmdd_data = typeid_cast(*converted_arguments[0]).getData(); + + const auto & date_lut = DateLUT::instance(); + const Int32 max_days_since_epoch = date_lut.makeDayNum(Traits::MAX_DATE[0], Traits::MAX_DATE[1], Traits::MAX_DATE[2]); + + for (size_t i = 0; i < input_rows_count; ++i) + { + const auto yyyymmdd = static_cast(yyyymmdd_data[i]); + + const auto year = yyyymmdd / 10'000; + const auto month = yyyymmdd / 100 % 100; + const auto day = yyyymmdd % 100; + + Int32 day_num = 0; + + if (year >= Traits::MIN_YEAR && year <= Traits::MAX_YEAR && + month >= 1 && month <= 12 && + day >= 1 && day <= 31) + { + Int32 days_since_epoch = date_lut.makeDayNum(static_cast(year), static_cast(month), static_cast(day)); + if (days_since_epoch <= max_days_since_epoch) + day_num = days_since_epoch; + } + + result_data[i] = day_num; + } + + return res_column; + } +}; + +struct DateTraits +{ + static constexpr auto makeDateName = "makeDate"; + static constexpr auto YYYYMMDDName = "YYYYMMDDToDate"; using ReturnDataType = DataTypeDate; static constexpr auto MIN_YEAR = 1970; static constexpr auto MAX_YEAR = 2149; - /// This date has the maximum day number that fits in 16-bit uint static constexpr std::array MAX_DATE = {MAX_YEAR, 6, 6}; }; -struct MakeDate32Traits +struct Date32Traits { - static constexpr auto name = "makeDate32"; + static constexpr auto makeDateName = "makeDate32"; + static constexpr auto YYYYMMDDName = "YYYYMMDDToDate32"; using ReturnDataType = DataTypeDate32; static constexpr auto MIN_YEAR = 1900; @@ -196,11 +263,11 @@ struct MakeDate32Traits static constexpr std::array MAX_DATE = {MAX_YEAR, 12, 31}; }; -/// Common implementation for makeDateTime, makeDateTime64 -class FunctionMakeDateTimeBase : public FunctionWithNumericParamsBase +/// Functionality common to makeDateTime, makeDateTime64, YYYYMMDDhhmmssToDateTime, YYYYMMDDhhmmssToDateTime64 +class FunctionDateTimeBase : public FunctionWithNumericParamsBase { protected: - static constexpr std::array mandatory_argument_names = {"year", "month", "day", "hour", "minute", "second"}; + static constexpr UInt32 DEFAULT_PRECISION = 3; template static Int64 dateTime(T year, T month, T day_of_month, T hour, T minute, T second, const DateLUTImpl & lut) @@ -233,14 +300,35 @@ protected: std::string extractTimezone(const ColumnWithTypeAndName & timezone_argument) const { - std::string timezone; if (!isStringOrFixedString(timezone_argument.type) || !timezone_argument.column || (timezone_argument.column->size() != 1 && !typeid_cast(timezone_argument.column.get()))) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Argument 'timezone' for function {} must be const string", getName()); - timezone = timezone_argument.column->getDataAt(0).toString(); + + String timezone = timezone_argument.column->getDataAt(0).toString(); return timezone; } + + UInt32 extractPrecision(const ColumnWithTypeAndName & precision_argument) const + { + if (!isNumber(precision_argument.type) || !precision_argument.column || (precision_argument.column->size() != 1 && !typeid_cast(precision_argument.column.get()))) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Argument 'precision' for function {} must be constant number", getName()); + + Int64 precision = precision_argument.column->getInt(0); + + if (precision < 0 || precision > 9) + throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, + "Argument 'precision' for function {} must be in range [0, 9]", getName()); + + return static_cast(precision); + } +}; + +class FunctionMakeDateTimeBase : public FunctionDateTimeBase +{ +protected: + static constexpr std::array mandatory_argument_names = {"year", "month", "day", "hour", "minute", "second"}; }; /// makeDateTime(year, month, day, hour, minute, second, [timezone]) @@ -268,7 +356,7 @@ public: }; FunctionArgumentDescriptors optional_args{ - {optional_argument_names[0], &isString, nullptr, "String"} + {optional_argument_names[0], &isString, isColumnConst, "const String"} }; validateFunctionArgumentTypes(*this, arguments, mandatory_args, optional_args); @@ -329,7 +417,6 @@ class FunctionMakeDateTime64 : public FunctionMakeDateTimeBase { private: static constexpr std::array optional_argument_names = {"fraction", "precision", "timezone"}; - static constexpr UInt8 DEFAULT_PRECISION = 3; public: static constexpr auto name = "makeDateTime64"; @@ -350,9 +437,9 @@ public: }; FunctionArgumentDescriptors optional_args{ - {optional_argument_names[0], &isNumber, nullptr, "Number"}, - {optional_argument_names[1], &isNumber, nullptr, "Number"}, - {optional_argument_names[2], &isString, nullptr, "String"} + {optional_argument_names[0], &isNumber, isColumnConst, "const Number"}, + {optional_argument_names[1], &isNumber, isColumnConst, "const Number"}, + {optional_argument_names[2], &isString, isColumnConst, "const String"} }; validateFunctionArgumentTypes(*this, arguments, mandatory_args, optional_args); @@ -456,20 +543,179 @@ public: return res_column; } +}; +class FunctionYYYYMMDDhhmmssToDateTimeBase : public FunctionDateTimeBase +{ +protected: + static constexpr std::array mandatory_argument_names = { "YYYYMMDDhhmmss" }; +}; + +/// YYYYMMDDhhmmssToDateTime +class FunctionYYYYMMDDhhmmssToDateTime : public FunctionYYYYMMDDhhmmssToDateTimeBase +{ private: - UInt8 extractPrecision(const ColumnWithTypeAndName & precision_argument) const - { - Int64 precision = DEFAULT_PRECISION; - if (!isNumber(precision_argument.type) || !precision_argument.column || (precision_argument.column->size() != 1 && !typeid_cast(precision_argument.column.get()))) - throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, - "Argument 'precision' for function {} must be constant number", getName()); - precision = precision_argument.column->getInt(0); - if (precision < 0 || precision > 9) - throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, - "Argument 'precision' for function {} must be in range [0, 9]", getName()); + static constexpr std::array optional_argument_names = { "timezone" }; - return precision; +public: + static constexpr auto name = "YYYYMMDDhhmmssToDateTime"; + + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + + String getName() const override { return name; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + FunctionArgumentDescriptors mandatory_args{ + {mandatory_argument_names[0], &isNumber, nullptr, "Number"} + }; + + FunctionArgumentDescriptors optional_args{ + {optional_argument_names[0], &isString, isColumnConst, "const String"} + }; + + validateFunctionArgumentTypes(*this, arguments, mandatory_args, optional_args); + + /// Optional timezone argument + std::string timezone; + if (arguments.size() == mandatory_argument_names.size() + 1) + timezone = extractTimezone(arguments.back()); + + return std::make_shared(timezone); + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + std::string timezone; + if (arguments.size() == mandatory_argument_names.size() + 1) + timezone = extractTimezone(arguments.back()); + + Columns converted_arguments = convertMandatoryArguments(arguments, mandatory_argument_names); + + auto res_column = ColumnDateTime::create(input_rows_count); + auto & result_data = res_column->getData(); + + const auto & yyyymmddhhmmss_data = typeid_cast(*converted_arguments[0]).getData(); + + const auto & date_lut = DateLUT::instance(timezone); + + for (size_t i = 0; i < input_rows_count; i++) + { + const auto yyyymmddhhmmss = static_cast(yyyymmddhhmmss_data[i]); + + const auto yyyymmdd = yyyymmddhhmmss / 1'000'000; + const auto hhmmss = yyyymmddhhmmss % 1'000'000; + + const auto year = yyyymmdd / 10'000; + const auto month = yyyymmdd / 100 % 100; + const auto day = yyyymmdd % 100; + const auto hour = hhmmss / 10'000; + const auto minute = hhmmss / 100 % 100; + const auto second = hhmmss % 100; + + auto date_time = dateTime(year, month, day, hour, minute, second, date_lut); + + if (date_time < 0) [[unlikely]] + date_time = 0; + else if (date_time > 0x0ffffffffll) [[unlikely]] + date_time = 0x0ffffffffll; + + result_data[i] = static_cast(date_time); + } + + return res_column; + } +}; + +/// YYYYMMDDhhmmssToDateTime64 +class FunctionYYYYMMDDhhmmssToDateTime64 : public FunctionYYYYMMDDhhmmssToDateTimeBase +{ +private: + static constexpr std::array optional_argument_names = { "precision", "timezone" }; + +public: + static constexpr auto name = "YYYYMMDDhhmmssToDateTime64"; + + static FunctionPtr create(ContextPtr) { return std::make_shared(); } + + String getName() const override { return name; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + FunctionArgumentDescriptors mandatory_args{ + {mandatory_argument_names[0], &isNumber, nullptr, "Number"} + }; + + FunctionArgumentDescriptors optional_args{ + {optional_argument_names[0], &isNumber, isColumnConst, "const Number"}, + {optional_argument_names[0], &isString, isColumnConst, "const String"} + }; + + validateFunctionArgumentTypes(*this, arguments, mandatory_args, optional_args); + + /// Optional precision argument + auto precision = DEFAULT_PRECISION; + if (arguments.size() >= mandatory_argument_names.size() + 1) + precision = extractPrecision(arguments[mandatory_argument_names.size()]); + + /// Optional timezone argument + std::string timezone; + if (arguments.size() == mandatory_argument_names.size() + 2) + timezone = extractTimezone(arguments.back()); + + return std::make_shared(precision, timezone); + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + UInt32 precision = DEFAULT_PRECISION; + if (arguments.size() >= mandatory_argument_names.size() + 1) + precision = extractPrecision(arguments[mandatory_argument_names.size()]); + + std::string timezone; + if (arguments.size() == mandatory_argument_names.size() + 2) + timezone = extractTimezone(arguments.back()); + + Columns converted_arguments = convertMandatoryArguments(arguments, mandatory_argument_names); + + auto res_column = ColumnDateTime64::create(input_rows_count, static_cast(precision)); + auto & result_data = res_column->getData(); + + const auto & yyyymmddhhmmss_data = typeid_cast(*converted_arguments[0]).getData(); + + const auto & date_lut = DateLUT::instance(timezone); + + const auto fraction_pow = common::exp10_i32(precision); + + for (size_t i = 0; i < input_rows_count; i++) + { + const auto float_date = yyyymmddhhmmss_data[i]; + + const auto yyyymmddhhmmss = static_cast(float_date); + + const auto yyyymmdd = yyyymmddhhmmss / 1'000'000; + const auto hhmmss = yyyymmddhhmmss % 1'000'000; + + const auto decimal = float_date - yyyymmddhhmmss; + + const auto year = yyyymmdd / 10'000; + const auto month = yyyymmdd / 100 % 100; + const auto day = yyyymmdd % 100; + const auto hour = hhmmss / 10'000; + const auto minute = hhmmss / 100 % 100; + const auto second = hhmmss % 100; + + auto fraction = static_cast(decimal * fraction_pow); + + auto date_time = dateTime(year, month, day, hour, minute, second, date_lut); + + result_data[i] = DecimalUtils::decimalFromComponents( + date_time, + static_cast(fraction), + static_cast(precision)); + } + + return res_column; } }; @@ -477,10 +723,15 @@ private: REGISTER_FUNCTION(MakeDate) { - factory.registerFunction>({}, FunctionFactory::CaseInsensitive); - factory.registerFunction>(); + factory.registerFunction>({}, FunctionFactory::CaseInsensitive); + factory.registerFunction>(); factory.registerFunction(); factory.registerFunction(); + + factory.registerFunction>(); + factory.registerFunction>(); + factory.registerFunction(); + factory.registerFunction(); } } diff --git a/tests/queries/0_stateless/02245_make_datetime64.sql b/tests/queries/0_stateless/02245_make_datetime64.sql index a7b3a3d23c5..62784cb9b75 100644 --- a/tests/queries/0_stateless/02245_make_datetime64.sql +++ b/tests/queries/0_stateless/02245_make_datetime64.sql @@ -86,4 +86,4 @@ select makeDateTime64(year, 1, 1, 1, 0, 0, 0, precision, timezone) from ( select 1984 as year, 5 as precision, 'UTC' as timezone union all select 1985 as year, 5 as precision, 'UTC' as timezone -); -- { serverError 43 } +); -- { serverError 44 } diff --git a/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference b/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference index dd843058281..7928ac23438 100644 --- a/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference +++ b/tests/queries/0_stateless/02415_all_new_functions_must_be_documented.reference @@ -56,6 +56,10 @@ URLHierarchy URLPathHierarchy UUIDNumToString UUIDStringToNum +YYYYMMDDToDate +YYYYMMDDToDate32 +YYYYMMDDhhmmssToDateTime +YYYYMMDDhhmmssToDateTime64 _CAST __bitBoolMaskAnd __bitBoolMaskOr diff --git a/tests/queries/0_stateless/02876_yyyymmddhhmmsstodatetime.reference b/tests/queries/0_stateless/02876_yyyymmddhhmmsstodatetime.reference new file mode 100644 index 00000000000..9d010535451 --- /dev/null +++ b/tests/queries/0_stateless/02876_yyyymmddhhmmsstodatetime.reference @@ -0,0 +1,85 @@ +--- YYYYMMDDToDateTime +Invalid input types are rejected +Result type is DateTime +DateTime +Nullable(DateTime) +Check correctness, integer arguments +1970-01-02 11:59:59 +1970-01-01 00:00:00 +2020-02-29 11:11:11 +2023-09-11 15:05:05 +2106-02-07 06:28:15 +2106-02-07 06:28:15 +Check correctness, float arguments +1970-01-02 11:59:59 +1970-01-01 00:00:00 +2020-02-29 11:11:11 +2023-09-11 15:05:05 +2106-02-07 06:28:15 +2106-02-07 06:28:15 +Check correctness, decimal arguments +1970-01-02 11:59:59 +1970-01-01 00:00:00 +2020-02-29 11:11:11 +2023-09-11 15:05:05 +2106-02-07 06:28:15 +2106-02-07 06:28:15 +Special cases +1970-01-01 00:00:00 +1970-01-01 00:00:00 +1970-01-01 00:00:00 +1970-01-01 00:00:00 +2106-02-07 06:28:15 +1970-01-01 00:00:00 +1970-01-01 00:00:00 +1970-01-01 00:00:00 +1970-01-01 00:00:00 +2023-02-28 11:11:11 +1970-01-01 00:00:00 +1970-01-01 00:00:00 +\N +1970-01-01 00:00:00 +1970-01-01 00:00:00 +--- YYYYMMDDToDateTime64 +Invalid input types are rejected +Result type is DateTime +DateTime64(3) +DateTime64(5) +Nullable(DateTime64(3)) +Check correctness, integer arguments +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +2020-02-29 11:11:11.000 +2023-09-11 15:05:05.000 +2299-12-31 23:59:59.000 +2299-12-31 23:59:59.000 +Check correctness, float arguments +1900-01-01 00:00:00.900 +1900-01-01 00:00:00.899 +2020-02-29 11:11:11.101 +2023-09-11 15:05:05.101 +2299-12-31 23:59:59.101 +2299-12-31 23:59:59.101 +Check correctness, decimal arguments +1900-01-01 00:00:00.900 +1900-01-01 00:00:00.899 +2020-02-29 11:11:11.101 +2023-09-11 15:05:05.101 +2299-12-31 23:59:59.101 +2299-12-31 23:59:59.101 +Special cases +1900-01-01 00:00:00.648 +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +2299-12-31 23:59:59.000 +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +2023-02-28 11:11:11.000 +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 +\N +1900-01-01 00:00:00.000 +1900-01-01 00:00:00.000 diff --git a/tests/queries/0_stateless/02876_yyyymmddhhmmsstodatetime.sql b/tests/queries/0_stateless/02876_yyyymmddhhmmsstodatetime.sql new file mode 100644 index 00000000000..1b5f6220b50 --- /dev/null +++ b/tests/queries/0_stateless/02876_yyyymmddhhmmsstodatetime.sql @@ -0,0 +1,119 @@ +SET session_timezone = 'UTC'; -- no time zone randomization, please + +----------------------------------------------------------- +SELECT '--- YYYYMMDDToDateTime'; + +SELECT 'Invalid input types are rejected'; +SELECT YYYYMMDDhhmmssToDateTime(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +SELECT YYYYMMDDhhmmssToDateTime(toDate('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime(toDate32('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime(toDateTime('2023-09-11 12:18:00')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime(toDateTime64('2023-09-11 12:18:00', 3)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime('2023-09-11'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime(20230911134254, 3); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime(20230911134254, 'invalid tz'); -- { serverError BAD_ARGUMENTS } +SELECT YYYYMMDDhhmmssToDateTime(20230911134254, 'Europe/Berlin', 'bad'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } + +SELECT 'Result type is DateTime'; +SELECT toTypeName(YYYYMMDDhhmmssToDateTime(19910824)); +SELECT toTypeName(YYYYMMDDhhmmssToDateTime(cast(19910824 AS Nullable(UInt64)))); +-- +SELECT 'Check correctness, integer arguments'; +SELECT YYYYMMDDhhmmssToDateTime(19691231595959); +SELECT YYYYMMDDhhmmssToDateTime(19700101000000); +SELECT YYYYMMDDhhmmssToDateTime(20200229111111); -- leap day +SELECT YYYYMMDDhhmmssToDateTime(20230911150505); +SELECT YYYYMMDDhhmmssToDateTime(21060207062815); +SELECT YYYYMMDDhhmmssToDateTime(21060207062816); + +SELECT 'Check correctness, float arguments'; +SELECT YYYYMMDDhhmmssToDateTime(19691231595959.1); +SELECT YYYYMMDDhhmmssToDateTime(19700101000000.1); +SELECT YYYYMMDDhhmmssToDateTime(20200229111111.1); -- leap day +SELECT YYYYMMDDhhmmssToDateTime(20230911150505.1); +SELECT YYYYMMDDhhmmssToDateTime(21060207062815.1); +SELECT YYYYMMDDhhmmssToDateTime(21060207062816.1); + +SELECT 'Check correctness, decimal arguments'; +SELECT YYYYMMDDhhmmssToDateTime(toDecimal64(19691231595959.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime(toDecimal64(19700101000000.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime(toDecimal64(20200229111111.1, 5)); -- leap day +SELECT YYYYMMDDhhmmssToDateTime(toDecimal64(20230911150505.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime(toDecimal64(21060207062815.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime(toDecimal64(21060207062816.1, 5)); + +SELECT 'Special cases'; +SELECT YYYYMMDDhhmmssToDateTime(-20230911111111); -- negative +SELECT YYYYMMDDhhmmssToDateTime(110); -- invalid everything +SELECT YYYYMMDDhhmmssToDateTime(999999999999999999); -- huge value +SELECT YYYYMMDDhhmmssToDateTime(15001113111111); -- year out of range +SELECT YYYYMMDDhhmmssToDateTime(35001113111111); -- year out of range +SELECT YYYYMMDDhhmmssToDateTime(20231620111111); -- invalid month +SELECT YYYYMMDDhhmmssToDateTime(20230020111111); -- invalid month +SELECT YYYYMMDDhhmmssToDateTime(20230940111111); -- invalid day +SELECT YYYYMMDDhhmmssToDateTime(20230900111111); -- invalid day +SELECT YYYYMMDDhhmmssToDateTime(20230228111111); -- leap day when there is none +SELECT YYYYMMDDhhmmssToDateTime(True); +SELECT YYYYMMDDhhmmssToDateTime(False); +SELECT YYYYMMDDhhmmssToDateTime(NULL); +SELECT YYYYMMDDhhmmssToDateTime(yyyymmdd) FROM (SELECT 19840121 AS yyyymmdd UNION ALL SELECT 20230911 AS yyyymmdd) ORDER BY yyyymmdd; -- non-const + +----------------------------------------------------------- +SELECT '--- YYYYMMDDToDateTime64'; + +SELECT 'Invalid input types are rejected'; +SELECT YYYYMMDDhhmmssToDateTime64(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +SELECT YYYYMMDDhhmmssToDateTime64(toDate('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64(toDate32('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64(toDateTime('2023-09-11 12:18:00')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64(toDateTime64('2023-09-11 12:18:00', 3)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64('2023-09-11'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64('2023-09-11', 'invalid precision'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64(20230911134254, 3, 3); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDhhmmssToDateTime64(20230911134254, 3, 'invalid tz'); -- { serverError BAD_ARGUMENTS } +SELECT YYYYMMDDhhmmssToDateTime64(20230911134254, 3, 'Europe/Berlin', 'no more args'); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } + +SELECT 'Result type is DateTime'; +SELECT toTypeName(YYYYMMDDhhmmssToDateTime64(19910824)); +SELECT toTypeName(YYYYMMDDhhmmssToDateTime64(19910824, 5)); +SELECT toTypeName(YYYYMMDDhhmmssToDateTime64(cast(19910824 AS Nullable(UInt64)))); + +SELECT 'Check correctness, integer arguments'; +SELECT YYYYMMDDhhmmssToDateTime64(189912315959); +SELECT YYYYMMDDhhmmssToDateTime64(19000101000000); +SELECT YYYYMMDDhhmmssToDateTime64(20200229111111); -- leap day +SELECT YYYYMMDDhhmmssToDateTime64(20230911150505); +SELECT YYYYMMDDhhmmssToDateTime64(22991231235959); +SELECT YYYYMMDDhhmmssToDateTime64(23000101000000); + +SELECT 'Check correctness, float arguments'; +SELECT YYYYMMDDhhmmssToDateTime64(189912315959.1); +SELECT YYYYMMDDhhmmssToDateTime64(19000101000000.1); +SELECT YYYYMMDDhhmmssToDateTime64(20200229111111.1); -- leap day +SELECT YYYYMMDDhhmmssToDateTime64(20230911150505.1); +SELECT YYYYMMDDhhmmssToDateTime64(22991231235959.1); +SELECT YYYYMMDDhhmmssToDateTime64(23000101000000.1); + +SELECT 'Check correctness, decimal arguments'; +SELECT YYYYMMDDhhmmssToDateTime64(toDecimal64(189912315959.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime64(toDecimal64(19000101000000.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime64(toDecimal64(20200229111111.1, 5)); -- leap day +SELECT YYYYMMDDhhmmssToDateTime64(toDecimal64(20230911150505.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime64(toDecimal64(22991231235959.1, 5)); +SELECT YYYYMMDDhhmmssToDateTime64(toDecimal64(23000101000000.1, 5)); + +SELECT 'Special cases'; +SELECT YYYYMMDDhhmmssToDateTime64(-20230911111111); -- negative +SELECT YYYYMMDDhhmmssToDateTime64(110); -- invalid everything +SELECT YYYYMMDDhhmmssToDateTime64(999999999999999999); -- huge value +SELECT YYYYMMDDhhmmssToDateTime64(15001113111111); -- year out of range +SELECT YYYYMMDDhhmmssToDateTime64(35001113111111); -- year out of range +SELECT YYYYMMDDhhmmssToDateTime64(20231620111111); -- invalid month +SELECT YYYYMMDDhhmmssToDateTime64(20230020111111); -- invalid month +SELECT YYYYMMDDhhmmssToDateTime64(20230940111111); -- invalid day +SELECT YYYYMMDDhhmmssToDateTime64(20230900111111); -- invalid day +SELECT YYYYMMDDhhmmssToDateTime64(20230228111111); -- leap day when there is none +SELECT YYYYMMDDhhmmssToDateTime64(True); +SELECT YYYYMMDDhhmmssToDateTime64(False); +SELECT YYYYMMDDhhmmssToDateTime64(NULL); +SELECT YYYYMMDDhhmmssToDateTime64(yyyymmdd) FROM (SELECT 19840121 AS yyyymmdd UNION ALL SELECT 20230911 AS yyyymmdd) ORDER BY yyyymmdd; -- non-const diff --git a/tests/queries/0_stateless/02876_yyyymmddtodate.reference b/tests/queries/0_stateless/02876_yyyymmddtodate.reference new file mode 100644 index 00000000000..e3c6a9e2d7c --- /dev/null +++ b/tests/queries/0_stateless/02876_yyyymmddtodate.reference @@ -0,0 +1,84 @@ +--- YYYYMMDDToDate +Invalid input types are rejected +Result type is Date +Date +Nullable(Date) +Check correctness, integer arguments +1970-01-01 +1970-01-01 +2020-02-29 +2023-09-11 +2149-06-06 +1970-01-01 +Check correctness, float arguments +1970-01-01 +1970-01-01 +2020-02-29 +2023-09-11 +2149-06-06 +1970-01-01 +Check correctness, decimal arguments +1970-01-01 +1970-01-01 +2020-02-29 +2023-09-11 +2149-06-06 +1970-01-01 +Special cases +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +2023-02-28 +1970-01-01 +1970-01-01 +\N +1984-01-21 +2023-09-11 +--- YYYYMMDDToDate32 +Invalid input types are rejected +Result type is Date32 +Date32 +Nullable(Date32) +Check correctness, integer arguments +1970-01-01 +1900-01-01 +2020-02-29 +2023-09-11 +2299-12-31 +1970-01-01 +Check correctness, float arguments +1970-01-01 +1900-01-01 +2020-02-29 +2023-09-11 +2299-12-31 +1970-01-01 +Check correctness, decimal arguments +1970-01-01 +1900-01-01 +2020-02-29 +2023-09-11 +2299-12-31 +1970-01-01 +Special cases +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +1970-01-01 +2023-02-28 +1970-01-01 +1970-01-01 +\N +1984-01-21 +2023-09-11 diff --git a/tests/queries/0_stateless/02876_yyyymmddtodate.sql b/tests/queries/0_stateless/02876_yyyymmddtodate.sql new file mode 100644 index 00000000000..fbacca1e2a2 --- /dev/null +++ b/tests/queries/0_stateless/02876_yyyymmddtodate.sql @@ -0,0 +1,112 @@ +----------------------------------------------------------- +SELECT '--- YYYYMMDDToDate'; + +SELECT 'Invalid input types are rejected'; +SELECT YYYYMMDDToDate(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +SELECT YYYYMMDDToDate(toDate('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate(toDate32('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate(toDateTime('2023-09-11 12:18:00')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate(toDateTime64('2023-09-11 12:18:00', 3)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate('2023-09-11'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate(2023, 09, 11); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +SELECT YYYYMMDDToDate(2023, 110); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } + +SELECT 'Result type is Date'; +SELECT toTypeName(YYYYMMDDToDate(19910824)); +SELECT toTypeName(YYYYMMDDToDate(cast(19910824 AS Nullable(UInt64)))); + +SELECT 'Check correctness, integer arguments'; +SELECT YYYYMMDDToDate(19691231); +SELECT YYYYMMDDToDate(19700101); +SELECT YYYYMMDDToDate(20200229); -- leap day +SELECT YYYYMMDDToDate(20230911); +SELECT YYYYMMDDToDate(21490606); +SELECT YYYYMMDDToDate(21490607); + +SELECT 'Check correctness, float arguments'; +SELECT YYYYMMDDToDate(19691231.1); +SELECT YYYYMMDDToDate(19700101.1); +SELECT YYYYMMDDToDate(20200229.1); -- leap day +SELECT YYYYMMDDToDate(20230911.1); +SELECT YYYYMMDDToDate(21490606.1); +SELECT YYYYMMDDToDate(21490607.1); + +SELECT 'Check correctness, decimal arguments'; +SELECT YYYYMMDDToDate(toDecimal64(19691231.1, 5)); +SELECT YYYYMMDDToDate(toDecimal64(19700101.1, 5)); +SELECT YYYYMMDDToDate(toDecimal64(20200229.1, 5)); -- leap day +SELECT YYYYMMDDToDate(toDecimal64(20230911.1, 5)); +SELECT YYYYMMDDToDate(toDecimal64(21490606.1, 5)); +SELECT YYYYMMDDToDate(toDecimal64(21490607.1, 5)); + +SELECT 'Special cases'; +SELECT YYYYMMDDToDate(-20230911); -- negative +SELECT YYYYMMDDToDate(110); -- invalid everything +SELECT YYYYMMDDToDate(9999999999999); -- huge value +SELECT YYYYMMDDToDate(15001113); -- year out of range +SELECT YYYYMMDDToDate(35001113); -- year out of range +SELECT YYYYMMDDToDate(20231620); -- invalid month +SELECT YYYYMMDDToDate(20230020); -- invalid month +SELECT YYYYMMDDToDate(20230940); -- invalid day +SELECT YYYYMMDDToDate(20230900); -- invalid day +SELECT YYYYMMDDToDate(20230228); -- leap day when there is none +SELECT YYYYMMDDToDate(True); +SELECT YYYYMMDDToDate(False); +SELECT YYYYMMDDToDate(NULL); +SELECT YYYYMMDDToDate(yyyymmdd) FROM (SELECT 19840121 AS yyyymmdd UNION ALL SELECT 20230911 AS yyyymmdd) ORDER BY yyyymmdd; -- non-const + +----------------------------------------------------------- +SELECT '--- YYYYMMDDToDate32'; + +SELECT 'Invalid input types are rejected'; +SELECT YYYYMMDDToDate32(toDate('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate32(toDate32('2023-09-11')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate32(toDateTime('2023-09-11 12:18:00')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate32(toDateTime64('2023-09-11 12:18:00', 3)); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate32('2023-09-11'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT } +SELECT YYYYMMDDToDate32(2023, 09, 11); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } +SELECT YYYYMMDDToDate32(2023, 110); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH } + +SELECT 'Result type is Date32'; +SELECT toTypeName(YYYYMMDDToDate32(19910824)); +SELECT toTypeName(YYYYMMDDToDate32(cast(19910824 AS Nullable(UInt64)))); + +SELECT 'Check correctness, integer arguments'; +SELECT YYYYMMDDToDate32(18991231); +SELECT YYYYMMDDToDate32(19000101); +SELECT YYYYMMDDToDate32(20200229); -- leap day +SELECT YYYYMMDDToDate32(20230911); +SELECT YYYYMMDDToDate32(22991231); +SELECT YYYYMMDDToDate32(23000101); + +SELECT 'Check correctness, float arguments'; +SELECT YYYYMMDDToDate32(18991231.1); +SELECT YYYYMMDDToDate32(19000101.1); +SELECT YYYYMMDDToDate32(20200229.1); -- leap day +SELECT YYYYMMDDToDate32(20230911.1); +SELECT YYYYMMDDToDate32(22991231.1); +SELECT YYYYMMDDToDate32(23000101.1); + +SELECT 'Check correctness, decimal arguments'; +SELECT YYYYMMDDToDate32(toDecimal64(18991231.1, 5)); +SELECT YYYYMMDDToDate32(toDecimal64(19000101.1, 5)); +SELECT YYYYMMDDToDate32(toDecimal64(20200229.1, 5)); -- leap day +SELECT YYYYMMDDToDate32(toDecimal64(20230911.1, 5)); +SELECT YYYYMMDDToDate32(toDecimal64(22991231.1, 5)); +SELECT YYYYMMDDToDate32(toDecimal64(23000101.1, 5)); + +SELECT 'Special cases'; +SELECT YYYYMMDDToDate32(-20230911); -- negative +SELECT YYYYMMDDToDate32(110); -- invalid everything +SELECT YYYYMMDDToDate32(9999999999999); -- huge value +SELECT YYYYMMDDToDate32(15001113); -- year out of range +SELECT YYYYMMDDToDate32(35001113); -- year out of range +SELECT YYYYMMDDToDate32(20231620); -- invalid month +SELECT YYYYMMDDToDate32(20230020); -- invalid month +SELECT YYYYMMDDToDate32(20230940); -- invalid day +SELECT YYYYMMDDToDate32(20230900); -- invalid day +SELECT YYYYMMDDToDate32(20230228); -- leap day when there is none +SELECT YYYYMMDDToDate32(True); +SELECT YYYYMMDDToDate32(False); +SELECT YYYYMMDDToDate32(NULL); +SELECT YYYYMMDDToDate32(yyyymmdd) FROM (SELECT 19840121 AS yyyymmdd UNION ALL SELECT 20230911 AS yyyymmdd) ORDER BY yyyymmdd; -- non-const