This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new b9de0dc4e6d branch-4.2: [fix](function) Align timezone offset parsing 
across FE and BE #68979 (#70137)
b9de0dc4e6d is described below

commit b9de0dc4e6d974230f8d8c17db933c9afe215395
Author: morrySnow <[email protected]>
AuthorDate: Wed Nov 4 08:37:39 2026 +0800

    branch-4.2: [fix](function) Align timezone offset parsing across FE and BE 
#68979 (#70137)
    
    picked from master #68979
    
    Co-authored-by: Mryange <[email protected]>
---
 .../function/cast/cast_to_datetimev2_impl.hpp      |   39 +-
 be/src/util/timezone_utils.cpp                     |   35 +-
 be/src/util/timezone_utils.h                       |    3 +
 be/test/util/timezone_utils_test.cpp               |   27 +
 .../org/apache/doris/common/util/TimeUtils.java    |   43 +-
 .../apache/doris/common/util/TimeZoneParser.java   |  118 ++
 .../executable/DateTimeExtractAndTransform.java    |   38 +-
 .../trees/expressions/literal/DateLiteral.java     |   31 +-
 .../trees/expressions/literal/DateTimeLiteral.java |    6 +-
 .../expressions/literal/StringLikeLiteral.java     |   24 -
 .../literal/format/DateTimeChecker.java            |   17 +-
 .../doris/common/util/TimeZoneParserTest.java      |  124 ++
 .../literal/TimeStampNsLiteralTest.java            |    2 +
 .../datetimev2/test_timezone_parser_contract.out   | 1504 ++++++++++++++++++++
 .../test_timezone_parser_contract.groovy           |  129 ++
 .../fold_constant/fe_constant_cast_to_date.groovy  |    2 +-
 .../fe_constant_cast_to_datetime.groovy            |    2 +-
 17 files changed, 2011 insertions(+), 133 deletions(-)

diff --git a/be/src/exprs/function/cast/cast_to_datetimev2_impl.hpp 
b/be/src/exprs/function/cast/cast_to_datetimev2_impl.hpp
index 2885e64b0c9..32a7ed948ef 100644
--- a/be/src/exprs/function/cast/cast_to_datetimev2_impl.hpp
+++ b/be/src/exprs/function/cast/cast_to_datetimev2_impl.hpp
@@ -706,7 +706,7 @@ FRAC:
                                          "invalid hour offset '{}'", 
std::string {ptr, end});
             }
             SET_PARAMS_RET_FALSE_IFN(
-                    part[0] < (type == DataTimeCastEnumType::TIMESTAMP_TZ ? 
24U : 15U),
+                    part[0] < (type == DataTimeCastEnumType::TIMESTAMP_TZ ? 
19U : 15U),
                     "invalid hour offset '{}'", part[0]);
             if (ptr < end) {
                 if (*ptr == ':') {
@@ -732,20 +732,19 @@ FRAC:
                                              "invalid minute offset '{}'", 
part[1]);
                 }
             }
-            if constexpr (type == DataTimeCastEnumType::TIMESTAMP_TZ) {
-                // Wire offsets include historical zones outside the session 
fixed-zone range.
-                // Use the exact offset even when it has no seconds (for 
example, Guam's -14:21).
-                const auto offset = static_cast<int>(part[0] * 3600 + part[1] 
* 60 + second_offset);
-                parsed_tz = cctz::fixed_time_zone(cctz::seconds(sign == '-' ? 
-offset : offset));
-            } else {
+            if constexpr (type == DataTimeCastEnumType::DATE_TIME) {
                 SET_PARAMS_RET_FALSE_IFN(part[0] != 14 || part[1] == 0,
                                          "invalid timezone offset '{}'",
                                          combine_tz_offset(sign, part[0], 
part[1]));
-                SET_PARAMS_RET_FALSE_IFN(
-                        TimezoneUtils::find_cctz_time_zone(
-                                combine_tz_offset(sign, part[0], part[1]), 
parsed_tz),
-                        "invalid timezone offset '{}'", 
combine_tz_offset(sign, part[0], part[1]));
             }
+            if constexpr (type == DataTimeCastEnumType::TIMESTAMP_TZ) {
+                SET_PARAMS_RET_FALSE_IFN(part[0] != 18 || (part[1] == 0 && 
second_offset == 0),
+                                         "invalid timezone offset '{}'",
+                                         combine_tz_offset(sign, part[0], 
part[1]));
+            }
+            const auto offset = static_cast<int>(part[0] * 3600 + part[1] * 60 
+ second_offset);
+            parsed_tz =
+                    TimezoneUtils::create_fixed_offset_time_zone(sign == '-' ? 
-offset : offset);
         } else {
             // timezone name
             const auto* start = ptr;
@@ -992,7 +991,7 @@ inline bool 
CastToDatetimeV2::from_string_non_strict_mode_internal(
                 PROPAGATE_FALSE((consume_digit<UInt32, 2>(ptr, end, 
hour_offset)));
             }
             SET_PARAMS_RET_FALSE_IFN(
-                    hour_offset < (type == DataTimeCastEnumType::TIMESTAMP_TZ 
? 24U : 15U),
+                    hour_offset < (type == DataTimeCastEnumType::TIMESTAMP_TZ 
? 19U : 15U),
                     "invalid hour offset '{}'", hour_offset);
             if (ptr < end) {
                 if (*ptr == ':') {
@@ -1015,21 +1014,21 @@ inline bool 
CastToDatetimeV2::from_string_non_strict_mode_internal(
                             "invalid minute offset {}", minute_offset);
                 }
             }
-            if constexpr (type == DataTimeCastEnumType::TIMESTAMP_TZ) {
-                // Match strict parsing: a serialized historical offset is not 
a session setting.
-                const auto offset =
-                        static_cast<int>(hour_offset * 3600 + minute_offset * 
60 + second_offset);
-                parsed_tz = cctz::fixed_time_zone(cctz::seconds(sign == '-' ? 
-offset : offset));
-            } else {
+            if constexpr (type == DataTimeCastEnumType::DATE_TIME) {
                 SET_PARAMS_RET_FALSE_IFN(hour_offset != 14 || minute_offset == 
0,
                                          "invalid timezone offset '{}'",
                                          combine_tz_offset(sign, hour_offset, 
minute_offset));
+            }
+            if constexpr (type == DataTimeCastEnumType::TIMESTAMP_TZ) {
                 SET_PARAMS_RET_FALSE_IFN(
-                        TimezoneUtils::find_cctz_time_zone(
-                                combine_tz_offset(sign, hour_offset, 
minute_offset), parsed_tz),
+                        hour_offset != 18 || (minute_offset == 0 && 
second_offset == 0),
                         "invalid timezone offset '{}'",
                         combine_tz_offset(sign, hour_offset, minute_offset));
             }
+            const auto offset =
+                    static_cast<int>(hour_offset * 3600 + minute_offset * 60 + 
second_offset);
+            parsed_tz =
+                    TimezoneUtils::create_fixed_offset_time_zone(sign == '-' ? 
-offset : offset);
         } else {
             // timezone name
             const auto* start = ptr;
diff --git a/be/src/util/timezone_utils.cpp b/be/src/util/timezone_utils.cpp
index 0a11c185445..04a98204326 100644
--- a/be/src/util/timezone_utils.cpp
+++ b/be/src/util/timezone_utils.cpp
@@ -51,17 +51,27 @@ namespace fs = std::filesystem;
 namespace doris {
 
 using ZoneList = std::unordered_map<std::string, cctz::time_zone>;
+using FixedZoneList = std::unordered_map<int32_t, cctz::time_zone>;
 
 RE2 time_zone_offset_format_reg(R"(^[+-]{1}\d{2}\:\d{2}$)"); // visiting is 
thread-safe
 
 // for ut, make it never nullptr.
 std::unique_ptr<ZoneList> lower_zone_cache_ = std::make_unique<ZoneList>();
+std::unique_ptr<FixedZoneList> fixed_zone_cache_ = 
std::make_unique<FixedZoneList>();
 
 const std::string TimezoneUtils::default_time_zone = "+08:00";
 static const char* tzdir = "/usr/share/zoneinfo"; // default value, may change 
by TZDIR env var
 
+cctz::time_zone TimezoneUtils::create_fixed_offset_time_zone(int32_t 
offset_seconds) {
+    if (auto it = fixed_zone_cache_->find(offset_seconds); it != 
fixed_zone_cache_->end()) {
+        return it->second;
+    }
+    return cctz::fixed_time_zone(cctz::seconds(offset_seconds));
+}
+
 void TimezoneUtils::clear_timezone_caches() {
     lower_zone_cache_->clear();
+    fixed_zone_cache_->clear();
 }
 size_t TimezoneUtils::cache_size() {
     return lower_zone_cache_->size();
@@ -131,28 +141,33 @@ void TimezoneUtils::load_offsets_to_cache() {
     static constexpr int supported_minutes[] = {0, 30, 45};
     for (int hour = -12; hour <= +14; hour++) {
         for (int minute : supported_minutes) {
+            if ((hour == -12 || hour == 14) && minute != 0) {
+                continue;
+            }
             char min_str[3];
             snprintf(min_str, sizeof(min_str), "%02d", minute);
             std::string offset_str = (hour >= 0 ? "+" : "") + 
to_hour_string(hour) + ':' + min_str;
             cctz::time_zone result;
-            // Rejected endpoint minutes must not be cached as the default UTC 
zone.
-            if (!parse_tz_offset_string(offset_str, result)) {
-                continue;
-            }
+            CHECK(parse_tz_offset_string(offset_str, result));
             lower_zone_cache_->emplace(offset_str, result);
+            const int offset_seconds = hour * 3600 + (hour < 0 ? -minute : 
minute) * 60;
+            fixed_zone_cache_->emplace(offset_seconds, result);
         }
     }
     // -00 for hour is also valid
     std::string offset_str = "-00:00";
     cctz::time_zone result;
-    parse_tz_offset_string(offset_str, result);
+    CHECK(parse_tz_offset_string(offset_str, result));
     lower_zone_cache_->emplace(offset_str, result);
+    fixed_zone_cache_->emplace(0, result);
     offset_str = "-00:30";
-    parse_tz_offset_string(offset_str, result);
+    CHECK(parse_tz_offset_string(offset_str, result));
     lower_zone_cache_->emplace(offset_str, result);
+    fixed_zone_cache_->emplace(-30 * 60, result);
     offset_str = "-00:45";
-    parse_tz_offset_string(offset_str, result);
+    CHECK(parse_tz_offset_string(offset_str, result));
     lower_zone_cache_->emplace(offset_str, result);
+    fixed_zone_cache_->emplace(-45 * 60, result);
 }
 
 bool TimezoneUtils::find_cctz_time_zone(const std::string& timezone, 
cctz::time_zone& ctz) {
@@ -233,8 +248,8 @@ static bool normalize_offset_string(const std::string& 
timezone, bool allow_hour
         hour = std::stoi(std::string(rest));
     }
 
-    // Session fixed offsets include the endpoints only when their minutes are 
zero.
-    if (minute >= 60 || hour * 60 + minute > (positive ? 14 : 12) * 60) {
+    const int limit = positive ? 14 : 12;
+    if (hour > limit || (hour == limit && minute != 0) || minute >= 60) {
         return false;
     }
 
@@ -283,7 +298,7 @@ bool TimezoneUtils::parse_tz_offset_string(const 
std::string& timezone, cctz::ti
         const int minute = std::stoi(value.substr(4, 2).as_string());
         int offset = hour * 60 * 60 + minute * 60;
         offset *= positive ? 1 : -1;
-        ctz = cctz::fixed_time_zone(cctz::seconds(offset));
+        ctz = create_fixed_offset_time_zone(offset);
         return true;
     }
     return false;
diff --git a/be/src/util/timezone_utils.h b/be/src/util/timezone_utils.h
index 1f37c7e2e9e..99fcf9b2afa 100644
--- a/be/src/util/timezone_utils.h
+++ b/be/src/util/timezone_utils.h
@@ -37,6 +37,9 @@ public:
 
     static bool find_cctz_time_zone(const std::string& timezone, 
cctz::time_zone& ctz);
 
+    // The caller validates the offset according to its parsing policy.
+    static cctz::time_zone create_fixed_offset_time_zone(int32_t 
offset_seconds);
+
     static bool try_get_fixed_offset_seconds(const cctz::time_zone& timezone,
                                              int32_t* offset_seconds);
 
diff --git a/be/test/util/timezone_utils_test.cpp 
b/be/test/util/timezone_utils_test.cpp
index e2c29203342..6983f74a907 100644
--- a/be/test/util/timezone_utils_test.cpp
+++ b/be/test/util/timezone_utils_test.cpp
@@ -183,6 +183,33 @@ TEST(TimezoneUtilsTest, FindTimezone) {
     EXPECT_FALSE(TimezoneUtils::find_cctz_time_zone(tzname, result));
 }
 
+TEST(TimezoneUtilsTest, OffsetBoundariesWithAndWithoutCache) {
+    for (bool preload : {false, true}) {
+        TimezoneUtils::clear_timezone_caches();
+        if (preload) {
+            TimezoneUtils::load_timezones_to_cache();
+        }
+        for (const auto* prefix : {"", "UTC", "GMT", "utc", "gmt"}) {
+            for (const auto* offset : {"+14:01", "+14:30", "+14:45", "-12:01", 
"-12:30", "-12:45",
+                                       "+15:00", "-13:00", "+08:60"}) {
+                const std::string input = std::string(prefix) + offset;
+                cctz::time_zone result = 
cctz::fixed_time_zone(cctz::seconds(3600));
+                EXPECT_FALSE(TimezoneUtils::find_cctz_time_zone(input, 
result)) << input;
+                EXPECT_EQ(
+                        result.lookup(cctz::convert(cctz::civil_second(2024, 
1, 1), result)).offset,
+                        3600)
+                        << input;
+            }
+            for (const auto* offset : {"+14:00", "-12:00", "+08:01", "+08:15", 
"-00:30"}) {
+                cctz::time_zone result;
+                EXPECT_TRUE(
+                        TimezoneUtils::find_cctz_time_zone(std::string(prefix) 
+ offset, result));
+            }
+        }
+    }
+    TimezoneUtils::load_timezones_to_cache();
+}
+
 TEST(TimezoneUtilsTest, TryGetFixedOffsetSeconds) {
     TimezoneUtils::load_timezones_to_cache();
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
index c808c17b52a..c59623bfe4e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
@@ -48,7 +48,6 @@ import java.util.Date;
 import java.util.Map;
 import java.util.TimeZone;
 import java.util.TreeMap;
-import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -108,7 +107,6 @@ public class TimeUtils {
     }
 
     private static final Logger LOG = LogManager.getLogger(TimeUtils.class);
-    private static final Pattern TIMEZONE_OFFSET_FORMAT_REG = 
Pattern.compile("^[+-]?\\d{1,2}:\\d{2}$");
 
     static {
         Map<String, String> timeZoneMap = new 
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
@@ -329,42 +327,18 @@ public class TimeUtils {
 
     // Check if the time zone_value is valid
     public static String checkTimeZoneValidAndStandardize(String value) throws 
DdlException {
-        Function<String, String> standardizeValue = s -> {
-            boolean positive = s.charAt(0) != '-';
-            String[] parts = s.replaceAll("[+-]", "").split(":");
-            return (positive ? "+" : "-") + String.format("%02d:%02d",
-                    Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
-        };
-
         try {
-            if (value == null) {
-                
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TIME_ZONE, "null");
-            }
-            // match offset type, such as +08:00, -07:00
-            Matcher matcher = TIMEZONE_OFFSET_FORMAT_REG.matcher(value);
-            // it supports offset and region timezone type, and short zone ids
-            boolean match = matcher.matches();
-            if (!value.contains("/") && !timeZoneAliasMap.containsKey(value) 
&& !match) {
-                if ((value.startsWith("GMT") || value.startsWith("UTC"))
-                        && 
TIMEZONE_OFFSET_FORMAT_REG.matcher(value.substring(3)).matches()) {
-                    value = value.substring(0, 3) + 
standardizeValue.apply(value.substring(3));
-                } else {
-                    
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TIME_ZONE, value);
-                }
+            String input = value;
+            if (input != null && !input.isEmpty() && 
Character.isDigit(input.charAt(0))) {
+                input = "+" + input;
             }
-            if (match) {
-                value = standardizeValue.apply(value);
-
-                // timezone offsets around the world extended from -12:00 to 
+14:00
-                int tz = Integer.parseInt(value.substring(1, 3)) * 100 + 
Integer.parseInt(value.substring(4, 6));
-                if (value.charAt(0) == '-' && tz > 1200) {
-                    
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TIME_ZONE, value);
-                } else if (value.charAt(0) == '+' && tz > 1400) {
-                    
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TIME_ZONE, value);
+            String normalized = TimeZoneParser.normalize(input, 
TimeZoneParser.Usage.SESSION);
+            for (String alias : timeZoneAliasMap.keySet()) {
+                if (alias.equalsIgnoreCase(value)) {
+                    return alias;
                 }
             }
-            ZoneId.of(value, timeZoneAliasMap);
-            return value;
+            return normalized;
         } catch (DateTimeException ex) {
             ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TIME_ZONE, 
value);
         }
@@ -433,4 +407,3 @@ public class TimeUtils {
         return convertToDateV2(dateTime.getYear(), dateTime.getMonthValue(), 
dateTime.getDayOfMonth());
     }
 }
-
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeZoneParser.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeZoneParser.java
new file mode 100644
index 00000000000..4e17490522e
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeZoneParser.java
@@ -0,0 +1,118 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.util;
+
+import java.time.DateTimeException;
+import java.time.ZoneId;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Doris timezone syntax, independent of the Java timezone database's input 
grammar. */
+public final class TimeZoneParser {
+    public enum Usage {
+        SESSION, DATETIME, TIMESTAMPTZ
+    }
+
+    private static final Pattern OFFSET = 
Pattern.compile("^([+-])([0-9]{1,2})(?::([0-9]{2}))?$");
+    private static final Pattern TEXT_OFFSET = Pattern.compile(
+            "^([+-])([0-9]{1,2})(?::?([0-9]{2}))?(?::([0-9]{2}))?$");
+    private static final Map<String, String> REGION_NAMES;
+
+    static {
+        Map<String, String> names = new HashMap<>();
+        for (String region : ZoneId.getAvailableZoneIds()) {
+            names.put(region.toLowerCase(Locale.ROOT), region);
+        }
+        REGION_NAMES = Collections.unmodifiableMap(names);
+    }
+
+    private TimeZoneParser() {
+    }
+
+    public static String normalize(String value, Usage usage) {
+        if (value == null || value.isEmpty()) {
+            throw new DateTimeException("Empty timezone");
+        }
+        String name = findAlias(value);
+        if (name != null) {
+            return name;
+        }
+        String region = findRegion(value, usage);
+        if (region != null) {
+            return region;
+        }
+        String upper = value.toUpperCase(Locale.ROOT);
+        // Java's standard UTC names are not present in SHORT_IDS.
+        if (upper.equals("UT") || upper.equals("Z")) {
+            return "UTC";
+        }
+        boolean prefixed = upper.startsWith("UTC") || upper.startsWith("GMT");
+        String offset = prefixed ? value.substring(3) : value;
+        Matcher matcher = (usage == Usage.SESSION || prefixed ? OFFSET : 
TEXT_OFFSET).matcher(offset);
+        if (!matcher.matches()
+                || (usage == Usage.SESSION && !prefixed && matcher.group(3) == 
null)) {
+            throw new DateTimeException("Invalid timezone: " + value);
+        }
+        boolean negative = "-".equals(matcher.group(1));
+        int hour = Integer.parseInt(matcher.group(2));
+        int minute = matcher.group(3) == null ? 0 : 
Integer.parseInt(matcher.group(3));
+        int second = !prefixed && usage != Usage.SESSION && matcher.group(4) 
!= null
+                ? Integer.parseInt(matcher.group(4)) : 0;
+        // Prefixed offsets have the same contract as session offsets in BE.
+        Usage policy = prefixed ? Usage.SESSION : usage;
+        int limit = policy == Usage.SESSION ? (negative ? 12 : 14)
+                : policy == Usage.DATETIME ? 14 : 18;
+        if (hour > limit || minute >= 60 || second >= 60
+                || (policy != Usage.TIMESTAMPTZ && hour == limit && minute != 
0)
+                || (policy == Usage.TIMESTAMPTZ && hour == limit && (minute != 
0 || second != 0))
+                || (policy == Usage.DATETIME
+                        && (matcher.group(4) != null || (minute != 0 && minute 
!= 30 && minute != 45)))) {
+            throw new DateTimeException("Invalid timezone offset: " + value);
+        }
+        String normalized = String.format(Locale.ROOT, "%s%02d:%02d", negative 
? "-" : "+", hour, minute);
+        if (second != 0) {
+            normalized += String.format(Locale.ROOT, ":%02d", second);
+        }
+        return prefixed ? upper.substring(0, 3) + normalized : normalized;
+    }
+
+    private static String findAlias(String value) {
+        for (String alias : TimeUtils.timeZoneAliasMap.keySet()) {
+            if (alias.equalsIgnoreCase(value)) {
+                return TimeUtils.getCanonicalTimeZone(alias);
+            }
+        }
+        return null;
+    }
+
+    private static String findRegion(String value, Usage usage) {
+        String region = REGION_NAMES.get(value.toLowerCase(Locale.ROOT));
+        if (usage == Usage.SESSION && !value.equals(region)) {
+            return null;
+        }
+        return region;
+    }
+
+    public static ZoneId parse(String value, Usage usage) {
+        return ZoneId.of(normalize(value, usage));
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
index 7a1e2a32f6e..001a15f509d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.trees.expressions.functions.executable;
 
+import org.apache.doris.common.util.TimeZoneParser;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import 
org.apache.doris.nereids.rules.expression.rules.SupportJavaDateFormatter;
 import org.apache.doris.nereids.trees.expressions.ExecFunction;
@@ -67,9 +68,7 @@ import java.time.YearMonth;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeFormatterBuilder;
 import java.time.format.DateTimeParseException;
-import java.time.format.ResolverStyle;
 import java.time.format.TextStyle;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.WeekFields;
@@ -1102,44 +1101,13 @@ public class DateTimeExtractAndTransform {
 
     private static LocalDateTime convertTz(LocalDateTime datetime, 
StringLikeLiteral fromTz,
             StringLikeLiteral toTz) {
-        // Validate timezone offset ranges before parsing
-        validateTimezoneOffset(fromTz.getStringValue());
-        validateTimezoneOffset(toTz.getStringValue());
-
-        DateTimeFormatter zoneFormatter = new DateTimeFormatterBuilder()
-                .parseCaseInsensitive()
-                .appendZoneOrOffsetId()
-                .toFormatter()
-                .withResolverStyle(ResolverStyle.STRICT);
-        ZoneId fromZone = 
ZoneId.from(zoneFormatter.parse(fromTz.getStringValue()));
-        ZoneId toZone = 
ZoneId.from(zoneFormatter.parse(toTz.getStringValue()));
+        ZoneId fromZone = TimeZoneParser.parse(fromTz.getStringValue(), 
TimeZoneParser.Usage.SESSION);
+        ZoneId toZone = TimeZoneParser.parse(toTz.getStringValue(), 
TimeZoneParser.Usage.SESSION);
 
         Instant instant = DateTimeLiteral.convertLocalToInstant(datetime, 
fromZone);
         return LocalDateTime.ofInstant(instant, toZone);
     }
 
-    private static void validateTimezoneOffset(String timezone) {
-        // Pattern to match offset format like +HH:MM or -HH:MM
-        if (timezone.matches("^[+-]\\d{2}:\\d{2}$")) {
-            boolean positive = timezone.charAt(0) == '+';
-            int hour = Integer.parseInt(timezone.substring(1, 3));
-            int minute = Integer.parseInt(timezone.substring(4, 6));
-
-            if (!positive && hour > 12) {
-                throw new AnalysisException("Invalid timezone offset: " + 
timezone
-                        + ". Timezone offsets must be between -12:00 and 
+14:00");
-            } else if (positive && hour > 14) {
-                throw new AnalysisException("Invalid timezone offset: " + 
timezone
-                        + ". Timezone offsets must be between -12:00 and 
+14:00");
-            }
-
-            if (minute != 0 && minute != 15 && minute != 30 && minute != 45) {
-                throw new AnalysisException("Invalid timezone offset: " + 
timezone
-                        + ". Minute part should be 00, 15, 30, or 45");
-            }
-        }
-    }
-
     @ExecFunction(name = "weekday")
     public static Expression weekDay(DateV2Literal date) {
         return new TinyIntLiteral((byte) 
((date.toJavaDateType().getDayOfWeek().getValue() + 6) % 7));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
index 08f43907d46..4ae683c042c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.expressions.literal;
 
 import org.apache.doris.analysis.LiteralExpr;
 import org.apache.doris.catalog.Type;
+import org.apache.doris.common.util.TimeZoneParser;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.exceptions.CastException;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -155,6 +156,11 @@ public class DateLiteral extends Literal implements 
ComparableLiteral {
     }
 
     private static Result<String, AnalysisException> normalize(String s, int 
maxFractionDigits) {
+        return normalize(s, maxFractionDigits, TimeZoneParser.Usage.DATETIME);
+    }
+
+    private static Result<String, AnalysisException> normalize(String s, int 
maxFractionDigits,
+            TimeZoneParser.Usage usage) {
         // merge consecutive space
         if (s.contains("  ")) {
             s = s.replaceAll(" +", " ");
@@ -276,7 +282,10 @@ public class DateLiteral extends Literal implements 
ComparableLiteral {
         }
 
         // trim use to remove any blank before zone id or zone offset
-        sb.append(s.substring(i).trim());
+        String zone = s.substring(i).trim();
+        if (!zone.isEmpty()) {
+            sb.append(TimeZoneParser.normalize(zone, usage));
+        }
 
         return Result.ok(sb.toString());
     }
@@ -304,10 +313,15 @@ public class DateLiteral extends Literal implements 
ComparableLiteral {
 
     /** parseDateTime */
     public static Result<TemporalAccessor, AnalysisException> 
parseDateTime(String s) {
-        return parseDateTime(s, 7);
+        return parseDateTime(s, 7, TimeZoneParser.Usage.DATETIME);
     }
 
     static Result<TemporalAccessor, AnalysisException> parseDateTime(String s, 
int maxFractionDigits) {
+        return parseDateTime(s, maxFractionDigits, 
TimeZoneParser.Usage.DATETIME);
+    }
+
+    static Result<TemporalAccessor, AnalysisException> parseDateTime(String s, 
int maxFractionDigits,
+            TimeZoneParser.Usage usage) {
         String originalString = s;
         try {
             // fast parse '2022-01-01'
@@ -340,13 +354,22 @@ public class DateLiteral extends Literal implements 
ComparableLiteral {
             }
             if (!containsPunctuation) {
                 s = normalizeBasic(s);
+                boolean hasTimeSeparator = s.length() > 8 && s.charAt(8) == 
'T';
+                int zoneStart = 0;
+                while (zoneStart < s.length() && 
(Character.isDigit(s.charAt(zoneStart))
+                        || (hasTimeSeparator && zoneStart == 8) || 
s.charAt(zoneStart) == '.')) {
+                    zoneStart++;
+                }
+                if (zoneStart < s.length() && s.charAt(zoneStart) != ' ') {
+                    s = s.substring(0, zoneStart) + 
TimeZoneParser.normalize(s.substring(zoneStart), usage);
+                }
                 boolean parseNanoseconds = maxFractionDigits > 7;
                 if (parseNanoseconds) {
                     s = DateUtils.truncateFractionalSecondForJavaParser(s);
                 }
                 // mysql reject "20200219 010101" "200219 010101", can't use ' 
' spilt basic date time.
 
-                if (!s.contains("T")) {
+                if (!hasTimeSeparator) {
                     dateTime = (parseNanoseconds
                             ? 
DateTimeFormatterUtils.TIMESTAMP_NS_BASIC_FORMATTER_WITHOUT_T
                             : 
DateTimeFormatterUtils.BASIC_FORMATTER_WITHOUT_T).parse(s);
@@ -358,7 +381,7 @@ public class DateLiteral extends Literal implements 
ComparableLiteral {
                 return Result.ok(dateTime);
             }
 
-            Result<String, AnalysisException> normalizeResult = normalize(s, 
maxFractionDigits);
+            Result<String, AnalysisException> normalizeResult = normalize(s, 
maxFractionDigits, usage);
             if (normalizeResult.isError()) {
                 return normalizeResult.cast();
             }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
index 4819de8e608..1b1a7aaf73e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.expressions.literal;
 
 import org.apache.doris.analysis.LiteralExpr;
 import org.apache.doris.catalog.Type;
+import org.apache.doris.common.util.TimeZoneParser;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.exceptions.CastException;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -222,7 +223,10 @@ public class DateTimeLiteral extends DateLiteral {
     }
 
     protected void init(String s) throws AnalysisException {
-        TemporalAccessor temporal = parseDateTime(s).get();
+        TemporalAccessor temporal = parseDateTime(s, 7,
+                this.dataType instanceof TimeStampTzType
+                        ? TimeZoneParser.Usage.TIMESTAMPTZ
+                        : TimeZoneParser.Usage.DATETIME).get();
 
         year = DateUtils.getOrDefault(temporal, ChronoField.YEAR);
         month = DateUtils.getOrDefault(temporal, ChronoField.MONTH_OF_YEAR);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
index 3ea2506e487..b37e3baf920 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
@@ -303,9 +303,6 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
                         ? strictMatcher.group("fraction2") : 
strictMatcher.group("fraction1");
                 tz = strictMatcher.group("tz");
             }
-            if (tz != null && tz.equalsIgnoreCase("CST")) {
-                tz = "+08:00";
-            }
             return getDateTimeLiteral(year, month, date, hour, minute, second, 
fraction, tz, targetType);
         } else if (!strictCast) {
             Matcher unStrictMatcher = dateUnStrictPattern.matcher(value);
@@ -318,9 +315,6 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
                 second = unStrictMatcher.group("second");
                 fraction = unStrictMatcher.group("fraction");
                 tz = unStrictMatcher.group("tz");
-                if (tz != null && tz.equalsIgnoreCase("CST")) {
-                    tz = "+08:00";
-                }
                 return getDateTimeLiteral(year, month, date, hour, minute, 
second, fraction, tz, targetType);
             }
         }
@@ -339,17 +333,6 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
             String second, String fraction, String tz, DataType targetType) {
         String year4 = year2ToYear4(year);
         tz = tz == null ? "" : tz;
-        if ((tz.startsWith("+") || tz.startsWith("-")) && !tz.contains(":")) {
-            if (tz.length() == 2) {
-                tz = String.format("%s0%s:00", tz.charAt(0), tz.charAt(1));
-            } else if (tz.length() == 3) {
-                tz = String.format("%s%s:00", tz.charAt(0), tz.substring(1, 
3));
-            } else if (tz.length() == 4) {
-                tz = String.format("%s0%s:%s", tz.charAt(0), tz.charAt(1), 
tz.substring(2, 4));
-            } else {
-                tz = String.format("%s%s:%s", tz.charAt(0), tz.substring(1, 
3), tz.substring(3, 5));
-            }
-        }
         hour = hour == null ? "00" : hour;
         minute = minute == null ? "00" : minute;
         second = second == null ? "00" : second;
@@ -357,13 +340,6 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
         if (fraction.endsWith(".")) {
             fraction = fraction.substring(0, fraction.length() - 1);
         }
-        if (tz.contains(":")) {
-            int hourOffset = Integer.parseInt(tz.substring(1, 
tz.indexOf(":")));
-            int minuteOffset = Integer.parseInt(tz.substring(tz.indexOf(":") + 
1));
-            if (hourOffset > 14 || hourOffset == 14 && minuteOffset > 0) {
-                throw new CastException("Time zone offset couldn't be larger 
than 14:00");
-            }
-        }
         String format = String.format("%s-%s-%sT%s:%s:%s%s%s", year4, month, 
date, hour, minute, second, fraction, tz);
         if (targetType.isDateType()) {
             try {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java
index 6336c746491..b8247bff98a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java
@@ -109,7 +109,8 @@ public class DateTimeChecker extends FormatChecker {
         // Find the separator between date and time parts (' ' or 'T')
         int timeSeparatorIndex = -1;
         for (int i = 0; i < str.length(); i++) {
-            if (str.charAt(i) == ' ' || str.charAt(i) == 'T') {
+            // A date ends within the first ten characters; later 'T' belongs 
to a zone name.
+            if (str.charAt(i) == ' ' || (str.charAt(i) == 'T' && i <= 10)) {
                 timeSeparatorIndex = i;
                 break;
             }
@@ -160,6 +161,18 @@ public class DateTimeChecker extends FormatChecker {
     }
 
     private FormatChecker timeZone() {
+        return or(namedTimeZone(), offsetTimeZone());
+    }
+
+    private FormatChecker namedTimeZone() {
+        return and(
+                atLeast(1, Character::isLetter),
+                atLeast(0, c -> Character.isLetterOrDigit(c) || c == '/' || c 
== '_'
+                        || c == '-' || c == '+')
+        );
+    }
+
+    private FormatChecker offsetTimeZone() {
         // Z or +08:00 or UTC-01:00
         return and(
             // timezone: Europe/London, America/New_York
@@ -167,7 +180,7 @@ public class DateTimeChecker extends FormatChecker {
             option(
                 and(
                     chars(c -> c == '+' || c == '-'),
-                    digit(1, 2),
+                    digit(1, 4),
                     option(
                         and(
                             ch(':'),
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeZoneParserTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeZoneParserTest.java
new file mode 100644
index 00000000000..91d58c3dd36
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeZoneParserTest.java
@@ -0,0 +1,124 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.common.util;
+
+import org.apache.doris.common.DdlException;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral;
+import 
org.apache.doris.nereids.trees.expressions.literal.format.DateTimeChecker;
+import org.apache.doris.nereids.types.TimeStampTzType;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.time.DateTimeException;
+
+class TimeZoneParserTest {
+    @Test
+    void sessionBoundaries() {
+        for (String prefix : new String[] {"", "UTC", "GMT", "utc", "gmt"}) {
+            for (String offset : new String[] {"+14:01", "+14:30", "+14:45", 
"-12:01", "-12:30",
+                    "-12:45", "+15:00", "-13:00", "+08:60"}) {
+                Assertions.assertThrows(DateTimeException.class,
+                        () -> TimeZoneParser.parse(prefix + offset, 
TimeZoneParser.Usage.SESSION), prefix + offset);
+            }
+            for (String offset : new String[] {"+14:00", "-12:00", "+08:01", 
"+08:15", "-00:30"}) {
+                Assertions.assertNotNull(TimeZoneParser.parse(prefix + offset, 
TimeZoneParser.Usage.SESSION));
+            }
+        }
+    }
+
+    @Test
+    void offsetSyntaxCannotBypassValidation() {
+        for (TimeZoneParser.Usage usage : TimeZoneParser.Usage.values()) {
+            for (String value : new String[] {"UTC8", "GMT8:30", "UTC0", 
"GMT0:00"}) {
+                Assertions.assertThrows(DateTimeException.class,
+                        () -> TimeZoneParser.normalize(value, usage), usage + 
": " + value);
+            }
+        }
+        for (String value : new String[] {"+00:00:00", "-00:00:00", "+0000", 
"+00"}) {
+            Assertions.assertThrows(DateTimeException.class,
+                    () -> TimeZoneParser.normalize(value, 
TimeZoneParser.Usage.SESSION), value);
+        }
+        Assertions.assertThrows(DateTimeException.class,
+                () -> TimeZoneParser.normalize("+00:00:00", 
TimeZoneParser.Usage.DATETIME));
+        Assertions.assertEquals("+00:00",
+                TimeZoneParser.normalize("+00:00:00", 
TimeZoneParser.Usage.TIMESTAMPTZ));
+        for (String value : new String[] {"UT", "ut", "Z", "z"}) {
+            Assertions.assertEquals("UTC", TimeZoneParser.normalize(value, 
TimeZoneParser.Usage.DATETIME));
+        }
+    }
+
+    @Test
+    void namesAndSyntax() throws Exception {
+        Assertions.assertEquals("UTC", 
TimeUtils.checkTimeZoneValidAndStandardize("utc"));
+        Assertions.assertThrows(DdlException.class,
+                () -> 
TimeUtils.checkTimeZoneValidAndStandardize("asia/shanghai"));
+        for (TimeZoneParser.Usage usage : new TimeZoneParser.Usage[] {
+                TimeZoneParser.Usage.DATETIME, 
TimeZoneParser.Usage.TIMESTAMPTZ}) {
+            Assertions.assertEquals("Asia/Shanghai", 
TimeZoneParser.normalize("asia/shanghai", usage));
+            Assertions.assertEquals("America/Los_Angeles", 
TimeZoneParser.normalize("america/los_angeLES", usage));
+            Assertions.assertEquals("Europe/Paris", 
TimeZoneParser.normalize("Europe/pARIS", usage));
+        }
+        for (String value : new String[] {"+8", "+08", "+0800", "+8:00"}) {
+            Assertions.assertEquals("+08:00", TimeZoneParser.normalize(value, 
TimeZoneParser.Usage.DATETIME));
+        }
+        for (String value : new String[] {"+14:30", "-14:45", "+08:01", 
"+08:15", "+08:00:01"}) {
+            Assertions.assertThrows(DateTimeException.class,
+                    () -> TimeZoneParser.normalize(value, 
TimeZoneParser.Usage.DATETIME));
+        }
+        Assertions.assertEquals("-14:00", TimeZoneParser.normalize("-14:00", 
TimeZoneParser.Usage.DATETIME));
+        Assertions.assertEquals("+08:05:43",
+                TimeZoneParser.normalize("+08:05:43", 
TimeZoneParser.Usage.TIMESTAMPTZ));
+        Assertions.assertEquals("+18:00",
+                TimeZoneParser.normalize("+18:00", 
TimeZoneParser.Usage.TIMESTAMPTZ));
+        for (String value : new String[] {"+18:01", "+19:00", "-18:01", 
"-19:00"}) {
+            Assertions.assertThrows(DateTimeException.class,
+                    () -> TimeZoneParser.normalize(value, 
TimeZoneParser.Usage.TIMESTAMPTZ));
+        }
+    }
+
+    @Test
+    void literalSuffixes() {
+        Assertions.assertTrue(DateTimeChecker.hasTimeZone("2024-01-02 12:00:00 
America/Port-au-Prince"));
+        Assertions.assertTrue(DateTimeChecker.hasTimeZone("2024-01-02 12:00:00 
Etc/GMT+3"));
+        Assertions.assertTrue(DateTimeChecker.hasTimeZone("2024-01-02 
12:00:00+0800"));
+        Assertions.assertDoesNotThrow(() -> new DateTimeV2Literal("2024-01-02 
12:00:00-14:00"));
+        Assertions.assertDoesNotThrow(() -> new DateTimeV2Literal("2024-01-02 
12:00:00+8:30"));
+        Assertions.assertDoesNotThrow(() -> new DateTimeV2Literal("2024-01-02 
12:00:00 UTC+8"));
+        Assertions.assertDoesNotThrow(() -> new TimestampTzLiteral("1900-01-01 
08:05:43+08:05:43"));
+        Assertions.assertDoesNotThrow(() -> new 
TimestampTzLiteral("20240102120000+0800"));
+        for (String suffix : new String[] {"UTC", "CST", "Asia/Shanghai",
+                "GMT0", "EST5EDT", "W-SU"}) {
+            Assertions.assertTrue(DateTimeChecker.hasTimeZone("2024-01-02 
12:00:00 " + suffix), suffix);
+            Assertions.assertTrue(DateTimeChecker.hasTimeZone("20240102120000" 
+ suffix), suffix);
+            Assertions.assertEquals(new TimestampTzLiteral("2024-01-02 
12:00:00 " + suffix).getValue(),
+                    new TimestampTzLiteral("20240102120000" + 
suffix).getValue(), suffix);
+            Assertions.assertEquals(new TimestampTzLiteral("2024-01-02 
12:00:00 " + suffix).getValue(),
+                    new TimestampTzLiteral("20240102T120000" + 
suffix).getValue(), suffix);
+            Assertions.assertEquals(new TimestampTzLiteral("2024-01-02 
12:00:00 " + suffix).getValue(),
+                    
TimestampTzLiteral.fromTimeZone(TimeStampTzType.SYSTEM_DEFAULT,
+                            "20240102120000" + suffix, "+08:00").getValue(), 
suffix);
+        }
+        Assertions.assertThrows(AnalysisException.class,
+                () -> new DateTimeV2Literal("2024-01-02 12:00:00+08:01"));
+        Assertions.assertThrows(AnalysisException.class,
+                () -> new TimestampTzLiteral("2024-01-02 12:00:00 GMT-13:00"));
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeStampNsLiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeStampNsLiteralTest.java
index b2bdc40ca98..ccbcf0d124b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeStampNsLiteralTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeStampNsLiteralTest.java
@@ -77,6 +77,8 @@ class TimeStampNsLiteralTest {
         TimeStampNsLiteral withTimeZone = new TimeStampNsLiteral(
                 "2024-01-02 03:04:05.123456789+00:00");
         Assertions.assertEquals(123456789, withTimeZone.getNanoSecond());
+        Assertions.assertThrows(Exception.class,
+                () -> new TimeStampNsLiteral("2024-01-02 
03:04:05.123456789+08:01"));
 
         TimeStampNsLiteral rounded = new TimeStampNsLiteral(
                 "2024-01-02 03:04:05.9999999995");
diff --git 
a/regression-test/data/datatype_p0/datetimev2/test_timezone_parser_contract.out 
b/regression-test/data/datatype_p0/datetimev2/test_timezone_parser_contract.out
new file mode 100644
index 00000000000..cb61013a144
--- /dev/null
+++ 
b/regression-test/data/datatype_p0/datetimev2/test_timezone_parser_contract.out
@@ -0,0 +1,1504 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !literal_0_0_0 --
+2024-01-02T12:00
+
+-- !literal_0_0_1 --
+2024-01-02T12:30
+
+-- !literal_0_0_2 --
+2024-01-01T22:00
+
+-- !literal_0_0_3 --
+2024-01-03T00:00
+
+-- !literal_0_0_4 --
+2024-01-03T01:00
+
+-- !literal_0_0_5 --
+2024-01-03T02:00
+
+-- !literal_0_0_6 --
+2024-01-02T04:00
+
+-- !literal_0_0_7 --
+2024-01-02T04:00
+
+-- !literal_0_0_8 --
+2024-01-02T04:00
+
+-- !literal_0_0_9 --
+2024-01-02T03:30
+
+-- !literal_0_0_10 --
+2024-01-02T03:15
+
+-- !literal_0_0_11 --
+\N
+
+-- !literal_0_0_12 --
+\N
+
+-- !literal_0_0_13 --
+\N
+
+-- !literal_0_0_14 --
+\N
+
+-- !literal_0_0_15 --
+\N
+
+-- !literal_0_0_16 --
+\N
+
+-- !literal_0_0_17 --
+2024-01-02T12:00
+
+-- !literal_0_0_18 --
+2024-01-02T12:00
+
+-- !literal_0_0_19 --
+2024-01-02T12:00
+
+-- !literal_0_0_20 --
+2024-01-02T12:00
+
+-- !literal_0_0_21 --
+2024-01-02T12:00
+
+-- !literal_0_0_22 --
+2024-01-02T04:00
+
+-- !literal_0_0_23 --
+2024-01-02T04:00
+
+-- !literal_0_0_24 --
+2024-01-02T04:00
+
+-- !literal_0_0_25 --
+2024-01-02T04:00
+
+-- !literal_0_0_26 --
+2024-01-02T15:00
+
+-- !literal_0_0_27 --
+2024-01-02T17:00
+
+-- !literal_0_0_28 --
+\N
+
+-- !literal_0_0_29 --
+2024-01-02T03:59
+
+-- !literal_0_0_30 --
+\N
+
+-- !literal_0_0_31 --
+2024-01-02T04:00
+
+-- !literal_0_0_32 --
+\N
+
+-- !literal_0_0_33 --
+\N
+
+-- !literal_0_0_34 --
+\N
+
+-- !literal_0_0_35 --
+\N
+
+-- !literal_0_0_36 --
+\N
+
+-- !literal_0_0_37 --
+\N
+
+-- !literal_0_0_38 --
+2024-01-02T12:00
+
+-- !literal_0_0_39 --
+2024-01-02T17:00
+
+-- !literal_0_0_40 --
+2024-01-02T09:00
+
+-- !literal_0_0_41 --
+\N
+
+-- !literal_0_0_42 --
+\N
+
+-- !literal_0_0_43 --
+\N
+
+-- !literal_0_0_44 --
+\N
+
+-- !literal_0_0_45 --
+\N
+
+-- !literal_0_0_46 --
+\N
+
+-- !literal_0_0_47 --
+1900-01-01T00:00
+
+-- !literal_0_0_48 --
+1900-01-01T00:00
+
+-- !literal_0_0_49 --
+\N
+
+-- !literal_0_0_50 --
+2024-01-02T04:00
+
+-- !literal_0_0_51 --
+2024-01-02T03:30
+
+-- !literal_0_0_52 --
+2024-01-02T12:00
+
+-- !literal_0_0_53 --
+2024-01-02T12:00
+
+-- !literal_0_0_54 --
+\N
+
+-- !literal_0_1_0 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_1 --
+2024-01-02 12:30:00.000000Z
+
+-- !literal_0_1_2 --
+2024-01-01 22:00:00.000000Z
+
+-- !literal_0_1_3 --
+2024-01-03 00:00:00.000000Z
+
+-- !literal_0_1_4 --
+2024-01-03 01:00:00.000000Z
+
+-- !literal_0_1_5 --
+2024-01-03 02:00:00.000000Z
+
+-- !literal_0_1_6 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_7 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_8 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_9 --
+2024-01-02 03:30:00.000000Z
+
+-- !literal_0_1_10 --
+2024-01-02 03:15:00.000000Z
+
+-- !literal_0_1_11 --
+2024-01-01 21:30:00.000000Z
+
+-- !literal_0_1_12 --
+2024-01-03 02:45:00.000000Z
+
+-- !literal_0_1_13 --
+2024-01-02 03:59:00.000000Z
+
+-- !literal_0_1_14 --
+2024-01-02 03:45:00.000000Z
+
+-- !literal_0_1_15 --
+\N
+
+-- !literal_0_1_16 --
+\N
+
+-- !literal_0_1_17 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_18 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_19 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_20 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_21 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_22 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_23 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_24 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_25 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_26 --
+2024-01-02 15:00:00.000000Z
+
+-- !literal_0_1_27 --
+2024-01-02 17:00:00.000000Z
+
+-- !literal_0_1_28 --
+\N
+
+-- !literal_0_1_29 --
+2024-01-02 03:59:00.000000Z
+
+-- !literal_0_1_30 --
+\N
+
+-- !literal_0_1_31 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_32 --
+2024-01-02 03:54:17.000000Z
+
+-- !literal_0_1_33 --
+2024-01-03 02:21:00.000000Z
+
+-- !literal_0_1_34 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_35 --
+2024-01-01 18:00:00.000000Z
+
+-- !literal_0_1_36 --
+\N
+
+-- !literal_0_1_37 --
+\N
+
+-- !literal_0_1_38 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_39 --
+2024-01-02 17:00:00.000000Z
+
+-- !literal_0_1_40 --
+2024-01-02 09:00:00.000000Z
+
+-- !literal_0_1_41 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_42 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_43 --
+\N
+
+-- !literal_0_1_44 --
+\N
+
+-- !literal_0_1_45 --
+\N
+
+-- !literal_0_1_46 --
+\N
+
+-- !literal_0_1_47 --
+1900-01-01 00:00:00.000000Z
+
+-- !literal_0_1_48 --
+1900-01-01 00:00:00.000000Z
+
+-- !literal_0_1_49 --
+1900-01-01 00:00:00.000000Z
+
+-- !literal_0_1_50 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_0_1_51 --
+2024-01-02 03:30:00.000000Z
+
+-- !literal_0_1_52 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_53 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_0_1_54 --
+\N
+
+-- !literal_1_0_0 --
+2024-01-02T12:00
+
+-- !literal_1_0_1 --
+2024-01-02T12:30
+
+-- !literal_1_0_2 --
+2024-01-01T22:00
+
+-- !literal_1_0_3 --
+2024-01-03T00:00
+
+-- !literal_1_0_4 --
+2024-01-03T01:00
+
+-- !literal_1_0_5 --
+2024-01-03T02:00
+
+-- !literal_1_0_6 --
+2024-01-02T04:00
+
+-- !literal_1_0_7 --
+2024-01-02T04:00
+
+-- !literal_1_0_8 --
+2024-01-02T04:00
+
+-- !literal_1_0_9 --
+2024-01-02T03:30
+
+-- !literal_1_0_10 --
+2024-01-02T03:15
+
+-- !literal_1_0_11 --
+\N
+
+-- !literal_1_0_12 --
+\N
+
+-- !literal_1_0_13 --
+\N
+
+-- !literal_1_0_14 --
+\N
+
+-- !literal_1_0_15 --
+\N
+
+-- !literal_1_0_16 --
+\N
+
+-- !literal_1_0_17 --
+2024-01-02T12:00
+
+-- !literal_1_0_18 --
+2024-01-02T12:00
+
+-- !literal_1_0_19 --
+2024-01-02T12:00
+
+-- !literal_1_0_20 --
+2024-01-02T12:00
+
+-- !literal_1_0_21 --
+2024-01-02T12:00
+
+-- !literal_1_0_22 --
+2024-01-02T04:00
+
+-- !literal_1_0_23 --
+2024-01-02T04:00
+
+-- !literal_1_0_24 --
+2024-01-02T04:00
+
+-- !literal_1_0_25 --
+2024-01-02T04:00
+
+-- !literal_1_0_26 --
+2024-01-02T15:00
+
+-- !literal_1_0_27 --
+2024-01-02T17:00
+
+-- !literal_1_0_28 --
+\N
+
+-- !literal_1_0_29 --
+2024-01-02T03:59
+
+-- !literal_1_0_30 --
+\N
+
+-- !literal_1_0_31 --
+2024-01-02T04:00
+
+-- !literal_1_0_32 --
+\N
+
+-- !literal_1_0_33 --
+\N
+
+-- !literal_1_0_34 --
+\N
+
+-- !literal_1_0_35 --
+\N
+
+-- !literal_1_0_36 --
+\N
+
+-- !literal_1_0_37 --
+\N
+
+-- !literal_1_0_38 --
+2024-01-02T12:00
+
+-- !literal_1_0_39 --
+2024-01-02T17:00
+
+-- !literal_1_0_40 --
+2024-01-02T09:00
+
+-- !literal_1_0_41 --
+\N
+
+-- !literal_1_0_42 --
+\N
+
+-- !literal_1_0_43 --
+\N
+
+-- !literal_1_0_44 --
+\N
+
+-- !literal_1_0_45 --
+\N
+
+-- !literal_1_0_46 --
+\N
+
+-- !literal_1_0_47 --
+1900-01-01T00:00
+
+-- !literal_1_0_48 --
+1900-01-01T00:00
+
+-- !literal_1_0_49 --
+\N
+
+-- !literal_1_0_50 --
+2024-01-02T04:00
+
+-- !literal_1_0_51 --
+2024-01-02T03:30
+
+-- !literal_1_0_52 --
+2024-01-02T12:00
+
+-- !literal_1_0_53 --
+2024-01-02T12:00
+
+-- !literal_1_0_54 --
+\N
+
+-- !literal_1_1_0 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_1 --
+2024-01-02 12:30:00.000000+00:00
+
+-- !literal_1_1_2 --
+2024-01-01 22:00:00.000000+00:00
+
+-- !literal_1_1_3 --
+2024-01-03 00:00:00.000000+00:00
+
+-- !literal_1_1_4 --
+2024-01-03 01:00:00.000000+00:00
+
+-- !literal_1_1_5 --
+2024-01-03 02:00:00.000000+00:00
+
+-- !literal_1_1_6 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_7 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_8 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_9 --
+2024-01-02 03:30:00.000000+00:00
+
+-- !literal_1_1_10 --
+2024-01-02 03:15:00.000000+00:00
+
+-- !literal_1_1_11 --
+2024-01-01 21:30:00.000000+00:00
+
+-- !literal_1_1_12 --
+2024-01-03 02:45:00.000000+00:00
+
+-- !literal_1_1_13 --
+2024-01-02 03:59:00.000000+00:00
+
+-- !literal_1_1_14 --
+2024-01-02 03:45:00.000000+00:00
+
+-- !literal_1_1_15 --
+\N
+
+-- !literal_1_1_16 --
+\N
+
+-- !literal_1_1_17 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_18 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_19 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_20 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_21 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_22 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_23 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_24 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_25 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_26 --
+2024-01-02 15:00:00.000000+00:00
+
+-- !literal_1_1_27 --
+2024-01-02 17:00:00.000000+00:00
+
+-- !literal_1_1_28 --
+\N
+
+-- !literal_1_1_29 --
+2024-01-02 03:59:00.000000+00:00
+
+-- !literal_1_1_30 --
+\N
+
+-- !literal_1_1_31 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_32 --
+2024-01-02 03:54:17.000000+00:00
+
+-- !literal_1_1_33 --
+2024-01-03 02:21:00.000000+00:00
+
+-- !literal_1_1_34 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_35 --
+2024-01-01 18:00:00.000000+00:00
+
+-- !literal_1_1_36 --
+\N
+
+-- !literal_1_1_37 --
+\N
+
+-- !literal_1_1_38 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_39 --
+2024-01-02 17:00:00.000000+00:00
+
+-- !literal_1_1_40 --
+2024-01-02 09:00:00.000000+00:00
+
+-- !literal_1_1_41 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_42 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_43 --
+\N
+
+-- !literal_1_1_44 --
+\N
+
+-- !literal_1_1_45 --
+\N
+
+-- !literal_1_1_46 --
+\N
+
+-- !literal_1_1_47 --
+1900-01-01 00:00:00.000000+00:00
+
+-- !literal_1_1_48 --
+1900-01-01 00:00:00.000000+00:00
+
+-- !literal_1_1_49 --
+1900-01-01 00:00:00.000000+00:00
+
+-- !literal_1_1_50 --
+2024-01-02 04:00:00.000000+00:00
+
+-- !literal_1_1_51 --
+2024-01-02 03:30:00.000000+00:00
+
+-- !literal_1_1_52 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_53 --
+2024-01-02 12:00:00.000000+00:00
+
+-- !literal_1_1_54 --
+\N
+
+-- !literal_2_0_0 --
+2024-01-02T12:00
+
+-- !literal_2_0_1 --
+2024-01-02T12:30
+
+-- !literal_2_0_2 --
+2024-01-01T22:00
+
+-- !literal_2_0_3 --
+2024-01-03T00:00
+
+-- !literal_2_0_4 --
+2024-01-03T01:00
+
+-- !literal_2_0_5 --
+2024-01-03T02:00
+
+-- !literal_2_0_6 --
+2024-01-02T04:00
+
+-- !literal_2_0_7 --
+2024-01-02T04:00
+
+-- !literal_2_0_8 --
+2024-01-02T04:00
+
+-- !literal_2_0_9 --
+2024-01-02T03:30
+
+-- !literal_2_0_10 --
+2024-01-02T03:15
+
+-- !literal_2_0_11 --
+\N
+
+-- !literal_2_0_12 --
+\N
+
+-- !literal_2_0_13 --
+\N
+
+-- !literal_2_0_14 --
+\N
+
+-- !literal_2_0_15 --
+\N
+
+-- !literal_2_0_16 --
+\N
+
+-- !literal_2_0_17 --
+2024-01-02T12:00
+
+-- !literal_2_0_18 --
+2024-01-02T12:00
+
+-- !literal_2_0_19 --
+2024-01-02T12:00
+
+-- !literal_2_0_20 --
+2024-01-02T12:00
+
+-- !literal_2_0_21 --
+2024-01-02T12:00
+
+-- !literal_2_0_22 --
+2024-01-02T04:00
+
+-- !literal_2_0_23 --
+2024-01-02T04:00
+
+-- !literal_2_0_24 --
+2024-01-02T04:00
+
+-- !literal_2_0_25 --
+2024-01-02T04:00
+
+-- !literal_2_0_26 --
+2024-01-02T15:00
+
+-- !literal_2_0_27 --
+2024-01-02T17:00
+
+-- !literal_2_0_28 --
+\N
+
+-- !literal_2_0_29 --
+2024-01-02T03:59
+
+-- !literal_2_0_30 --
+\N
+
+-- !literal_2_0_31 --
+2024-01-02T04:00
+
+-- !literal_2_0_32 --
+\N
+
+-- !literal_2_0_33 --
+\N
+
+-- !literal_2_0_34 --
+\N
+
+-- !literal_2_0_35 --
+\N
+
+-- !literal_2_0_36 --
+\N
+
+-- !literal_2_0_37 --
+\N
+
+-- !literal_2_0_38 --
+2024-01-02T12:00
+
+-- !literal_2_0_39 --
+2024-01-02T17:00
+
+-- !literal_2_0_40 --
+2024-01-02T09:00
+
+-- !literal_2_0_41 --
+\N
+
+-- !literal_2_0_42 --
+\N
+
+-- !literal_2_0_43 --
+\N
+
+-- !literal_2_0_44 --
+\N
+
+-- !literal_2_0_45 --
+\N
+
+-- !literal_2_0_46 --
+\N
+
+-- !literal_2_0_47 --
+1900-01-01T00:00
+
+-- !literal_2_0_48 --
+1900-01-01T00:00
+
+-- !literal_2_0_49 --
+\N
+
+-- !literal_2_0_50 --
+2024-01-02T04:00
+
+-- !literal_2_0_51 --
+2024-01-02T03:30
+
+-- !literal_2_0_52 --
+2024-01-02T12:00
+
+-- !literal_2_0_53 --
+2024-01-02T12:00
+
+-- !literal_2_0_54 --
+\N
+
+-- !literal_2_1_0 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_1 --
+2024-01-02 12:30:00.000000Z
+
+-- !literal_2_1_2 --
+2024-01-01 22:00:00.000000Z
+
+-- !literal_2_1_3 --
+2024-01-03 00:00:00.000000Z
+
+-- !literal_2_1_4 --
+2024-01-03 01:00:00.000000Z
+
+-- !literal_2_1_5 --
+2024-01-03 02:00:00.000000Z
+
+-- !literal_2_1_6 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_7 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_8 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_9 --
+2024-01-02 03:30:00.000000Z
+
+-- !literal_2_1_10 --
+2024-01-02 03:15:00.000000Z
+
+-- !literal_2_1_11 --
+2024-01-01 21:30:00.000000Z
+
+-- !literal_2_1_12 --
+2024-01-03 02:45:00.000000Z
+
+-- !literal_2_1_13 --
+2024-01-02 03:59:00.000000Z
+
+-- !literal_2_1_14 --
+2024-01-02 03:45:00.000000Z
+
+-- !literal_2_1_15 --
+\N
+
+-- !literal_2_1_16 --
+\N
+
+-- !literal_2_1_17 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_18 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_19 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_20 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_21 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_22 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_23 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_24 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_25 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_26 --
+2024-01-02 15:00:00.000000Z
+
+-- !literal_2_1_27 --
+2024-01-02 17:00:00.000000Z
+
+-- !literal_2_1_28 --
+\N
+
+-- !literal_2_1_29 --
+2024-01-02 03:59:00.000000Z
+
+-- !literal_2_1_30 --
+\N
+
+-- !literal_2_1_31 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_32 --
+2024-01-02 03:54:17.000000Z
+
+-- !literal_2_1_33 --
+2024-01-03 02:21:00.000000Z
+
+-- !literal_2_1_34 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_35 --
+2024-01-01 18:00:00.000000Z
+
+-- !literal_2_1_36 --
+\N
+
+-- !literal_2_1_37 --
+\N
+
+-- !literal_2_1_38 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_39 --
+2024-01-02 17:00:00.000000Z
+
+-- !literal_2_1_40 --
+2024-01-02 09:00:00.000000Z
+
+-- !literal_2_1_41 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_42 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_43 --
+\N
+
+-- !literal_2_1_44 --
+\N
+
+-- !literal_2_1_45 --
+\N
+
+-- !literal_2_1_46 --
+\N
+
+-- !literal_2_1_47 --
+1900-01-01 00:00:00.000000Z
+
+-- !literal_2_1_48 --
+1900-01-01 00:00:00.000000Z
+
+-- !literal_2_1_49 --
+1900-01-01 00:00:00.000000Z
+
+-- !literal_2_1_50 --
+2024-01-02 04:00:00.000000Z
+
+-- !literal_2_1_51 --
+2024-01-02 03:30:00.000000Z
+
+-- !literal_2_1_52 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_53 --
+2024-01-02 12:00:00.000000Z
+
+-- !literal_2_1_54 --
+\N
+
+-- !slots_0 --
+0      2024-01-02T12:00
+1      2024-01-02T12:30
+2      2024-01-01T22:00
+3      2024-01-03T00:00
+4      2024-01-03T01:00
+5      2024-01-03T02:00
+6      2024-01-02T04:00
+7      2024-01-02T04:00
+8      2024-01-02T04:00
+9      2024-01-02T03:30
+10     2024-01-02T03:15
+11     \N
+12     \N
+13     \N
+14     \N
+15     \N
+16     \N
+17     2024-01-02T12:00
+18     2024-01-02T12:00
+19     2024-01-02T12:00
+20     2024-01-02T12:00
+21     2024-01-02T12:00
+22     2024-01-02T04:00
+23     2024-01-02T04:00
+24     2024-01-02T04:00
+25     2024-01-02T04:00
+26     2024-01-02T15:00
+27     2024-01-02T17:00
+28     \N
+29     2024-01-02T03:59
+30     \N
+31     2024-01-02T04:00
+32     \N
+33     \N
+34     \N
+35     \N
+36     \N
+37     \N
+38     2024-01-02T12:00
+39     2024-01-02T17:00
+40     2024-01-02T09:00
+41     \N
+42     \N
+43     \N
+44     \N
+45     \N
+46     \N
+47     1900-01-01T00:00
+48     1900-01-01T00:00
+49     \N
+50     2024-01-02T04:00
+51     2024-01-02T03:30
+52     2024-01-02T12:00
+53     2024-01-02T12:00
+54     \N
+
+-- !lambda_0 --
+0      ["2024-01-02 12:00:00.000000"]
+1      ["2024-01-02 12:30:00.000000"]
+2      ["2024-01-01 22:00:00.000000"]
+3      ["2024-01-03 00:00:00.000000"]
+4      ["2024-01-03 01:00:00.000000"]
+5      ["2024-01-03 02:00:00.000000"]
+6      ["2024-01-02 04:00:00.000000"]
+7      ["2024-01-02 04:00:00.000000"]
+8      ["2024-01-02 04:00:00.000000"]
+9      ["2024-01-02 03:30:00.000000"]
+10     ["2024-01-02 03:15:00.000000"]
+11     [null]
+12     [null]
+13     [null]
+14     [null]
+15     [null]
+16     [null]
+17     ["2024-01-02 12:00:00.000000"]
+18     ["2024-01-02 12:00:00.000000"]
+19     ["2024-01-02 12:00:00.000000"]
+20     ["2024-01-02 12:00:00.000000"]
+21     ["2024-01-02 12:00:00.000000"]
+22     ["2024-01-02 04:00:00.000000"]
+23     ["2024-01-02 04:00:00.000000"]
+24     ["2024-01-02 04:00:00.000000"]
+25     ["2024-01-02 04:00:00.000000"]
+26     ["2024-01-02 15:00:00.000000"]
+27     ["2024-01-02 17:00:00.000000"]
+28     [null]
+29     ["2024-01-02 03:59:00.000000"]
+30     [null]
+31     ["2024-01-02 04:00:00.000000"]
+32     [null]
+33     [null]
+34     [null]
+35     [null]
+36     [null]
+37     [null]
+38     ["2024-01-02 12:00:00.000000"]
+39     ["2024-01-02 17:00:00.000000"]
+40     ["2024-01-02 09:00:00.000000"]
+41     [null]
+42     [null]
+43     [null]
+44     [null]
+45     [null]
+46     [null]
+47     ["1900-01-01 00:00:00.000000"]
+48     ["1900-01-01 00:00:00.000000"]
+49     [null]
+50     ["2024-01-02 04:00:00.000000"]
+51     ["2024-01-02 03:30:00.000000"]
+52     ["2024-01-02 12:00:00.000000"]
+53     ["2024-01-02 12:00:00.000000"]
+54     [null]
+
+-- !slots_1 --
+0      2024-01-02 12:00:00.000000+00:00
+1      2024-01-02 12:30:00.000000+00:00
+2      2024-01-01 22:00:00.000000+00:00
+3      2024-01-03 00:00:00.000000+00:00
+4      2024-01-03 01:00:00.000000+00:00
+5      2024-01-03 02:00:00.000000+00:00
+6      2024-01-02 04:00:00.000000+00:00
+7      2024-01-02 04:00:00.000000+00:00
+8      2024-01-02 04:00:00.000000+00:00
+9      2024-01-02 03:30:00.000000+00:00
+10     2024-01-02 03:15:00.000000+00:00
+11     2024-01-01 21:30:00.000000+00:00
+12     2024-01-03 02:45:00.000000+00:00
+13     2024-01-02 03:59:00.000000+00:00
+14     2024-01-02 03:45:00.000000+00:00
+15     \N
+16     \N
+17     2024-01-02 12:00:00.000000+00:00
+18     2024-01-02 12:00:00.000000+00:00
+19     2024-01-02 12:00:00.000000+00:00
+20     2024-01-02 12:00:00.000000+00:00
+21     2024-01-02 12:00:00.000000+00:00
+22     2024-01-02 04:00:00.000000+00:00
+23     2024-01-02 04:00:00.000000+00:00
+24     2024-01-02 04:00:00.000000+00:00
+25     2024-01-02 04:00:00.000000+00:00
+26     2024-01-02 15:00:00.000000+00:00
+27     2024-01-02 17:00:00.000000+00:00
+28     \N
+29     2024-01-02 03:59:00.000000+00:00
+30     \N
+31     2024-01-02 04:00:00.000000+00:00
+32     2024-01-02 03:54:17.000000+00:00
+33     2024-01-03 02:21:00.000000+00:00
+34     2024-01-02 04:00:00.000000+00:00
+35     2024-01-01 18:00:00.000000+00:00
+36     \N
+37     \N
+38     2024-01-02 12:00:00.000000+00:00
+39     2024-01-02 17:00:00.000000+00:00
+40     2024-01-02 09:00:00.000000+00:00
+41     2024-01-02 12:00:00.000000+00:00
+42     2024-01-02 12:00:00.000000+00:00
+43     \N
+44     \N
+45     \N
+46     \N
+47     1900-01-01 00:00:00.000000+00:00
+48     1900-01-01 00:00:00.000000+00:00
+49     1900-01-01 00:00:00.000000+00:00
+50     2024-01-02 04:00:00.000000+00:00
+51     2024-01-02 03:30:00.000000+00:00
+52     2024-01-02 12:00:00.000000+00:00
+53     2024-01-02 12:00:00.000000+00:00
+54     \N
+
+-- !lambda_1 --
+0      ["2024-01-02 12:00:00.000000+00:00"]
+1      ["2024-01-02 12:30:00.000000+00:00"]
+2      ["2024-01-01 22:00:00.000000+00:00"]
+3      ["2024-01-03 00:00:00.000000+00:00"]
+4      ["2024-01-03 01:00:00.000000+00:00"]
+5      ["2024-01-03 02:00:00.000000+00:00"]
+6      ["2024-01-02 04:00:00.000000+00:00"]
+7      ["2024-01-02 04:00:00.000000+00:00"]
+8      ["2024-01-02 04:00:00.000000+00:00"]
+9      ["2024-01-02 03:30:00.000000+00:00"]
+10     ["2024-01-02 03:15:00.000000+00:00"]
+11     ["2024-01-01 21:30:00.000000+00:00"]
+12     ["2024-01-03 02:45:00.000000+00:00"]
+13     ["2024-01-02 03:59:00.000000+00:00"]
+14     ["2024-01-02 03:45:00.000000+00:00"]
+15     [null]
+16     [null]
+17     ["2024-01-02 12:00:00.000000+00:00"]
+18     ["2024-01-02 12:00:00.000000+00:00"]
+19     ["2024-01-02 12:00:00.000000+00:00"]
+20     ["2024-01-02 12:00:00.000000+00:00"]
+21     ["2024-01-02 12:00:00.000000+00:00"]
+22     ["2024-01-02 04:00:00.000000+00:00"]
+23     ["2024-01-02 04:00:00.000000+00:00"]
+24     ["2024-01-02 04:00:00.000000+00:00"]
+25     ["2024-01-02 04:00:00.000000+00:00"]
+26     ["2024-01-02 15:00:00.000000+00:00"]
+27     ["2024-01-02 17:00:00.000000+00:00"]
+28     [null]
+29     ["2024-01-02 03:59:00.000000+00:00"]
+30     [null]
+31     ["2024-01-02 04:00:00.000000+00:00"]
+32     ["2024-01-02 03:54:17.000000+00:00"]
+33     ["2024-01-03 02:21:00.000000+00:00"]
+34     ["2024-01-02 04:00:00.000000+00:00"]
+35     ["2024-01-01 18:00:00.000000+00:00"]
+36     [null]
+37     [null]
+38     ["2024-01-02 12:00:00.000000+00:00"]
+39     ["2024-01-02 17:00:00.000000+00:00"]
+40     ["2024-01-02 09:00:00.000000+00:00"]
+41     ["2024-01-02 12:00:00.000000+00:00"]
+42     ["2024-01-02 12:00:00.000000+00:00"]
+43     [null]
+44     [null]
+45     [null]
+46     [null]
+47     ["1900-01-01 00:00:00.000000+00:00"]
+48     ["1900-01-01 00:00:00.000000+00:00"]
+49     ["1900-01-01 00:00:00.000000+00:00"]
+50     ["2024-01-02 04:00:00.000000+00:00"]
+51     ["2024-01-02 03:30:00.000000+00:00"]
+52     ["2024-01-02 12:00:00.000000+00:00"]
+53     ["2024-01-02 12:00:00.000000+00:00"]
+54     [null]
+
+-- !session_literal_0_0 --
+2024-01-02 12:00:00.000000+08:00
+
+-- !session_literal_0_1 --
+2024-01-03 01:00:00.000000+08:00
+
+-- !session_literal_0_2 --
+2024-01-02 23:00:00.000000+08:00
+
+-- !session_literal_1_0 --
+2024-01-02 12:00:00.000000+08:00
+
+-- !session_literal_1_1 --
+2024-01-03 01:00:00.000000+08:00
+
+-- !session_literal_1_2 --
+2024-01-02 23:00:00.000000+08:00
+
+-- !session_slots --
+0      2024-01-02 20:00:00.000000+08:00
+1      2024-01-02 20:30:00.000000+08:00
+2      2024-01-02 06:00:00.000000+08:00
+3      2024-01-03 08:00:00.000000+08:00
+4      2024-01-03 09:00:00.000000+08:00
+5      2024-01-03 10:00:00.000000+08:00
+6      2024-01-02 12:00:00.000000+08:00
+7      2024-01-02 12:00:00.000000+08:00
+8      2024-01-02 12:00:00.000000+08:00
+9      2024-01-02 11:30:00.000000+08:00
+10     2024-01-02 11:15:00.000000+08:00
+11     2024-01-02 05:30:00.000000+08:00
+12     2024-01-03 10:45:00.000000+08:00
+13     2024-01-02 11:59:00.000000+08:00
+14     2024-01-02 11:45:00.000000+08:00
+15     \N
+16     \N
+17     2024-01-02 20:00:00.000000+08:00
+18     2024-01-02 20:00:00.000000+08:00
+19     2024-01-02 20:00:00.000000+08:00
+20     2024-01-02 20:00:00.000000+08:00
+21     2024-01-02 20:00:00.000000+08:00
+22     2024-01-02 12:00:00.000000+08:00
+23     2024-01-02 12:00:00.000000+08:00
+24     2024-01-02 12:00:00.000000+08:00
+25     2024-01-02 12:00:00.000000+08:00
+26     2024-01-02 23:00:00.000000+08:00
+27     2024-01-03 01:00:00.000000+08:00
+28     \N
+29     2024-01-02 11:59:00.000000+08:00
+30     \N
+31     2024-01-02 12:00:00.000000+08:00
+32     2024-01-02 11:54:17.000000+08:00
+33     2024-01-03 10:21:00.000000+08:00
+34     2024-01-02 12:00:00.000000+08:00
+35     2024-01-02 02:00:00.000000+08:00
+36     \N
+37     \N
+38     2024-01-02 20:00:00.000000+08:00
+39     2024-01-03 01:00:00.000000+08:00
+40     2024-01-02 17:00:00.000000+08:00
+41     2024-01-02 20:00:00.000000+08:00
+42     2024-01-02 20:00:00.000000+08:00
+43     \N
+44     \N
+45     \N
+46     \N
+47     1900-01-01 08:00:00.000000+08:00
+48     1900-01-01 08:00:00.000000+08:00
+49     1900-01-01 08:00:00.000000+08:00
+50     2024-01-02 12:00:00.000000+08:00
+51     2024-01-02 11:30:00.000000+08:00
+52     2024-01-02 20:00:00.000000+08:00
+53     2024-01-02 20:00:00.000000+08:00
+54     \N
+
+-- !session_zone_0 --
+UTC    2024-01-02T12:00
+
+-- !session_zone_1 --
+UTC    2024-01-02T12:00
+
+-- !session_zone_2 --
+Zulu   2024-01-02T12:00
+
+-- !session_zone_3 --
+CST    2024-01-02T20:00
+
+-- !session_zone_4 --
+Asia/Shanghai  2024-01-02T20:00
+
+-- !session_zone_5 --
+UTC+08:00      2024-01-02T20:00
+
+-- !session_zone_6 --
+GMT-06:00      2024-01-02T06:00
+
+-- !session_zone_7 --
++14:00 2024-01-03T02:00
+
+-- !session_zone_8 --
+-12:00 2024-01-02T00:00
+
+-- !convert_0_0_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_0_0_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_0_0_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_0_0_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_0_0_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_0_1_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_0_1_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_0_1_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_0_1_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_0_1_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_0_2_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_0_2_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_0_2_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_0_2_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_0_2_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_0_3_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_0_3_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_0_3_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_0_3_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_0_3_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_0_4_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_0_4_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_0_4_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_0_4_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_0_4_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_1_0_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_1_0_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_1_0_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_1_0_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_1_0_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_1_1_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_1_1_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_1_1_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_1_1_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_1_1_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_1_2_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_1_2_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_1_2_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_1_2_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_1_2_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_1_3_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_1_3_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_1_3_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_1_3_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_1_3_4 --
+2024-01-02T12:30:00.123456
+
+-- !convert_1_4_0 --
+2024-01-01T22:00:00.123456
+
+-- !convert_1_4_1 --
+2024-01-03T00:00:00.123456
+
+-- !convert_1_4_2 --
+2024-01-02T03:59:00.123456
+
+-- !convert_1_4_3 --
+2024-01-02T03:45:00.123456
+
+-- !convert_1_4_4 --
+2024-01-02T12:30:00.123456
+
+-- !strict_0 --
+2024-01-03T02:00
+
+-- !strict_1 --
+2024-01-02T03:30
+
+-- !strict_2 --
+2024-01-02T04:00
+
+-- !strict_timestamptz_0_0 --
+2024-01-01 18:00:00.000000Z
+
+-- !strict_timestamptz_0_1 --
+2024-01-03 06:00:00.000000Z
+
+-- !strict_timestamptz_0_2 --
+2024-01-02 03:54:17.000000Z
+
+-- !strict_timestamptz_0_3 --
+2024-01-02 12:00:00.000000Z
+
+-- !strict_timestamptz_1_0 --
+2024-01-01 18:00:00.000000+00:00
+
+-- !strict_timestamptz_1_1 --
+2024-01-03 06:00:00.000000+00:00
+
+-- !strict_timestamptz_1_2 --
+2024-01-02 03:54:17.000000+00:00
+
+-- !strict_timestamptz_1_3 --
+2024-01-02 12:00:00.000000+00:00
+
diff --git 
a/regression-test/suites/datatype_p0/datetimev2/test_timezone_parser_contract.groovy
 
b/regression-test/suites/datatype_p0/datetimev2/test_timezone_parser_contract.groovy
new file mode 100644
index 00000000000..ba63c9955fb
--- /dev/null
+++ 
b/regression-test/suites/datatype_p0/datetimev2/test_timezone_parser_contract.groovy
@@ -0,0 +1,129 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_timezone_parser_contract") {
+    sql "DROP TABLE IF EXISTS timezone_parser_contract_inputs"
+    sql """CREATE TABLE timezone_parser_contract_inputs (id INT, v STRING)
+        DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ('replication_num'='1')"""
+
+    def suffixes = ["+00:00", "-00:30", "+14:00", "-12:00", "-13:00", "-14:00",
+                    "+8", "+08", "+0800", "+8:30", "+08:45", "+14:30", 
"-14:45",
+                    "+08:01", "+08:15", "+08:60", "+24:00", "UTC", "utc", 
"GMT", "Z",
+                    "zulu", "CST", "cst", "Asia/Shanghai", "asia/shanghai", 
"Etc/GMT+3",
+                    "America/Port-au-Prince", "America/Does_not_exist", 
"UTC+08:01",
+                    "GMT-13:00", "UTC+8", "+08:05:43", "-14:21", "+08:00:00", 
"+18:00", "+18:01",
+                    "+19:00", "GMT0", "EST5EDT", "W-SU",
+                    "+00:00:00", "-00:00:00", "UTC8", "GMT8:30", "UTC0", 
"GMT0:00"]
+    def inputs = suffixes.collect { "2024-01-02 12:00:00 ${it}" }
+    inputs.addAll(["1900-01-01 08:05:43 CST", "1900-01-01 08:05:43 
Asia/Shanghai",
+                   "1900-01-01 08:05:43+08:05:43", "20240102120000+0800", 
"20240102T120000+8:30",
+                   "20240102120000UTC", "20240102T120000UTC"])
+    inputs.add(null)
+    sql "INSERT INTO timezone_parser_contract_inputs VALUES " + 
inputs.withIndex().collect { v, id ->
+        "(${id}, ${v == null ? 'NULL' : "'${v}'"})"
+    }.join(",")
+    sql "SET enable_sql_cache=false"
+    sql "SET time_zone='+00:00'"
+    sql "SET enable_strict_cast=false"
+
+    // Literal folding requests, folding disabled, stored slots and Lambda 
inputs.
+    [[false, false], [false, true], [true, false]].eachWithIndex { settings, 
path ->
+        sql "SET enable_fold_constant_by_be=${settings[0]}"
+        sql "SET debug_skip_fold_constant=${settings[1]}"
+        ["DATETIME(6)", "TIMESTAMPTZ(6)"].eachWithIndex { type, typeId ->
+            inputs.eachWithIndex { value, id ->
+                def literal = value == null ? "NULL" : "'${value}'"
+                "qt_literal_${path}_${typeId}_${id}"("SELECT CAST(${literal} 
AS ${type})")
+            }
+        }
+    }
+    sql "SET enable_fold_constant_by_be=false"
+    sql "SET debug_skip_fold_constant=true"
+    ["DATETIME(6)", "TIMESTAMPTZ(6)"].eachWithIndex { type, typeId ->
+        "qt_slots_${typeId}"("SELECT id, CAST(v AS ${type}) FROM 
timezone_parser_contract_inputs ORDER BY id")
+        "qt_lambda_${typeId}"("SELECT id, array_map(x -> CAST(x AS ${type}), 
ARRAY(v)) FROM timezone_parser_contract_inputs ORDER BY id")
+    }
+    sql "SET time_zone='+08:00'"
+    [false, true].eachWithIndex { skip, path ->
+        sql "SET debug_skip_fold_constant=${skip}"
+        ["+0800", "America/Port-au-Prince", "Etc/GMT+3"].eachWithIndex { 
suffix, id ->
+            "qt_session_literal_${path}_${id}"("SELECT CAST('2024-01-02 
12:00:00 ${suffix}' AS TIMESTAMPTZ(6))")
+        }
+    }
+    sql "SET debug_skip_fold_constant=true"
+    qt_session_slots "SELECT id, CAST(v AS TIMESTAMPTZ(6)) FROM 
timezone_parser_contract_inputs ORDER BY id"
+
+    ["utc", "Z", "Zulu", "cst", "Asia/Shanghai", "UTC+8", "GMT-6", "+14:00", 
"-12:00"].eachWithIndex { zone, id ->
+        sql "SET time_zone='${zone}'"
+        "qt_session_zone_${id}"("SELECT @@time_zone, CAST('2024-01-02 
12:00:00+00:00' AS DATETIME(6))")
+    }
+    sql "SET time_zone='+00:00'"
+    ["UTC8", "GMT8:30", "UTC0", "GMT0:00", "+00:00:00", "-00:00:00"].each { 
zone ->
+        test {
+            sql "SET time_zone='${zone}'"
+            exception "Unknown or incorrect time zone"
+        }
+    }
+    [false, true].eachWithIndex { skip, path ->
+        sql "SET debug_skip_fold_constant=${skip}"
+        ["", "UTC", "GMT", "utc", "gmt"].eachWithIndex { prefix, prefixId ->
+            ["+14:00", "-12:00", "+08:01", "+08:15", "-00:30"].eachWithIndex { 
offset, id ->
+                "qt_convert_${path}_${prefixId}_${id}"("SELECT 
CONVERT_TZ(CAST('2024-01-02 12:00:00.123456' AS DATETIME(6)), 
'${prefix}${offset}', 'UTC')")
+            }
+            ["+14:01", "+14:30", "+14:45", "-12:01", "-12:30", "-12:45", 
"+15:00", "-13:00", "+08:60"].each { offset ->
+                test {
+                    sql "SELECT CONVERT_TZ(CAST('2024-01-02 12:00:00' AS 
DATETIME(6)), '${prefix}${offset}', 'UTC')"
+                    exception "timezone"
+                }
+                test {
+                    sql "SET time_zone='${prefix}${offset}'"
+                    exception "Unknown or incorrect time zone"
+                }
+            }
+        }
+    }
+    sql "SET debug_skip_fold_constant=true"
+    test {
+        sql "SELECT CONVERT_TZ(CAST('2024-01-02 12:00:00' AS DATETIME(6)), 
split_part(v, ' ', 3), 'UTC') FROM timezone_parser_contract_inputs WHERE id=11"
+        exception "timezone"
+    }
+    sql "SET debug_skip_fold_constant=true"
+    sql "SET enable_strict_cast=true"
+    ["-14:00", "+8:30", "UTC+8"].eachWithIndex { suffix, id ->
+        "qt_strict_${id}"("SELECT CAST('2024-01-02 12:00:00 ${suffix}' AS 
DATETIME(6))")
+    }
+    ["+14:30", "+08:01", "+08:60", "+00:00:00", "UTC8", "GMT8:30"].each { 
suffix ->
+        test {
+            sql "SELECT CAST('2024-01-02 12:00:00 ${suffix}' AS DATETIME(6))"
+            exception "parse"
+        }
+    }
+    [false, true].eachWithIndex { skip, path ->
+        sql "SET debug_skip_fold_constant=${skip}"
+        ["+18:00", "-18:00", "+08:05:43", "20240102120000UTC"].eachWithIndex { 
suffix, id ->
+            def value = suffix.startsWith("2024") ? suffix : "2024-01-02 
12:00:00 ${suffix}"
+            "qt_strict_timestamptz_${path}_${id}"("SELECT CAST('${value}' AS 
TIMESTAMPTZ(6))")
+        }
+        ["+18:01", "-18:01", "+18:00:01", "-18:00:01", "+19:00", 
"-19:00"].each { suffix ->
+            test {
+                sql "SELECT CAST('2024-01-02 12:00:00 ${suffix}' AS 
TIMESTAMPTZ(6))"
+                exception "parse"
+            }
+        }
+    }
+}
diff --git 
a/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
 
b/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
index 6f9e4382b29..1475e5055fc 100644
--- 
a/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
+++ 
b/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
@@ -112,7 +112,7 @@ suite("fe_constant_cast_to_date") {
     }
     test {
         sql """select cast("2024-05-01T00:00+14:30" as date)"""
-        exception "Time zone offset couldn't be larger than 14:00"
+        exception "date/datetime literal [2024-05-01T00:00:00+14:30] is 
invalid"
     }
     test {
         sql """select cast("2024-05-01T00:00+08:25" as date)"""
diff --git 
a/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_datetime.groovy
 
b/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_datetime.groovy
index 22dae2e6384..84629b0a013 100644
--- 
a/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_datetime.groovy
+++ 
b/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_datetime.groovy
@@ -112,7 +112,7 @@ suite("fe_constant_cast_to_datetime") {
     }
     test {
         sql """select cast("2024-05-01T00:00+14:30" as datetime)"""
-        exception "Time zone offset couldn't be larger than 14:00"
+        exception "date/datetime literal [2024-05-01T00:00:00+14:30] is 
invalid"
     }
     test {
         sql """select cast("2024-05-01T00:00+08:25" as datetime)"""


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to