diff --git a/src/Formats/SchemaInferenceUtils.cpp b/src/Formats/SchemaInferenceUtils.cpp index 940b85065542..837947997fb2 100644 --- a/src/Formats/SchemaInferenceUtils.cpp +++ b/src/Formats/SchemaInferenceUtils.cpp @@ -791,7 +791,7 @@ namespace ReadBufferFromString buf(field); DayNum tmp; - return tryReadDateText(tmp, buf, DateLUT::instance(), /*allowed_delimiters=*/"-/:") && buf.eof(); + return tryReadDateText(tmp, buf, DateLUT::instance(), /*allowed_delimiters=*/"-/:", /*saturate_on_overflow=*/false) && buf.eof(); } DataTypePtr tryInferDateTimeOrDateTime64(std::string_view field, const FormatSettings & settings) @@ -827,7 +827,7 @@ namespace switch (settings.date_time_input_format) { case FormatSettings::DateTimeInputFormat::Basic: - if (tryReadDateTimeText(tmp, buf, DateLUT::instance(), /*allowed_date_delimiters=*/"-/:", /*allowed_time_delimiters=*/":") && buf.eof()) + if (tryReadDateTimeText(tmp, buf, DateLUT::instance(), /*allowed_date_delimiters=*/"-/:", /*allowed_time_delimiters=*/":", /*saturate_on_overflow=*/false) && buf.eof()) return std::make_shared(); break; case FormatSettings::DateTimeInputFormat::BestEffort: @@ -846,7 +846,7 @@ namespace switch (settings.date_time_input_format) { case FormatSettings::DateTimeInputFormat::Basic: - if (tryReadDateTime64Text(tmp, 9, buf, DateLUT::instance(), /*allowed_date_delimiters=*/"-/:", /*allowed_time_delimiters=*/":") && buf.eof()) + if (tryReadDateTime64Text(tmp, 9, buf, DateLUT::instance(), /*allowed_date_delimiters=*/"-/:", /*allowed_time_delimiters=*/":", /*saturate_on_overflow=*/false) && buf.eof()) return std::make_shared(9); break; case FormatSettings::DateTimeInputFormat::BestEffort: diff --git a/src/IO/ReadHelpers.cpp b/src/IO/ReadHelpers.cpp index c3575ca52bc6..410f3b8562ab 100644 --- a/src/IO/ReadHelpers.cpp +++ b/src/IO/ReadHelpers.cpp @@ -1458,7 +1458,13 @@ template bool readDateTextFallback(LocalDate &, ReadBuffer &, const char * template -ReturnType readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters, const char * allowed_time_delimiters) +ReturnType readDateTimeTextFallback( + time_t & datetime, + ReadBuffer & buf, + const DateLUTImpl & date_lut, + const char * allowed_date_delimiters, + const char * allowed_time_delimiters, + bool saturate_on_overflow) { static constexpr bool throw_exception = std::is_same_v; @@ -1576,15 +1582,29 @@ ReturnType readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf, const D } else { - auto datetime_maybe = tryToMakeDateTime(date_lut, year, month, day, hour, minute, second); - if (!datetime_maybe) - return false; + if (saturate_on_overflow) + { + /// Use saturating version - makeDateTime saturates out-of-range years + if (unlikely(year == 0)) + datetime = 0; + else + datetime = makeDateTime(date_lut, year, month, day, hour, minute, second); + } + else + { + /// Use non-saturating version - return false for out-of-range values + auto datetime_maybe = tryToMakeDateTime(date_lut, year, month, day, hour, minute, second); + if (!datetime_maybe) + return false; - /// For usual DateTime check if value is within supported range - if (!dt64_mode && (*datetime_maybe < 0 || *datetime_maybe > UINT32_MAX)) - return false; + if constexpr (!dt64_mode) + { + if (*datetime_maybe < 0 || *datetime_maybe > static_cast(UINT32_MAX)) + return false; + } - datetime = *datetime_maybe; + datetime = *datetime_maybe; + } } } else @@ -1620,10 +1640,10 @@ ReturnType readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf, const D return ReturnType(true); } -template void readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *); -template void readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *); -template bool readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *); -template bool readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *); +template void readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *, bool); +template void readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *, bool); +template bool readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *, bool); +template bool readDateTimeTextFallback(time_t &, ReadBuffer &, const DateLUTImpl &, const char *, const char *, bool); template ReturnType readTimeTextFallback(time_t & time, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters, const char * allowed_time_delimiters) diff --git a/src/IO/ReadHelpers.h b/src/IO/ReadHelpers.h index 058799acf457..5b7779d23be9 100644 --- a/src/IO/ReadHelpers.h +++ b/src/IO/ReadHelpers.h @@ -619,7 +619,7 @@ inline bool tryToConvertToDayNum(DayNum & date, ExtendedDayNum & from) } template -inline ReturnType readDateTextImpl(DayNum & date, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_delimiters = nullptr) +inline ReturnType readDateTextImpl(DayNum & date, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_delimiters = nullptr, bool saturate_on_overflow = true) { static constexpr bool throw_exception = std::is_same_v; @@ -636,12 +636,22 @@ inline ReturnType readDateTextImpl(DayNum & date, ReadBuffer & buf, const DateLU if (!readDateTextImpl(local_date, buf, allowed_delimiters)) return false; - auto ret = tryToMakeDayNum(date_lut, local_date.year(), local_date.month(), local_date.day()); - if (!ret) - return false; + if (saturate_on_overflow) + { + /// Use saturating versions - makeDayNum saturates out-of-range years, convertToDayNum saturates to 0 or 0xFFFF + ExtendedDayNum ret = makeDayNum(date_lut, local_date.year(), local_date.month(), local_date.day()); + convertToDayNum(date, ret); + } + else + { + /// Use non-saturating versions - return false for out-of-range values + auto ret = tryToMakeDayNum(date_lut, local_date.year(), local_date.month(), local_date.day()); + if (!ret) + return false; - if (!tryToConvertToDayNum(date, *ret)) - return false; + if (!tryToConvertToDayNum(date, *ret)) + return false; + } return true; } @@ -685,9 +695,9 @@ inline bool tryReadDateText(LocalDate & date, ReadBuffer & buf, const char * all return readDateTextImpl(date, buf, allowed_delimiters); } -inline bool tryReadDateText(DayNum & date, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance(), const char * allowed_delimiters = nullptr) +inline bool tryReadDateText(DayNum & date, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance(), const char * allowed_delimiters = nullptr, bool saturate_on_overflow = true) { - return readDateTextImpl(date, buf, time_zone, allowed_delimiters); + return readDateTextImpl(date, buf, time_zone, allowed_delimiters, saturate_on_overflow); } inline bool tryReadDateText(ExtendedDayNum & date, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance(), const char * allowed_delimiters = nullptr) @@ -815,7 +825,7 @@ inline T parseFromStringWithoutAssertEOF(std::string_view str) } template -ReturnType readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr); +ReturnType readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr, bool saturate_on_overflow = true); template ReturnType readTimeTextFallback(time_t & time, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr); @@ -824,7 +834,7 @@ ReturnType readTimeTextFallback(time_t & time, ReadBuffer & buf, const DateLUTIm * As an exception, also supported parsing of unix timestamp in form of decimal number. */ template -inline ReturnType readDateTimeTextImpl(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr) +inline ReturnType readDateTimeTextImpl(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr, bool saturate_on_overflow = true) { static constexpr bool throw_exception = std::is_same_v; @@ -898,15 +908,30 @@ inline ReturnType readDateTimeTextImpl(time_t & datetime, ReadBuffer & buf, cons } else { - auto datetime_maybe = tryToMakeDateTime(date_lut, year, month, day, hour, minute, second); - if (!datetime_maybe) - return false; - - /// For usual DateTime check if value is within supported range - if (!dt64_mode && (*datetime_maybe < 0 || *datetime_maybe > UINT32_MAX)) - return false; - - datetime = *datetime_maybe; + if (saturate_on_overflow) + { + /// Use saturating version - makeDateTime saturates out-of-range years + if (unlikely(year == 0)) + datetime = 0; + else + datetime = makeDateTime(date_lut, year, month, day, hour, minute, second); + } + else + { + /// Use non-saturating version - return false for out-of-range values + auto datetime_maybe = tryToMakeDateTime(date_lut, year, month, day, hour, minute, second); + if (!datetime_maybe) + return false; + + /// For usual DateTime check if value is within supported range + if constexpr (!dt64_mode) + { + if (*datetime_maybe < 0 || *datetime_maybe > static_cast(UINT32_MAX)) + return false; + } + + datetime = *datetime_maybe; + } } if (dt_long) @@ -919,7 +944,7 @@ inline ReturnType readDateTimeTextImpl(time_t & datetime, ReadBuffer & buf, cons /// Why not readIntTextUnsafe? Because for needs of AdFox, parsing of unix timestamp with leading zeros is supported: 000...NNNN. return readIntTextImpl(datetime, buf); } - return readDateTimeTextFallback(datetime, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters); + return readDateTimeTextFallback(datetime, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters, saturate_on_overflow); } /** In hhh:mm:ss format, according to specified time zone. @@ -1107,7 +1132,7 @@ inline ReturnType readTimeTextImpl(time_t & time, ReadBuffer & buf, const DateLU } template -inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr) +inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut, const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr, bool saturate_on_overflow = true) { static constexpr bool throw_exception = std::is_same_v; @@ -1121,7 +1146,7 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re { try { - readDateTimeTextImpl(whole, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters); + readDateTimeTextImpl(whole, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters, saturate_on_overflow); } catch (const DB::Exception &) { @@ -1131,7 +1156,7 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re } else { - auto ok = readDateTimeTextImpl(whole, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters); + auto ok = readDateTimeTextImpl(whole, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters, saturate_on_overflow); if (!ok && (buf.eof() || *buf.position() != '.')) return ReturnType(false); } @@ -1367,14 +1392,14 @@ inline bool tryReadTimeText(time_t & time, ReadBuffer & buf, const DateLUTImpl & return readTimeTextImpl(time, buf, time_zone, allowed_date_delimiters, allowed_time_delimiters); } -inline bool tryReadDateTimeText(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance(), const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr) +inline bool tryReadDateTimeText(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance(), const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr, bool saturate_on_overflow = true) { - return readDateTimeTextImpl(datetime, buf, time_zone, allowed_date_delimiters, allowed_time_delimiters); + return readDateTimeTextImpl(datetime, buf, time_zone, allowed_date_delimiters, allowed_time_delimiters, saturate_on_overflow); } -inline bool tryReadDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance(), const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr) +inline bool tryReadDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance(), const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr, bool saturate_on_overflow = true) { - return readDateTimeTextImpl(datetime64, scale, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters); + return readDateTimeTextImpl(datetime64, scale, buf, date_lut, allowed_date_delimiters, allowed_time_delimiters, saturate_on_overflow); } inline bool tryReadTime64Text(Time64 & time64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance(), const char * allowed_date_delimiters = nullptr, const char * allowed_time_delimiters = nullptr) diff --git a/src/IO/parseDateTimeBestEffort.cpp b/src/IO/parseDateTimeBestEffort.cpp index f4981482df2f..5b2c7ed5a9c7 100644 --- a/src/IO/parseDateTimeBestEffort.cpp +++ b/src/IO/parseDateTimeBestEffort.cpp @@ -13,8 +13,8 @@ namespace DB namespace ErrorCodes { - extern const int LOGICAL_ERROR; - extern const int CANNOT_PARSE_DATETIME; +extern const int LOGICAL_ERROR; +extern const int CANNOT_PARSE_DATETIME; } @@ -753,7 +753,7 @@ ReturnType parseDateTimeBestEffortImpl( } }; - if constexpr (std::is_same_v) + if constexpr (!strict || std::is_same_v) { if (has_time_zone_offset) { @@ -764,10 +764,12 @@ ReturnType parseDateTimeBestEffortImpl( { res = local_time_zone.makeDateTime(year, month, day_of_month, hour, minute, second); } + + if constexpr (std::is_same_v) + return true; } else { - if (has_time_zone_offset) { auto res_maybe = utc_time_zone.tryToMakeDateTime(year, month, day_of_month, hour, minute, second); @@ -775,9 +777,11 @@ ReturnType parseDateTimeBestEffortImpl( return false; /// For usual DateTime check if value is within supported range - if (!is_64 && (*res_maybe < 0 || *res_maybe > UINT32_MAX)) - return false; - + if constexpr (!is_64) + { + if (*res_maybe < 0 || *res_maybe > UINT32_MAX) + return false; + } res = *res_maybe; adjust_time_zone(); } @@ -788,9 +792,11 @@ ReturnType parseDateTimeBestEffortImpl( return false; /// For usual DateTime check if value is within supported range - if (!is_64 && (*res_maybe < 0 || *res_maybe > UINT32_MAX)) - return false; - + if constexpr (!is_64) + { + if (*res_maybe < 0 || *res_maybe > UINT32_MAX) + return false; + } res = *res_maybe; } diff --git a/tests/queries/0_stateless/01186_conversion_to_nullable.reference b/tests/queries/0_stateless/01186_conversion_to_nullable.reference index b05ab29a9952..e4c1fd7c40bf 100644 --- a/tests/queries/0_stateless/01186_conversion_to_nullable.reference +++ b/tests/queries/0_stateless/01186_conversion_to_nullable.reference @@ -10,12 +10,12 @@ 256 2020-12-24 \N +1970-01-01 \N -\N -\N +2149-06-06 2020-12-24 01:02:03 \N -\N +1970-01-01 02:00:00 \N 2020-12-24 01:02:03.00 \N diff --git a/tests/queries/0_stateless/01556_accurate_cast_or_null.reference b/tests/queries/0_stateless/01556_accurate_cast_or_null.reference index 6b5e76bcd42a..5187a19cc726 100644 --- a/tests/queries/0_stateless/01556_accurate_cast_or_null.reference +++ b/tests/queries/0_stateless/01556_accurate_cast_or_null.reference @@ -36,13 +36,13 @@ 2023-05-30 14:38:20 1970-01-01 00:00:19 1970-01-01 19:26:40 -\N -\N +1970-01-01 00:00:00 +2106-02-07 06:28:15 \N \N \N 2023-05-30 -\N +2149-06-06 1970-01-20 \N \N diff --git a/tests/queries/0_stateless/03149_asof_join_ddb_timestamps.reference b/tests/queries/0_stateless/03149_asof_join_ddb_timestamps.reference index 9447377eabda..7cfc85d23a5d 100644 --- a/tests/queries/0_stateless/03149_asof_join_ddb_timestamps.reference +++ b/tests/queries/0_stateless/03149_asof_join_ddb_timestamps.reference @@ -7,6 +7,7 @@ 2023-03-21 19:00:00 3 2023-03-21 20:00:00 3 2023-03-21 21:00:00 3 +2106-02-07 06:28:15 9 2023-03-21 13:00:00 0 2023-03-21 14:00:00 1 2023-03-21 15:00:00 2 @@ -16,6 +17,7 @@ 2023-03-21 19:00:00 3 2023-03-21 20:00:00 3 2023-03-21 21:00:00 3 +2106-02-07 06:28:15 9 2023-03-21 12:00:00 \N 2023-03-21 13:00:00 0 2023-03-21 14:00:00 1 @@ -26,7 +28,7 @@ 2023-03-21 19:00:00 3 2023-03-21 20:00:00 3 2023-03-21 21:00:00 3 -\N \N +2106-02-07 06:28:15 9 \N \N 2023-03-21 12:00:00 0 2023-03-21 13:00:00 0 @@ -38,7 +40,7 @@ 2023-03-21 19:00:00 3 2023-03-21 20:00:00 3 2023-03-21 21:00:00 3 -\N 0 +2106-02-07 06:28:15 9 \N 0 2023-03-21 12:00:00 \N 2023-03-21 13:00:00 \N @@ -50,5 +52,5 @@ 2023-03-21 19:00:00 \N 2023-03-21 20:00:00 \N 2023-03-21 21:00:00 \N -\N \N +2106-02-07 06:28:15 \N \N \N diff --git a/tests/queries/0_stateless/03724_to_date_time_or_null_negative_arg_bug.reference b/tests/queries/0_stateless/03724_to_date_time_or_null_negative_arg_bug.reference new file mode 100644 index 000000000000..ed23201e10dc --- /dev/null +++ b/tests/queries/0_stateless/03724_to_date_time_or_null_negative_arg_bug.reference @@ -0,0 +1,38 @@ +toDateOrNull: +1960-01-01 1970-01-01 +1800-01-01 1970-01-01 +3000-01-01 2149-06-06 +toDateTimeOrNull: +1960-01-01 00:00:00 1970-01-01 00:00:00 +1800-01-01 00:00:00 1970-01-01 00:00:00 +3000-01-01 00:00:00 2106-02-07 06:28:15 +toDateTime64OrNull: +1800-01-01 00:00:00 1900-01-01 00:00:00.000 +3000-01-01 00:00:00 2299-12-31 00:00:00.000 +accurateCastOrNull to Date: +1960-01-01 1970-01-01 +1800-01-01 1970-01-01 +3000-01-01 2149-06-06 +accurateCastOrNull to DateTime: +1960-01-01 1970-01-01 00:00:00 +1800-01-01 1970-01-01 00:00:00 +3000-01-01 2106-02-07 06:28:15 +accurateCastOrNull to DateTime (best_effort): +1960-01-01 1970-01-01 00:00:00 +1800-01-01 1970-01-01 00:00:00 +3000-01-01 2106-02-07 06:28:15 +accurateCastOrNull to DateTime (best_effort_us): +1960-01-01 1970-01-01 00:00:00 +1800-01-01 1970-01-01 00:00:00 +3000-01-01 2106-02-07 06:28:15 +accurateCastOrNull to DateTime64: +1800-01-01 1900-01-01 00:00:00.000 +3000-01-01 2299-12-31 00:00:00.000 +accurateCastOrNull to DateTime64 (best_effort): +1960-01-01 1960-01-01 00:00:00.000 +1800-01-01 1900-01-01 00:00:00.000 +3000-01-01 2299-12-31 00:00:00.000 +accurateCastOrNull to DateTime64 (best_effort_us): +1960-01-01 1960-01-01 00:00:00.000 +1800-01-01 1900-01-01 00:00:00.000 +3000-01-01 2299-12-31 00:00:00.000 diff --git a/tests/queries/0_stateless/03724_to_date_time_or_null_negative_arg_bug.sql b/tests/queries/0_stateless/03724_to_date_time_or_null_negative_arg_bug.sql new file mode 100644 index 000000000000..8653b99dfadd --- /dev/null +++ b/tests/queries/0_stateless/03724_to_date_time_or_null_negative_arg_bug.sql @@ -0,0 +1,58 @@ +set session_timezone='UTC'; +-- toDateOrNull: pre-epoch and far future dates +select 'toDateOrNull:'; +select '1960-01-01' as input, toDateOrNull('1960-01-01') as result; +select '1800-01-01' as input, toDateOrNull('1800-01-01') as result; +select '3000-01-01' as input, toDateOrNull('3000-01-01') as result; + +-- toDateTimeOrNull: pre-epoch and far future datetimes +select 'toDateTimeOrNull:'; +select '1960-01-01 00:00:00' as input, toDateTimeOrNull('1960-01-01 00:00:00') as result; +select '1800-01-01 00:00:00' as input, toDateTimeOrNull('1800-01-01 00:00:00') as result; +select '3000-01-01 00:00:00' as input, toDateTimeOrNull('3000-01-01 00:00:00') as result; + +-- toDateTime64OrNull: pre-epoch and far future datetimes +select 'toDateTime64OrNull:'; +select '1800-01-01 00:00:00' as input, toDateTime64OrNull('1800-01-01 00:00:00') as result; +select '3000-01-01 00:00:00' as input, toDateTime64OrNull('3000-01-01 00:00:00') as result; + +-- accurateCastOrNull to Date +select 'accurateCastOrNull to Date:'; +select '1960-01-01' as input, accurateCastOrNull('1960-01-01', 'Date') as result; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'Date') as result; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'Date') as result; + +-- accurateCastOrNull to DateTime +select 'accurateCastOrNull to DateTime:'; +select '1960-01-01' as input, accurateCastOrNull('1960-01-01', 'DateTime') as result; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'DateTime') as result; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'DateTime') as result; + +-- accurateCastOrNull to DateTime with best_effort mode +select 'accurateCastOrNull to DateTime (best_effort):'; +select '1960-01-01' as input, accurateCastOrNull('1960-01-01', 'DateTime') as result settings cast_string_to_date_time_mode='best_effort'; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'DateTime') as result settings cast_string_to_date_time_mode='best_effort'; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'DateTime') as result settings cast_string_to_date_time_mode='best_effort'; + +-- accurateCastOrNull to DateTime with best_effort_us mode +select 'accurateCastOrNull to DateTime (best_effort_us):'; +select '1960-01-01' as input, accurateCastOrNull('1960-01-01', 'DateTime') as result settings cast_string_to_date_time_mode='best_effort_us'; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'DateTime') as result settings cast_string_to_date_time_mode='best_effort_us'; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'DateTime') as result settings cast_string_to_date_time_mode='best_effort_us'; + +-- accurateCastOrNull to DateTime64 +select 'accurateCastOrNull to DateTime64:'; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'DateTime64') as result; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'DateTime64') as result; + +-- accurateCastOrNull to DateTime64 with best_effort mode +select 'accurateCastOrNull to DateTime64 (best_effort):'; +select '1960-01-01' as input, accurateCastOrNull('1960-01-01', 'DateTime64') as result settings cast_string_to_date_time_mode='best_effort'; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'DateTime64') as result settings cast_string_to_date_time_mode='best_effort'; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'DateTime64') as result settings cast_string_to_date_time_mode='best_effort'; + +-- accurateCastOrNull to DateTime64 with best_effort_us mode +select 'accurateCastOrNull to DateTime64 (best_effort_us):'; +select '1960-01-01' as input, accurateCastOrNull('1960-01-01', 'DateTime64') as result settings cast_string_to_date_time_mode='best_effort_us'; +select '1800-01-01' as input, accurateCastOrNull('1800-01-01', 'DateTime64') as result settings cast_string_to_date_time_mode='best_effort_us'; +select '3000-01-01' as input, accurateCastOrNull('3000-01-01', 'DateTime64') as result settings cast_string_to_date_time_mode='best_effort_us'; \ No newline at end of file