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

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


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 89da1665cc2 branch-4.1: [Fix](ttz) Fix TIMESTAMPTZ elapsed-time 
semantics to use UTC #63161 (#63248)
89da1665cc2 is described below

commit 89da1665cc2112ca44e29cef8236dacf0220ccae
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri May 15 12:31:38 2026 +0800

    branch-4.1: [Fix](ttz) Fix TIMESTAMPTZ elapsed-time semantics to use UTC 
#63161 (#63248)
    
    Cherry-picked from #63161
    
    Co-authored-by: linrrarity <[email protected]>
---
 be/src/core/value/timestamptz_value.h              |   6 +
 .../aggregate_function_sequence_match.cpp          |   3 +
 .../aggregate/aggregate_function_sequence_match.h  |   7 +-
 .../aggregate/aggregate_function_window_funnel.cpp |   7 +-
 .../aggregate/aggregate_function_window_funnel.h   |  34 ++--
 .../aggregate_function_window_funnel_v2.cpp        |   7 +-
 .../aggregate_function_window_funnel_v2.h          |  27 +--
 .../function_date_or_datetime_computation.cpp      |   6 +-
 .../function_date_or_datetime_computation.h        |  16 +-
 .../expressions/functions/agg/SequenceCount.java   |   3 +
 .../functions/agg/SequenceFunction.java            |   2 +-
 .../expressions/functions/agg/SequenceMatch.java   |   3 +
 .../expressions/functions/agg/TopNWeighted.java    |   8 +
 .../expressions/functions/agg/WindowFunnel.java    |  15 +-
 .../expressions/functions/agg/WindowFunnelV2.java  |  15 +-
 .../expressions/functions/scalar/DateDiff.java     |   3 +
 .../expressions/functions/scalar/DaysDiff.java     |   2 +
 .../expressions/functions/scalar/HoursDiff.java    |   3 +
 .../functions/scalar/MicroSecondsDiff.java         |   3 +
 .../functions/scalar/MilliSecondsDiff.java         |   3 +
 .../expressions/functions/scalar/MinutesDiff.java  |   3 +
 .../expressions/functions/scalar/MonthsDiff.java   |   7 +-
 .../expressions/functions/scalar/QuartersDiff.java |   7 +-
 .../expressions/functions/scalar/SecondsDiff.java  |   3 +
 .../expressions/functions/scalar/TimeDiff.java     |  11 ++
 .../expressions/functions/scalar/WeeksDiff.java    |   7 +-
 .../expressions/functions/scalar/YearsDiff.java    |   7 +-
 .../timestamptz/test_timestamptz_utc_functions.out |  33 ++++
 .../test_timestamptz_utc_functions.groovy          | 183 +++++++++++++++++++++
 29 files changed, 378 insertions(+), 56 deletions(-)

diff --git a/be/src/core/value/timestamptz_value.h 
b/be/src/core/value/timestamptz_value.h
index 1a0fa21f004..a3e3861ac2b 100644
--- a/be/src/core/value/timestamptz_value.h
+++ b/be/src/core/value/timestamptz_value.h
@@ -96,6 +96,10 @@ public:
         return _utc_dt.datetime_diff_in_seconds(other._utc_dt);
     }
 
+    int64_t datetime_diff_in_microseconds(const TimestampTzValue& other) const 
{
+        return _utc_dt.datetime_diff_in_microseconds(other._utc_dt);
+    }
+
     template <TimeUnit unit>
     bool date_set_interval(const TimeInterval& interval) {
         return _utc_dt.date_set_interval<unit>(interval);
@@ -143,6 +147,8 @@ public:
         _utc_dt.unix_timestamp(timestamp, ctz);
     }
 
+    std::string debug_string() const { return _utc_dt.debug_string(); }
+
     // Convert UTC time to local time based on the given timezone
     void convert_utc_to_local(const cctz::time_zone& local_time_zone,
                               DateV2Value<DateTimeV2ValueType>& dt) const;
diff --git a/be/src/exprs/aggregate/aggregate_function_sequence_match.cpp 
b/be/src/exprs/aggregate/aggregate_function_sequence_match.cpp
index 2e2d9a6cc60..755dd6a2520 100644
--- a/be/src/exprs/aggregate/aggregate_function_sequence_match.cpp
+++ b/be/src/exprs/aggregate/aggregate_function_sequence_match.cpp
@@ -52,6 +52,9 @@ AggregateFunctionPtr 
create_aggregate_function_sequence_base(const std::string&
     case TYPE_DATEV2:
         return creator_without_type::create<AggregateFunction<TYPE_DATEV2>>(
                 argument_types, result_is_nullable, attr);
+    case TYPE_TIMESTAMPTZ:
+        return 
creator_without_type::create<AggregateFunction<TYPE_TIMESTAMPTZ>>(
+                argument_types, result_is_nullable, attr);
     default:
         return nullptr;
     }
diff --git a/be/src/exprs/aggregate/aggregate_function_sequence_match.h 
b/be/src/exprs/aggregate/aggregate_function_sequence_match.h
index d7084f57695..bb308159328 100644
--- a/be/src/exprs/aggregate/aggregate_function_sequence_match.h
+++ b/be/src/exprs/aggregate/aggregate_function_sequence_match.h
@@ -74,10 +74,6 @@ constexpr auto sequence_match_max_iterations = 1000000l;
 template <PrimitiveType T, typename Derived>
 struct AggregateFunctionSequenceMatchData final {
     using Timestamp = typename PrimitiveTypeTraits<T>::CppType;
-    using NativeType =
-            std::conditional_t<T == TYPE_DATEV2, uint32_t,
-                               std::conditional_t<T == TYPE_DATETIMEV2, 
uint64_t,
-                                                  typename 
PrimitiveTypeTraits<T>::CppType>>;
     using Events = std::bitset<MAX_EVENTS>;
     using TimestampEvents = std::pair<Timestamp, Events>;
     using Comparator = ComparePairFirst<std::less>;
@@ -280,7 +276,7 @@ private:
                         return;
                     }
 
-                    NativeType duration = 0;
+                    uint64_t duration = 0;
                     if (!parse_uint(duration)) {
                         throw_exception("Could not parse number");
                         return;
@@ -618,7 +614,6 @@ class AggregateFunctionSequenceBase
         : public 
IAggregateFunctionDataHelper<AggregateFunctionSequenceMatchData<T, Derived>,
                                               Derived> {
 public:
-    using NativeType = typename PrimitiveTypeTraits<T>::CppType;
     AggregateFunctionSequenceBase(const DataTypes& arguments)
             : 
IAggregateFunctionDataHelper<AggregateFunctionSequenceMatchData<T, Derived>, 
Derived>(
                       arguments) {
diff --git a/be/src/exprs/aggregate/aggregate_function_window_funnel.cpp 
b/be/src/exprs/aggregate/aggregate_function_window_funnel.cpp
index fbd9fde900e..8249e3ee812 100644
--- a/be/src/exprs/aggregate/aggregate_function_window_funnel.cpp
+++ b/be/src/exprs/aggregate/aggregate_function_window_funnel.cpp
@@ -38,10 +38,13 @@ AggregateFunctionPtr 
create_aggregate_function_window_funnel(const std::string&
         return nullptr;
     }
     if (argument_types[2]->get_primitive_type() == TYPE_DATETIMEV2) {
-        return creator_without_type::create<AggregateFunctionWindowFunnel>(
+        return 
creator_without_type::create<AggregateFunctionWindowFunnel<TYPE_DATETIMEV2>>(
+                argument_types, result_is_nullable, attr);
+    } else if (argument_types[2]->get_primitive_type() == TYPE_TIMESTAMPTZ) {
+        return 
creator_without_type::create<AggregateFunctionWindowFunnel<TYPE_TIMESTAMPTZ>>(
                 argument_types, result_is_nullable, attr);
     } else {
-        LOG(WARNING) << "Only support DateTime type as window argument!";
+        LOG(WARNING) << "Only support DateTime or TimeStampTz type as window 
argument!";
         return nullptr;
     }
 }
diff --git a/be/src/exprs/aggregate/aggregate_function_window_funnel.h 
b/be/src/exprs/aggregate/aggregate_function_window_funnel.h
index 07e18fb4a64..778bc903ee9 100644
--- a/be/src/exprs/aggregate/aggregate_function_window_funnel.h
+++ b/be/src/exprs/aggregate/aggregate_function_window_funnel.h
@@ -70,9 +70,11 @@ inline WindowFunnelMode string_to_window_funnel_mode(const 
String& string) {
     }
 }
 
+template <PrimitiveType T>
 struct DataValue {
     using TimestampEvent = std::vector<ColumnUInt8::Container>;
-    std::vector<DateV2Value<DateTimeV2ValueType>> dt;
+    using DateValueType = typename PrimitiveTypeTraits<T>::CppType;
+    std::vector<DateValueType> dt;
     TimestampEvent event_columns_data;
     bool operator<(const DataValue& other) const { return dt < other.dt; }
     void clear() {
@@ -97,15 +99,16 @@ struct DataValue {
     }
 };
 
+template <PrimitiveType T>
 struct WindowFunnelState {
-    static constexpr PrimitiveType PType = PrimitiveType::TYPE_DATETIMEV2;
-    using NativeType = UInt64;
-    using DateValueType = DateV2Value<DateTimeV2ValueType>;
+    static constexpr PrimitiveType PType = T;
+    using NativeType = typename PrimitiveTypeTraits<T>::StorageFieldType;
+    using DateValueType = typename PrimitiveTypeTraits<T>::CppType;
     int event_count = 0;
     int64_t window;
     bool enable_mode;
     WindowFunnelMode window_funnel_mode;
-    DataValue events_list;
+    DataValue<T> events_list;
 
     WindowFunnelState() {
         event_count = 0;
@@ -123,7 +126,8 @@ struct WindowFunnelState {
         window = win;
         window_funnel_mode = enable_mode ? mode : WindowFunnelMode::DEFAULT;
         events_list.dt.emplace_back(
-                assert_cast<const 
ColumnVector<PType>&>(*arg_columns[2]).get_data()[row_num]);
+                assert_cast<const typename 
PrimitiveTypeTraits<PType>::ColumnType&>(*arg_columns[2])
+                        .get_data()[row_num]);
         for (int i = 0; i < event_count; i++) {
             events_list.event_columns_data[i].emplace_back(
                     assert_cast<const ColumnUInt8&>(*arg_columns[3 + 
i]).get_data()[row_num]);
@@ -268,7 +272,7 @@ struct WindowFunnelState {
         }
     }
 
-    void merge(const WindowFunnelState& other) {
+    void merge(const WindowFunnelState<T>& other) {
         if (other.events_list.empty()) {
             return;
         }
@@ -327,7 +331,9 @@ struct WindowFunnelState {
         events_list.clear();
         events_list.dt.resize(size);
         for (auto i = 0; i < size; i++) {
-            read_var_int(*reinterpret_cast<Int64*>(&events_list.dt[i]), in);
+            Int64 timestamp = 0;
+            read_var_int(timestamp, in);
+            events_list.dt[i] = DateValueType(static_cast<UInt64>(timestamp));
         }
         events_list.event_columns_data.resize(event_count);
         for (int64_t i = 0; i < event_count; i++) {
@@ -342,17 +348,19 @@ struct WindowFunnelState {
     }
 };
 
+template <PrimitiveType T>
 class AggregateFunctionWindowFunnel final
-        : public IAggregateFunctionDataHelper<WindowFunnelState, 
AggregateFunctionWindowFunnel>,
+        : public IAggregateFunctionDataHelper<WindowFunnelState<T>,
+                                              
AggregateFunctionWindowFunnel<T>>,
           MultiExpression,
           NullableAggregateFunction {
 public:
     AggregateFunctionWindowFunnel(const DataTypes& argument_types_)
-            : IAggregateFunctionDataHelper<WindowFunnelState, 
AggregateFunctionWindowFunnel>(
+            : IAggregateFunctionDataHelper<WindowFunnelState<T>, 
AggregateFunctionWindowFunnel<T>>(
                       argument_types_) {}
 
     void create(AggregateDataPtr __restrict place) const override {
-        auto data = new (place) WindowFunnelState(
+        auto data = new (place) WindowFunnelState<T>(
                 cast_set<int>(IAggregateFunction::get_argument_types().size() 
- 3));
         /// support window funnel mode from 2.0. See 
`BeExecVersionManager::max_be_exec_version`
         data->enable_mode = IAggregateFunction::version >= 3;
@@ -390,8 +398,8 @@ public:
         // place is essentially an AggregateDataPtr, passed as a 
ConstAggregateDataPtr.
         this->data(const_cast<AggregateDataPtr>(place)).sort();
         assert_cast<ColumnInt32&>(to).get_data().push_back(
-                IAggregateFunctionDataHelper<WindowFunnelState,
-                                             
AggregateFunctionWindowFunnel>::data(place)
+                IAggregateFunctionDataHelper<WindowFunnelState<T>,
+                                             
AggregateFunctionWindowFunnel<T>>::data(place)
                         .get());
     }
 
diff --git a/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.cpp 
b/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.cpp
index 8cb73675c34..0445602a0b1 100644
--- a/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.cpp
+++ b/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.cpp
@@ -40,10 +40,13 @@ AggregateFunctionPtr 
create_aggregate_function_window_funnel_v2(const std::strin
         return nullptr;
     }
     if (argument_types[2]->get_primitive_type() == TYPE_DATETIMEV2) {
-        return creator_without_type::create<AggregateFunctionWindowFunnelV2>(
+        return 
creator_without_type::create<AggregateFunctionWindowFunnelV2<TYPE_DATETIMEV2>>(
+                argument_types, result_is_nullable, attr);
+    } else if (argument_types[2]->get_primitive_type() == TYPE_TIMESTAMPTZ) {
+        return 
creator_without_type::create<AggregateFunctionWindowFunnelV2<TYPE_TIMESTAMPTZ>>(
                 argument_types, result_is_nullable, attr);
     } else {
-        LOG(WARNING) << "Only support DateTime type as window argument!";
+        LOG(WARNING) << "Only support DateTime or TimeStampTz type as window 
argument!";
         return nullptr;
     }
 }
diff --git a/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.h 
b/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.h
index 8fb35addaf9..25557c039c9 100644
--- a/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.h
+++ b/be/src/exprs/aggregate/aggregate_function_window_funnel_v2.h
@@ -77,6 +77,7 @@ void merge_events_list(T& events_list, size_t prefix_size, 
bool prefix_sorted, b
 /// The algorithm uses this to ensure each funnel step comes from a different 
row.
 ///
 /// This approach adds ZERO storage overhead — each event remains 9 bytes 
(UInt64 + UInt8).
+template <PrimitiveType T>
 struct WindowFunnelStateV2 {
     /// (timestamp_int_val, 1-based event_index with continuation flag in bit 
7)
     ///
@@ -136,10 +137,10 @@ struct WindowFunnelStateV2 {
         window = win;
         window_funnel_mode = mode;
 
-        // get_data() returns DateV2Value<DateTimeV2ValueType>; convert to 
packed UInt64
-        auto timestamp = assert_cast<const 
ColumnVector<TYPE_DATETIMEV2>&>(*arg_columns[2])
-                                 .get_data()[row_num]
-                                 .to_date_int_val();
+        auto timestamp =
+                assert_cast<const typename 
PrimitiveTypeTraits<T>::ColumnType&>(*arg_columns[2])
+                        .get_data()[row_num]
+                        .to_date_int_val();
 
         // Iterate from last event to first (reverse order).
         // This ensures that after stable_sort, events with the same timestamp
@@ -174,7 +175,7 @@ struct WindowFunnelStateV2 {
         }
     }
 
-    void merge(const WindowFunnelStateV2& other) {
+    void merge(const WindowFunnelStateV2<T>& other) {
         if (other.events_list.empty()) {
             return;
         }
@@ -617,20 +618,22 @@ private:
     }
 };
 
+template <PrimitiveType T>
 class AggregateFunctionWindowFunnelV2 final
-        : public IAggregateFunctionDataHelper<WindowFunnelStateV2, 
AggregateFunctionWindowFunnelV2>,
+        : public IAggregateFunctionDataHelper<WindowFunnelStateV2<T>,
+                                              
AggregateFunctionWindowFunnelV2<T>>,
           MultiExpression,
           NullableAggregateFunction {
 public:
     AggregateFunctionWindowFunnelV2(const DataTypes& argument_types_)
-            : IAggregateFunctionDataHelper<WindowFunnelStateV2, 
AggregateFunctionWindowFunnelV2>(
-                      argument_types_) {
-        WindowFunnelStateV2::validate_event_count(
+            : IAggregateFunctionDataHelper<WindowFunnelStateV2<T>,
+                                           
AggregateFunctionWindowFunnelV2<T>>(argument_types_) {
+        WindowFunnelStateV2<T>::validate_event_count(
                 cast_set<int>(IAggregateFunction::get_argument_types().size() 
- 3));
     }
 
     void create(AggregateDataPtr __restrict place) const override {
-        new (place) WindowFunnelStateV2(
+        new (place) WindowFunnelStateV2<T>(
                 cast_set<int>(IAggregateFunction::get_argument_types().size() 
- 3));
     }
 
@@ -665,8 +668,8 @@ public:
     void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn& 
to) const override {
         this->data(const_cast<AggregateDataPtr>(place)).sort();
         assert_cast<ColumnInt32&>(to).get_data().push_back(
-                IAggregateFunctionDataHelper<WindowFunnelStateV2,
-                                             
AggregateFunctionWindowFunnelV2>::data(place)
+                IAggregateFunctionDataHelper<WindowFunnelStateV2<T>,
+                                             
AggregateFunctionWindowFunnelV2<T>>::data(place)
                         .get());
     }
 
diff --git a/be/src/exprs/function/function_date_or_datetime_computation.cpp 
b/be/src/exprs/function/function_date_or_datetime_computation.cpp
index 26cda7e4138..8973cab77e3 100644
--- a/be/src/exprs/function/function_date_or_datetime_computation.cpp
+++ b/be/src/exprs/function/function_date_or_datetime_computation.cpp
@@ -186,7 +186,8 @@ using FunctionSubTimeTimestampTz =
 
 #define ALL_FUNCTION_TIME_DIFF(NAME, IMPL)          \
     FUNCTION_TIME_DIFF(NAME, IMPL, TYPE_DATETIMEV2) \
-    FUNCTION_TIME_DIFF(NAME, IMPL, TYPE_DATEV2)
+    FUNCTION_TIME_DIFF(NAME, IMPL, TYPE_DATEV2)     \
+    FUNCTION_TIME_DIFF(NAME, IMPL, TYPE_TIMESTAMPTZ)
 // these diff functions accept all v2 types. but for v1 only datetime.
 ALL_FUNCTION_TIME_DIFF(FunctionDatetimeDateDiff, DateDiffImpl)
 ALL_FUNCTION_TIME_DIFF(FunctionDatetimeTimeDiff, TimeDiffImpl)
@@ -324,7 +325,8 @@ void 
register_function_date_time_computation(SimpleFunctionFactory& factory) {
 
 #define REGISTER_ALL_DATEV2_FUNCTIONS_DIFF(NAME)          \
     REGISTER_DATEV2_FUNCTIONS_DIFF(NAME, TYPE_DATETIMEV2) \
-    REGISTER_DATEV2_FUNCTIONS_DIFF(NAME, TYPE_DATEV2)
+    REGISTER_DATEV2_FUNCTIONS_DIFF(NAME, TYPE_DATEV2)     \
+    REGISTER_DATEV2_FUNCTIONS_DIFF(NAME, TYPE_TIMESTAMPTZ)
 
     REGISTER_ALL_DATEV2_FUNCTIONS_DIFF(FunctionDatetimeDateDiff)
     REGISTER_ALL_DATEV2_FUNCTIONS_DIFF(FunctionDatetimeTimeDiff)
diff --git a/be/src/exprs/function/function_date_or_datetime_computation.h 
b/be/src/exprs/function/function_date_or_datetime_computation.h
index 90938d78d19..b5bc21bb00d 100644
--- a/be/src/exprs/function/function_date_or_datetime_computation.h
+++ b/be/src/exprs/function/function_date_or_datetime_computation.h
@@ -573,7 +573,8 @@ struct TimeDiffImpl {
     using ValueType = typename PrimitiveTypeTraits<DateType>::CppType;
     using ArgType = typename 
PrimitiveTypeTraits<DateType>::DataType::FieldType;
     //TODO: remove V1 since FE already removed it.
-    static constexpr bool UsingTimev2 = is_date_v2_or_datetime_v2(DateType);
+    static constexpr bool UsingTimev2 =
+            is_date_v2_or_datetime_v2(DateType) || DateType == 
TYPE_TIMESTAMPTZ;
     static constexpr PrimitiveType ReturnType = TYPE_TIMEV2;
 
     static constexpr auto name = "timediff";
@@ -601,8 +602,19 @@ struct TimeDiffImpl {
         return 
std::make_shared<DataTypeTimeV2>(arguments[0].type->get_scale());
     }
 };
+
+template <TimeUnit UNIT, typename T0, typename T1>
+int64_t diff_on_utc_datetime(const T0& ts1, const T1& ts0) {
+    return datetime_diff<UNIT>(ts1, ts0);
+}
+
+template <TimeUnit UNIT>
+int64_t diff_on_utc_datetime(const TimestampTzValue& ts1, const 
TimestampTzValue& ts0) {
+    return datetime_diff<UNIT>(ts1.utc_dt(), ts0.utc_dt());
+}
+
 #define TIME_DIFF_FUNCTION_IMPL(CLASS, NAME, UNIT) \
-    DECLARE_DATE_FUNCTIONS(CLASS, NAME, TYPE_BIGINT, 
datetime_diff<TimeUnit::UNIT>(ts1, ts0))
+    DECLARE_DATE_FUNCTIONS(CLASS, NAME, TYPE_BIGINT, 
diff_on_utc_datetime<TimeUnit::UNIT>(ts1, ts0))
 
 // all these functions implemented by datediff
 TIME_DIFF_FUNCTION_IMPL(YearsDiffImpl, years_diff, YEAR);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceCount.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceCount.java
index 5cbc0903979..e4ad5956c1c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceCount.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceCount.java
@@ -27,6 +27,7 @@ import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.base.Preconditions;
@@ -43,6 +44,8 @@ public class SequenceCount extends 
NotNullableAggregateFunction
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .varArgs(StringType.INSTANCE, DateV2Type.INSTANCE, 
BooleanType.INSTANCE),
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .varArgs(StringType.INSTANCE, TimeStampTzType.WILDCARD, 
BooleanType.INSTANCE),
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .varArgs(StringType.INSTANCE, DateTimeV2Type.WILDCARD, 
BooleanType.INSTANCE)
     );
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
index 673c91479a4..6bbf4e6e992 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
@@ -36,7 +36,7 @@ public interface SequenceFunction extends FunctionTrait {
         }
         if (!getArgumentType(1).isDateLikeType()) {
             throw new AnalysisException("The timestamp params of " + 
functionName
-                    + " function must be DATE or DATETIME, but it is " + 
getArgumentType(1));
+                    + " function must be DATE, DATETIME or TIMESTAMPTZ, but it 
is " + getArgumentType(1));
         }
         String pattern = ((StringLikeLiteral) firstArg).getStringValue();
         if (!FunctionCallExpr.parsePattern(pattern)) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceMatch.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceMatch.java
index c6cf9e436a4..c9f15731a19 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceMatch.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceMatch.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.base.Preconditions;
@@ -41,6 +42,8 @@ public class SequenceMatch extends NullableAggregateFunction
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
             FunctionSignature.ret(BooleanType.INSTANCE)
                     .varArgs(StringType.INSTANCE, DateV2Type.INSTANCE, 
BooleanType.INSTANCE),
+            FunctionSignature.ret(BooleanType.INSTANCE)
+                    .varArgs(StringType.INSTANCE, TimeStampTzType.WILDCARD, 
BooleanType.INSTANCE),
             FunctionSignature.ret(BooleanType.INSTANCE)
                     .varArgs(StringType.INSTANCE, DateTimeV2Type.WILDCARD, 
BooleanType.INSTANCE)
     );
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/TopNWeighted.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/TopNWeighted.java
index 79db1318c62..1ed1586f99f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/TopNWeighted.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/TopNWeighted.java
@@ -35,6 +35,7 @@ import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.LargeIntType;
 import org.apache.doris.nereids.types.SmallIntType;
 import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.types.TinyIntType;
 import org.apache.doris.nereids.types.VarcharType;
 
@@ -71,6 +72,8 @@ public class TopNWeighted extends NullableAggregateFunction
                                     .args(FloatType.INSTANCE, 
BigIntType.INSTANCE, IntegerType.INSTANCE),
                             
FunctionSignature.ret(ArrayType.of(DateV2Type.INSTANCE))
                     .args(DateV2Type.INSTANCE, BigIntType.INSTANCE, 
IntegerType.INSTANCE),
+            FunctionSignature.ret(ArrayType.of(TimeStampTzType.WILDCARD))
+                    .args(TimeStampTzType.WILDCARD, BigIntType.INSTANCE, 
IntegerType.INSTANCE),
             FunctionSignature.ret(ArrayType.of(DateTimeV2Type.WILDCARD))
                     .args(DateTimeV2Type.WILDCARD, BigIntType.INSTANCE, 
IntegerType.INSTANCE),
             FunctionSignature.ret(ArrayType.of(StringType.INSTANCE))
@@ -105,6 +108,11 @@ public class TopNWeighted extends NullableAggregateFunction
                                                     IntegerType.INSTANCE),
                             
FunctionSignature.ret(ArrayType.of(DateV2Type.INSTANCE))
                     .args(DateV2Type.INSTANCE, BigIntType.INSTANCE, 
IntegerType.INSTANCE, IntegerType.INSTANCE),
+            FunctionSignature.ret(ArrayType.of(TimeStampTzType.WILDCARD))
+                    .args(TimeStampTzType.WILDCARD,
+                            BigIntType.INSTANCE,
+                            IntegerType.INSTANCE,
+                            IntegerType.INSTANCE),
             FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT)
                     .args(DateTimeV2Type.WILDCARD,
                             BigIntType.INSTANCE,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnel.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnel.java
index 848d3cfd6d5..ef60c0a8c07 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnel.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnel.java
@@ -24,14 +24,17 @@ import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSi
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.BooleanType;
+import org.apache.doris.nereids.types.DataType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 import java.util.List;
 
@@ -42,6 +45,9 @@ public class WindowFunnel extends NullableAggregateFunction
         implements ExplicitlyCastableSignature {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(IntegerType.INSTANCE)
+                    .varArgs(BigIntType.INSTANCE, StringType.INSTANCE, 
TimeStampTzType.WILDCARD,
+                            BooleanType.INSTANCE),
             FunctionSignature.ret(IntegerType.INSTANCE)
                     .varArgs(BigIntType.INSTANCE, StringType.INSTANCE, 
DateTimeV2Type.WILDCARD,
                             BooleanType.INSTANCE)
@@ -84,7 +90,8 @@ public class WindowFunnel extends NullableAggregateFunction
             throw new AnalysisException("The mode params of " + functionName + 
" function must be string");
         }
         if (!getArgumentType(2).isDateLikeType()) {
-            throw new AnalysisException("The 3rd param of " + functionName + " 
function must be DATE or DATETIME");
+            throw new AnalysisException("The 3rd param of " + functionName
+                    + " function must be DATE, DATETIME or TIMESTAMPTZ");
         }
         for (int i = 3; i < arity(); i++) {
             if (!getArgumentType(i).isBooleanType()) {
@@ -98,9 +105,9 @@ public class WindowFunnel extends NullableAggregateFunction
     public FunctionSignature computeSignature(FunctionSignature signature) {
         FunctionSignature functionSignature = 
super.computeSignature(signature);
         if (functionSignature.getArgType(2) instanceof DateV2Type) {
-            return functionSignature.withArgumentTypes(getArguments(), (index, 
originType, arg) ->
-                    (index == 2) ? DateTimeV2Type.SYSTEM_DEFAULT : originType
-            );
+            List<DataType> newTypes = 
Lists.newArrayList(functionSignature.argumentsTypes);
+            newTypes.set(2, DateTimeV2Type.SYSTEM_DEFAULT);
+            return 
functionSignature.withArgumentTypes(functionSignature.hasVarArgs, newTypes);
         }
         return functionSignature;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnelV2.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnelV2.java
index fa4f5d8e469..697774eb47f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnelV2.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/WindowFunnelV2.java
@@ -24,14 +24,17 @@ import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSi
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.BooleanType;
+import org.apache.doris.nereids.types.DataType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 import java.util.List;
 
@@ -45,6 +48,9 @@ public class WindowFunnelV2 extends NullableAggregateFunction
     public static final int MAX_EVENT_CONDITIONS = 127;
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(IntegerType.INSTANCE)
+                    .varArgs(BigIntType.INSTANCE, StringType.INSTANCE, 
TimeStampTzType.WILDCARD,
+                            BooleanType.INSTANCE),
             FunctionSignature.ret(IntegerType.INSTANCE)
                     .varArgs(BigIntType.INSTANCE, StringType.INSTANCE, 
DateTimeV2Type.WILDCARD,
                             BooleanType.INSTANCE)
@@ -93,7 +99,8 @@ public class WindowFunnelV2 extends NullableAggregateFunction
             throw new AnalysisException("The mode params of " + functionName + 
" function must be string");
         }
         if (!getArgumentType(2).isDateLikeType()) {
-            throw new AnalysisException("The 3rd param of " + functionName + " 
function must be DATE or DATETIME");
+            throw new AnalysisException("The 3rd param of " + functionName
+                    + " function must be DATE, DATETIME or TIMESTAMPTZ");
         }
         for (int i = 3; i < arity(); i++) {
             if (!getArgumentType(i).isBooleanType()) {
@@ -107,9 +114,9 @@ public class WindowFunnelV2 extends 
NullableAggregateFunction
     public FunctionSignature computeSignature(FunctionSignature signature) {
         FunctionSignature functionSignature = 
super.computeSignature(signature);
         if (functionSignature.getArgType(2) instanceof DateV2Type) {
-            return functionSignature.withArgumentTypes(getArguments(), (index, 
originType, arg) ->
-                    (index == 2) ? DateTimeV2Type.SYSTEM_DEFAULT : originType
-            );
+            List<DataType> newTypes = 
Lists.newArrayList(functionSignature.argumentsTypes);
+            newTypes.set(2, DateTimeV2Type.SYSTEM_DEFAULT);
+            return 
functionSignature.withArgumentTypes(functionSignature.hasVarArgs, newTypes);
         }
         return functionSignature;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateDiff.java
index 16a2e1cf325..2d766295f64 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateDiff.java
@@ -26,6 +26,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -39,6 +40,8 @@ public class DateDiff extends ScalarFunction
         implements BinaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(IntegerType.INSTANCE)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(IntegerType.INSTANCE)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
             
FunctionSignature.ret(IntegerType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DaysDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DaysDiff.java
index f894c9f09a6..012ad7c6399 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DaysDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DaysDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,6 +42,7 @@ public class DaysDiff extends ScalarFunction implements 
BinaryExpression, Explic
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(BigIntType.INSTANCE).args(TimeStampTzType.WILDCARD, 
TimeStampTzType.WILDCARD),
             
FunctionSignature.ret(BigIntType.INSTANCE).args(DateTimeV2Type.WILDCARD, 
DateTimeV2Type.WILDCARD),
             
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HoursDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HoursDiff.java
index b4d8f4ce181..c601497c45d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HoursDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HoursDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,6 +42,8 @@ public class HoursDiff extends ScalarFunction implements 
BinaryExpression, Expli
         PropagateNullable, DateDiffMonotonic {
 
     public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
             
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MicroSecondsDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MicroSecondsDiff.java
index 434043119a0..26081a347a0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MicroSecondsDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MicroSecondsDiff.java
@@ -27,6 +27,7 @@ import 
org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -40,6 +41,8 @@ public class MicroSecondsDiff extends ScalarFunction 
implements BinaryExpression
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD)
             );
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MilliSecondsDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MilliSecondsDiff.java
index 3f07927f920..9cda6cb8e4a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MilliSecondsDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MilliSecondsDiff.java
@@ -27,6 +27,7 @@ import 
org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -40,6 +41,8 @@ public class MilliSecondsDiff extends ScalarFunction 
implements BinaryExpression
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD)
             );
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinutesDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinutesDiff.java
index a66b5beb53f..f1a320932f0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinutesDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinutesDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,6 +42,8 @@ public class MinutesDiff extends ScalarFunction implements 
BinaryExpression, Exp
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
             
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsDiff.java
index 32735b3931e..7c11156861b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,9 +42,11 @@ public class MonthsDiff extends ScalarFunction implements 
BinaryExpression, Expl
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
-            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE),
             FunctionSignature.ret(BigIntType.INSTANCE)
-                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD));
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
+            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
 
     /**
      * constructor with 2 arguments.
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuartersDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuartersDiff.java
index d13b49db683..ec8f889baa5 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuartersDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuartersDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,9 +42,11 @@ public class QuartersDiff extends ScalarFunction implements 
BinaryExpression, Ex
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
-            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE),
             FunctionSignature.ret(BigIntType.INSTANCE)
-                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
+            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE)
     );
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondsDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondsDiff.java
index aab4fb18054..bb7df199d28 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondsDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondsDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,6 +42,8 @@ public class SecondsDiff extends ScalarFunction implements 
BinaryExpression, Exp
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(BigIntType.INSTANCE)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
             
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
index 7602743ec5c..a25ce2d45f8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
@@ -25,6 +25,7 @@ import 
org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.types.TimeV2Type;
 
 import com.google.common.base.Preconditions;
@@ -39,6 +40,8 @@ public class TimeDiff extends ScalarFunction
         implements BinaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(TimeV2Type.WILDCARD)
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
             FunctionSignature.ret(TimeV2Type.WILDCARD)
                     .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
             
FunctionSignature.ret(TimeV2Type.SYSTEM_DEFAULT).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
@@ -83,11 +86,19 @@ public class TimeDiff extends ScalarFunction
             DateTimeV2Type left = (DateTimeV2Type) 
getArgument(0).getDataType();
             scale = Math.max(scale, left.getScale());
             useTimev2 = true;
+        } else if (getArgument(0).getDataType() instanceof TimeStampTzType) {
+            TimeStampTzType left = (TimeStampTzType) 
getArgument(0).getDataType();
+            scale = Math.max(scale, left.getScale());
+            useTimev2 = true;
         }
         if (getArgument(1).getDataType() instanceof DateTimeV2Type) {
             DateTimeV2Type right = (DateTimeV2Type) 
getArgument(1).getDataType();
             scale = Math.max(scale, right.getScale());
             useTimev2 = true;
+        } else if (getArgument(1).getDataType() instanceof TimeStampTzType) {
+            TimeStampTzType right = (TimeStampTzType) 
getArgument(1).getDataType();
+            scale = Math.max(scale, right.getScale());
+            useTimev2 = true;
         }
         if (useTimev2) {
             signature = signature.withReturnType(TimeV2Type.of(scale));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeeksDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeeksDiff.java
index cc884d44911..771d43d2cb3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeeksDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeeksDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,9 +42,11 @@ public class WeeksDiff extends ScalarFunction implements 
BinaryExpression, Expli
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
-            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE),
             FunctionSignature.ret(BigIntType.INSTANCE)
-                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD));
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
+            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
 
     /**
      * constructor with 2 arguments.
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsDiff.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsDiff.java
index 060daf4fc57..da227338e0a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsDiff.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsDiff.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.TimeStampTzType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -41,9 +42,11 @@ public class YearsDiff extends ScalarFunction implements 
BinaryExpression, Expli
         PropagateNullable, DateDiffMonotonic {
 
     private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
-            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE),
             FunctionSignature.ret(BigIntType.INSTANCE)
-                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD));
+                    .args(TimeStampTzType.WILDCARD, TimeStampTzType.WILDCARD),
+            FunctionSignature.ret(BigIntType.INSTANCE)
+                    .args(DateTimeV2Type.WILDCARD, DateTimeV2Type.WILDCARD),
+            
FunctionSignature.ret(BigIntType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
 
     /**
      * constructor with 2 arguments.
diff --git 
a/regression-test/data/datatype_p0/timestamptz/test_timestamptz_utc_functions.out
 
b/regression-test/data/datatype_p0/timestamptz/test_timestamptz_utc_functions.out
new file mode 100644
index 00000000000..12744160ea0
--- /dev/null
+++ 
b/regression-test/data/datatype_p0/timestamptz/test_timestamptz_utc_functions.out
@@ -0,0 +1,33 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !timestamptz_diff_utc --
+600000 600000000       600     10      0       0       0       0       0       
0       0       00:10:00.000000
+
+-- !timestamptz_diff_ny --
+600000 600000000       600     10      0       0       0       0       0       
0       0       00:10:00.000000
+
+-- !timestamptz_diff_spring_forward_ny --
+600000 600     10      0       00:10:00.000000
+
+-- !timestamptz_diff_mixed_scale_ny --
+600123 600123000       600     00:10:00.123
+
+-- !timestamptz_agg_utc --
+true   1       2       2
+
+-- !timestamptz_agg_ny --
+true   1       2       2
+
+-- !timestamptz_agg_by_group_ny --
+1      true    1       2       2
+3      true    1       2       2
+
+-- !timestamptz_column_diff_ny --
+600000 600     00:10:00.000000
+
+-- !timestamptz_topn_weighted --
+1      2024-11-03 06:30:00.000000+00:00        2024-11-03 06:30:00.000000+00:00
+3      2024-03-10 07:30:00.000000+00:00        2024-03-10 07:30:00.000000+00:00
+
+-- !timestamptz_null --
+\N     0
+
diff --git 
a/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_utc_functions.groovy
 
b/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_utc_functions.groovy
new file mode 100644
index 00000000000..69c15e4d197
--- /dev/null
+++ 
b/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_utc_functions.groovy
@@ -0,0 +1,183 @@
+// 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_timestamptz_utc_functions") {
+    sql "SET enable_nereids_planner = true;"
+    sql "SET enable_fallback_to_original_planner = false;"
+
+    // Fall-back DST fold: 
+    // lhs UTC=2024-11-03 06:05:00, NY local=2024-11-03 01:05:00;
+    // rhs UTC=2024-11-03 05:55:00, NY local=2024-11-03 01:55:00.
+    // Expected: TIMESTAMPTZ functions use UTC instants, so lhs - rhs = +10 
minutes, not local -50 minutes.
+    def lhs = "CAST('2024-11-03 01:05:00 -05:00' AS TIMESTAMPTZ(6))"
+    def rhs = "CAST('2024-11-03 01:55:00 -04:00' AS TIMESTAMPTZ(6))"
+
+    // Spring-forward DST gap: 
+    // lhs UTC=2024-03-10 07:05:00, NY local=2024-03-10 03:05:00;
+    // rhs UTC=2024-03-10 06:55:00, NY local=2024-03-10 01:55:00.
+    // Expected: TIMESTAMPTZ functions use UTC instants, so lhs - rhs = +10 
minutes, not local +70 minutes.
+    def springLhs = "CAST('2024-03-10 03:05:00 -04:00' AS TIMESTAMPTZ(6))"
+    def springRhs = "CAST('2024-03-10 01:55:00 -05:00' AS TIMESTAMPTZ(6))"
+
+    // Mixed scale: lhs UTC=2024-11-03 06:05:00.123, rhs UTC=2024-11-03 
05:55:00.
+    // Expected: lhs - rhs = +600123 ms, and non-default TIMESTAMPTZ scales 
bind directly.
+    def scale3Lhs = "CAST('2024-11-03 01:05:00.123 -05:00' AS TIMESTAMPTZ(3))"
+    def scale0Rhs = "CAST('2024-11-03 01:55:00 -04:00' AS TIMESTAMPTZ(0))"
+
+    // Scalar diff results should be identical in UTC and America/New_York 
sessions.
+    sql "SET time_zone = '+00:00';"
+    qt_timestamptz_diff_utc """
+        SELECT milliseconds_diff(${lhs}, ${rhs}) AS ms_diff,
+               microseconds_diff(${lhs}, ${rhs}) AS us_diff,
+               seconds_diff(${lhs}, ${rhs}) AS sec_diff,
+               minutes_diff(${lhs}, ${rhs}) AS min_diff,
+               hours_diff(${lhs}, ${rhs}) AS hour_diff,
+               days_diff(${lhs}, ${rhs}) AS day_diff,
+               weeks_diff(${lhs}, ${rhs}) AS week_diff,
+               months_diff(${lhs}, ${rhs}) AS month_diff,
+               quarters_diff(${lhs}, ${rhs}) AS quarter_diff,
+               years_diff(${lhs}, ${rhs}) AS year_diff,
+               datediff(${lhs}, ${rhs}) AS date_diff,
+               timediff(${lhs}, ${rhs}) AS time_diff;
+    """
+
+    sql "SET time_zone = 'America/New_York';"
+    qt_timestamptz_diff_ny """
+        SELECT milliseconds_diff(${lhs}, ${rhs}) AS ms_diff,
+               microseconds_diff(${lhs}, ${rhs}) AS us_diff,
+               seconds_diff(${lhs}, ${rhs}) AS sec_diff,
+               minutes_diff(${lhs}, ${rhs}) AS min_diff,
+               hours_diff(${lhs}, ${rhs}) AS hour_diff,
+               days_diff(${lhs}, ${rhs}) AS day_diff,
+               weeks_diff(${lhs}, ${rhs}) AS week_diff,
+               months_diff(${lhs}, ${rhs}) AS month_diff,
+               quarters_diff(${lhs}, ${rhs}) AS quarter_diff,
+               years_diff(${lhs}, ${rhs}) AS year_diff,
+               datediff(${lhs}, ${rhs}) AS date_diff,
+               timediff(${lhs}, ${rhs}) AS time_diff;
+    """
+
+    qt_timestamptz_diff_spring_forward_ny """
+        SELECT milliseconds_diff(${springLhs}, ${springRhs}) AS ms_diff,
+               seconds_diff(${springLhs}, ${springRhs}) AS sec_diff,
+               minutes_diff(${springLhs}, ${springRhs}) AS min_diff,
+               hours_diff(${springLhs}, ${springRhs}) AS hour_diff,
+               timediff(${springLhs}, ${springRhs}) AS time_diff;
+    """
+
+    qt_timestamptz_diff_mixed_scale_ny """
+        SELECT milliseconds_diff(${scale3Lhs}, ${scale0Rhs}) AS ms_diff,
+               microseconds_diff(${scale3Lhs}, ${scale0Rhs}) AS us_diff,
+               seconds_diff(${scale3Lhs}, ${scale0Rhs}) AS sec_diff,
+               timediff(${scale3Lhs}, ${scale0Rhs}) AS time_diff;
+    """
+
+    testFoldConst("""
+        SELECT milliseconds_diff(${lhs}, ${rhs}),
+               seconds_diff(${lhs}, ${rhs}),
+               timediff(${lhs}, ${rhs});
+    """)
+
+    sql "DROP TABLE IF EXISTS tz_utc_function_events;"
+    sql """
+        CREATE TABLE tz_utc_function_events (
+            id INT,
+            grp INT,
+            weight BIGINT,
+            ts TIMESTAMPTZ(6),
+            e1 BOOLEAN,
+            e2 BOOLEAN
+        )
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES('replication_num' = '1');
+    """
+    sql """
+        INSERT INTO tz_utc_function_events VALUES
+        (1, 1, 10, CAST('2024-11-03 01:55:00 -04:00' AS TIMESTAMPTZ(6)), true, 
false),
+        (2, 1, 20, CAST('2024-11-03 01:05:00 -05:00' AS TIMESTAMPTZ(6)), 
false, true),
+        (3, 1, 30, CAST('2024-11-03 01:30:00 -05:00' AS TIMESTAMPTZ(6)), 
false, false),
+        (4, 2, 40, NULL, true, false),
+        (5, 3, 50, CAST('2024-03-10 01:55:00 -05:00' AS TIMESTAMPTZ(6)), true, 
false),
+        (6, 3, 60, CAST('2024-03-10 03:05:00 -04:00' AS TIMESTAMPTZ(6)), 
false, true),
+        (7, 3, 70, CAST('2024-03-10 03:30:00 -04:00' AS TIMESTAMPTZ(6)), 
false, false);
+    """
+
+    // Aggregates should also compare TIMESTAMPTZ values by UTC instant.
+    sql "SET time_zone = '+00:00';"
+    qt_timestamptz_agg_utc """
+        SELECT sequence_match('(?1)(?t<=600)(?2)', ts, e1, e2) AS seq_match,
+               sequence_count('(?1)(?t<=600)(?2)', ts, e1, e2) AS seq_count,
+               window_funnel(600, 'default', ts, e1, e2) AS funnel_v1,
+               window_funnel_v2(600, 'default', ts, e1, e2) AS funnel_v2
+        FROM tz_utc_function_events
+        WHERE grp = 1;
+    """
+
+    sql "SET time_zone = 'America/New_York';"
+    qt_timestamptz_agg_ny """
+        SELECT sequence_match('(?1)(?t<=600)(?2)', ts, e1, e2) AS seq_match,
+               sequence_count('(?1)(?t<=600)(?2)', ts, e1, e2) AS seq_count,
+               window_funnel(600, 'default', ts, e1, e2) AS funnel_v1,
+               window_funnel_v2(600, 'default', ts, e1, e2) AS funnel_v2
+        FROM tz_utc_function_events
+        WHERE grp = 1;
+    """
+
+    // Grouped aggregation verifies both DST shapes.
+    order_qt_timestamptz_agg_by_group_ny """
+        SELECT grp,
+               sequence_match('(?1)(?t<=600)(?2)', ts, e1, e2) AS seq_match,
+               sequence_count('(?1)(?t<=600)(?2)', ts, e1, e2) AS seq_count,
+               window_funnel(600, 'default', ts, e1, e2) AS funnel_v1,
+               window_funnel_v2(600, 'default', ts, e1, e2) AS funnel_v2
+        FROM tz_utc_function_events
+        WHERE grp IN (1, 3)
+        GROUP BY grp
+        ORDER BY grp;
+    """
+
+    // Column inputs should also use UTC instant semantics.
+    qt_timestamptz_column_diff_ny """
+        SELECT milliseconds_diff(t2.ts, t1.ts) AS ms_diff,
+               seconds_diff(t2.ts, t1.ts) AS sec_diff,
+               timediff(t2.ts, t1.ts) AS time_diff
+        FROM tz_utc_function_events t1, tz_utc_function_events t2
+        WHERE t1.id = 1 AND t2.id = 2;
+    """
+
+    // topn_weighted should preserve TIMESTAMPTZ as the result array item type.
+    sql "SET time_zone = '+00:00';"
+    order_qt_timestamptz_topn_weighted """
+        SELECT grp,
+               CAST(topn_weighted(ts, weight, 2)[1] AS VARCHAR(64)) AS top_ts,
+               CAST(topn_weighted(ts, weight, 2, 100)[1] AS VARCHAR(64)) AS 
top_ts_with_default
+        FROM tz_utc_function_events
+        WHERE grp IN (1, 3)
+        GROUP BY grp
+        ORDER BY grp;
+    """
+
+    // NULL input remains NULL, and sequence_count ignores the NULL timestamp 
row.
+    qt_timestamptz_null """
+        SELECT milliseconds_diff(MAX(ts), NULL), sequence_count('(?1)(?2)', 
ts, e1, e2)
+        FROM tz_utc_function_events
+        WHERE grp = 2;
+    """
+
+    sql "SET time_zone = default;"
+}


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

Reply via email to