From 0a9d25950d952db80d0c68eca0b1391889d5d585 Mon Sep 17 00:00:00 2001 From: TCeason <33082201+TCeason@users.noreply.github.com> Date: Fri, 21 Jun 2024 21:18:50 +0800 Subject: [PATCH] fix(query): to_timestmap should always return err if parse err (#15850) * fix(query): to_timestmap should always return err if parse err If do not want to see err in to_timestamp, could use try_to_timesmtap * In to_timestmap(string, format) if not asign the string tz or format do not parse tz, the string use with current session timezone * fix fmt err * fix test --- src/query/functions/src/scalars/datetime.rs | 51 ++++++++++++++----- .../functions/02_0012_function_datetimes.test | 4 +- .../02_0012_function_datetimes_tz.test | 42 ++++++++++++--- 3 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/query/functions/src/scalars/datetime.rs b/src/query/functions/src/scalars/datetime.rs index 5138ad94527d..16aab16cbfad 100644 --- a/src/query/functions/src/scalars/datetime.rs +++ b/src/query/functions/src/scalars/datetime.rs @@ -20,11 +20,12 @@ use chrono::format::StrftimeItems; use chrono::prelude::*; use chrono::Datelike; use chrono::Days; -use chrono::ParseError; +use chrono::MappedLocalTime; use chrono::Utc; use chrono_tz::Tz; use databend_common_arrow::arrow::bitmap::Bitmap; use databend_common_arrow::arrow::temporal_conversions::EPOCH_DAYS_FROM_CE; +use databend_common_exception::ErrorCode; use databend_common_expression::error_to_null; use databend_common_expression::types::date::check_date; use databend_common_expression::types::date::date_to_string; @@ -256,7 +257,7 @@ fn string_to_format_timestmap( timestamp: &str, format: &str, ctx: &mut EvalContext, -) -> Result<(i64, bool), ParseError> { +) -> Result<(i64, bool), ErrorCode> { if format.is_empty() { return Ok((0, true)); } @@ -271,10 +272,10 @@ fn string_to_format_timestmap( let parse_tz = timezone_strftime .iter() .any(|&pattern| format.contains(pattern)); - let res = if ctx.func_ctx.parse_datetime_ignore_remainder { + if ctx.func_ctx.parse_datetime_ignore_remainder { let mut parsed = Parsed::new(); - if parse_and_remainder(&mut parsed, timestamp, StrftimeItems::new(format)).is_err() { - return Ok((0, true)); + if let Err(e) = parse_and_remainder(&mut parsed, timestamp, StrftimeItems::new(format)) { + return Err(ErrorCode::BadArguments(format!("{}", e))); } // Additional checks and adjustments for parsed timestamp if parsed.month.is_none() { @@ -293,27 +294,49 @@ fn string_to_format_timestmap( if parsed.second.is_none() { parsed.second = Some(0); } + + // fn handle_err(result: Result) -> Result { + // result.map_err(|e| ErrorCode::BadArguments(e.to_string())) + // } // Convert parsed timestamp to datetime or naive datetime based on parse_tz if parse_tz { parsed.offset.get_or_insert(0); parsed .to_datetime() - .map(|res| res.with_timezone(&ctx.func_ctx.tz.tz).timestamp_micros()) + .map(|res| (res.timestamp_micros(), false)) + .map_err(|err| ErrorCode::BadArguments(format!("{err}"))) + // handle_err(parsed.to_datetime()).map(|res| (res.timestamp_micros(), false)) } else { parsed .to_naive_datetime_with_offset(0) - .map(|res| res.and_utc().timestamp_micros()) + .map_err(|err| ErrorCode::BadArguments(format!("{err}"))) + .and_then(|res| match res.and_local_timezone(ctx.func_ctx.tz.tz) { + MappedLocalTime::Single(t) => Ok((t.timestamp_micros(), false)), + _ => Err(ErrorCode::BadArguments( + "The local time can not map to a single unique result".to_string(), + )), + }) + // handle_err(parsed.to_naive_datetime_with_offset(0)) + // .and_then(|res| match res.and_local_timezone(ctx.func_ctx.tz.tz) { + // MappedLocalTime::Single(t) => Ok((t.timestamp_micros(), false)), + // _ => Err(ErrorCode::BadArguments( + // "The local time can not map to a single unique result".to_string(), + // )), + // }) } } else if parse_tz { DateTime::parse_from_str(timestamp, format) - .map(|res| res.with_timezone(&ctx.func_ctx.tz.tz).timestamp_micros()) + .map(|res| (res.timestamp_micros(), false)) + .map_err(|err| ErrorCode::BadArguments(format!("{}", err))) } else { - NaiveDateTime::parse_from_str(timestamp, format).map(|res| res.and_utc().timestamp_micros()) - }; - - match res { - Ok(res) => Ok((res, false)), - Err(err) => Err(err), + NaiveDateTime::parse_from_str(timestamp, format) + .map_err(|err| ErrorCode::BadArguments(format!("{}", err))) + .and_then(|res| match res.and_local_timezone(ctx.func_ctx.tz.tz) { + MappedLocalTime::Single(t) => Ok((t.timestamp_micros(), false)), + _ => Err(ErrorCode::BadArguments( + "The local time can not map to a single unique result".to_string(), + )), + }) } } diff --git a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test index 80d8db0dd41a..5d82edc94527 100644 --- a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test +++ b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test @@ -1084,10 +1084,8 @@ select to_timestamp('2022年02月04日,8时58分59秒', ''); ---- NULL -query T +statement error 1006 select to_timestamp('10000-09-09 01:46:39', '%Y-%m-%d %H:%M:%S'); ----- -NULL statement error 1006 select to_timestamp('10000-09-09 01:46:39', '%s-%m-%d %H:%M:%S'); diff --git a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test index 41749f7ae867..0c7e38a58ce0 100644 --- a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test +++ b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test @@ -455,46 +455,70 @@ select to_date('1941-03-15 02:00:00'); statement ok set parse_datetime_ignore_remainder=1; +statement ok +set timezone='UTC'; + +query T +select to_timestamp('2022年02月04日,8时58分59秒,时区:+0800', '%Y年%m月%d日,%H时%M分%S秒,时区:%z'); +---- +2022-02-04 00:58:59.000000 + statement ok set timezone='Asia/Shanghai'; query T select to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H'); ---- -2022-02-04 16:00:00.000000 +2022-02-04 08:00:00.000000 -query T +statement error 1006 select to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H%z'); + +query T +select try_to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H%z'); ---- NULL query T select to_timestamp('2022年02月04日,8时58分59秒,时区:+0800', '%Y年%m月%d日,%H时'); ---- -2022-02-04 16:00:00.000000 +2022-02-04 08:00:00.000000 + +query T +select to_timestamp('2022年02月04日,8时58分59秒,时区:+0900', '%Y年%m月%d日,%H时'); +---- +2022-02-04 08:00:00.000000 query T select to_timestamp('2022年02月04日,8时58分59秒,时区:+0800', '%Y年%m月%d日,%H时%M分%S秒,时区:%z'); ---- 2022-02-04 08:58:59.000000 +query T +select to_timestamp('2022年02月04日,8时58分59秒,时区:+0900', '%Y年%m月%d日,%H时%M分%S秒,时区:%z'); +---- +2022-02-04 07:58:59.000000 + statement ok set timezone='America/Los_Angeles'; query T select to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H'); ---- -2022-02-04 00:00:00.000000 +2022-02-04 08:00:00.000000 -query T +statement error 1006 select to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H%z'); + +query T +select try_to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H%z'); ---- NULL query T select to_timestamp('2022年02月04日,8时58分59秒,时区:+0800', '%Y年%m月%d日,%H时'); ---- -2022-02-04 00:00:00.000000 +2022-02-04 08:00:00.000000 query T select to_timestamp('2022年02月04日,8时58分59秒,时区:+0800', '%Y年%m月%d日,%H时%M分%S秒,时区:%z'); @@ -509,8 +533,11 @@ select to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H' ---- 2022-02-04 08:00:00.000000 -query T +statement error 1006 select to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H%z'); + +query T +select try_to_timestamp('2022年02月04日,8时58分59秒', '%Y年%m月%d日,%H%z'); ---- NULL @@ -529,3 +556,4 @@ unset timezone; statement ok unset parse_datetime_ignore_remainder; +