diff --git a/modules/drivers/athena/test/metabase/test/data/athena.clj b/modules/drivers/athena/test/metabase/test/data/athena.clj
index 69444f4ea224bd29879dd3f6c016ec6717200300..19d4fb25fa483832ff2bd9c013f1bbbf9df338e0 100644
--- a/modules/drivers/athena/test/metabase/test/data/athena.clj
+++ b/modules/drivers/athena/test/metabase/test/data/athena.clj
@@ -194,3 +194,7 @@
 (defmethod tx/supports-time-type? :athena
   [_driver]
   false)
+
+(defmethod tx/supports-timestamptz-type? :athena
+  [_driver]
+  false)
diff --git a/modules/drivers/mongo/test/metabase/test/data/mongo.clj b/modules/drivers/mongo/test/metabase/test/data/mongo.clj
index 7c0e342ee5ae478ab366286b783f972124a8cfee..fd6d6ae1f7c9b277d1cdb15dc6b6c5bb6bcee551 100644
--- a/modules/drivers/mongo/test/metabase/test/data/mongo.clj
+++ b/modules/drivers/mongo/test/metabase/test/data/mongo.clj
@@ -13,6 +13,7 @@
 (tx/add-test-extensions! :mongo)
 
 (defmethod tx/supports-time-type? :mongo [_driver] false)
+(defmethod tx/supports-timestamptz-type? :mongo [_driver] false)
 
 (defn ssl-required?
   "Returns if the mongo server requires an SSL connection."
diff --git a/modules/drivers/sqlite/test/metabase/test/data/sqlite.clj b/modules/drivers/sqlite/test/metabase/test/data/sqlite.clj
index f9fd1f4a0bcbfe06c8c31ecc1532bd620f910715..6d7da7fcdb13dedf4311dabe9b2401d2eb9ba30c 100644
--- a/modules/drivers/sqlite/test/metabase/test/data/sqlite.clj
+++ b/modules/drivers/sqlite/test/metabase/test/data/sqlite.clj
@@ -6,6 +6,8 @@
 
 (sql-jdbc.tx/add-test-extensions! :sqlite)
 
+(defmethod tx/supports-timestamptz-type? :sqlite [_driver] false)
+
 (defmethod tx/dbdef->connection-details :sqlite
   [_driver _context dbdef]
   {:db (str (tx/escaped-database-name dbdef) ".sqlite")})
diff --git a/src/metabase/driver/h2.clj b/src/metabase/driver/h2.clj
index 0a03f2048d3478ea5970708369e0e1cbfce19622..1e75f57a7be55a603e64e6f3940f18d276c9bb3b 100644
--- a/src/metabase/driver/h2.clj
+++ b/src/metabase/driver/h2.clj
@@ -239,22 +239,24 @@
 (defn- format-datetime   [format-str expr] (hsql/call :formatdatetime expr (hx/literal format-str)))
 (defn- parse-datetime    [format-str expr] (hsql/call :parsedatetime expr  (hx/literal format-str)))
 (defn- trunc-with-format [format-str expr] (parse-datetime format-str (format-datetime format-str expr)))
+(defn- extract           [unit expr]       (hsql/call :extract unit (hx/cast :timestamp expr)))
 
 (defmethod sql.qp/date [:h2 :minute]          [_ _ expr] (trunc-with-format "yyyyMMddHHmm" expr))
 (defmethod sql.qp/date [:h2 :minute-of-hour]  [_ _ expr] (hx/minute expr))
 (defmethod sql.qp/date [:h2 :hour]            [_ _ expr] (trunc-with-format "yyyyMMddHH" expr))
-(defmethod sql.qp/date [:h2 :hour-of-day]     [_ _ expr] (hx/hour expr))
+(defmethod sql.qp/date [:h2 :hour-of-day]     [_ _ expr] (extract :hour expr))
 (defmethod sql.qp/date [:h2 :day]             [_ _ expr] (hx/->date expr))
-(defmethod sql.qp/date [:h2 :day-of-month]    [_ _ expr] (hsql/call :day_of_month expr))
-(defmethod sql.qp/date [:h2 :day-of-year]     [_ _ expr] (hsql/call :day_of_year expr))
+(defmethod sql.qp/date [:h2 :day-of-month]    [_ _ expr] (extract :day expr))
+(defmethod sql.qp/date [:h2 :day-of-year]     [_ _ expr] (extract :day_of_year expr))
 (defmethod sql.qp/date [:h2 :month]           [_ _ expr] (trunc-with-format "yyyyMM" expr))
-(defmethod sql.qp/date [:h2 :month-of-year]   [_ _ expr] (hx/month expr))
-(defmethod sql.qp/date [:h2 :quarter-of-year] [_ _ expr] (hx/quarter expr))
-(defmethod sql.qp/date [:h2 :year]            [_ _ expr] (parse-datetime "yyyy" (hx/year expr)))
+(defmethod sql.qp/date [:h2 :month-of-year]   [_ _ expr] (extract :month expr))
+(defmethod sql.qp/date [:h2 :quarter-of-year] [_ _ expr] (extract :quarter expr))
+(defmethod sql.qp/date [:h2 :year]            [_ _ expr] (parse-datetime "yyyy" (extract :year expr)))
+(defmethod sql.qp/date [:h2 :year-of-era]     [_ _ expr] (extract :year expr))
 
 (defmethod sql.qp/date [:h2 :day-of-week]
   [_ _ expr]
-  (sql.qp/adjust-day-of-week :h2 (hsql/call :iso_day_of_week expr)))
+  (sql.qp/adjust-day-of-week :h2 (extract :iso_day_of_week expr)))
 
 (defmethod sql.qp/date [:h2 :week]
   [_ _ expr]
@@ -262,7 +264,7 @@
                                      (hx/- 1 (sql.qp/date :h2 :day-of-week expr))
                                      :day))
 
-(defmethod sql.qp/date [:h2 :week-of-year-iso] [_ _ expr] (hsql/call :iso_week expr))
+(defmethod sql.qp/date [:h2 :week-of-year-iso] [_ _ expr] (extract :iso_week expr))
 
 ;; Rounding dates to quarters is a bit involved but still doable. Here's the plan:
 ;; *  extract the year and quarter from the date;
diff --git a/test/metabase/driver/sql/query_processor_test.clj b/test/metabase/driver/sql/query_processor_test.clj
index a3fd3e8784b9a7e9f2a14a0a2d224a4431abb30c..8738750bf4ee18ebdf132ab63b4fbd7d41e1e685 100644
--- a/test/metabase/driver/sql/query_processor_test.clj
+++ b/test/metabase/driver/sql/query_processor_test.clj
@@ -380,7 +380,7 @@
     (mt/dataset sample-dataset
       (is (= '{:select   [source.PRODUCTS__via__PRODUCT_ID__CATEGORY AS PRODUCTS__via__PRODUCT_ID__CATEGORY
                           source.PEOPLE__via__USER_ID__SOURCE AS PEOPLE__via__USER_ID__SOURCE
-                          parsedatetime (year (source.CREATED_AT) "yyyy") AS CREATED_AT
+                          parsedatetime (extract (year from CAST (source.CREATED_AT AS timestamp)) "yyyy") AS CREATED_AT
                           source.pivot-grouping AS pivot-grouping
                           count (*) AS count]
                :from     [{:select    [ORDERS.ID                          AS ID
@@ -408,16 +408,16 @@
                            AND
                            (source.PRODUCTS__via__PRODUCT_ID__CATEGORY = ? OR source.PRODUCTS__via__PRODUCT_ID__CATEGORY = ?)
                            AND
-                           source.CREATED_AT >= parsedatetime (year (dateadd ("year" CAST (-2 AS long) now ())) "yyyy")
+                           source.CREATED_AT >= parsedatetime (extract (year from CAST (dateadd ("year" CAST (-2 AS long) now ()) AS timestamp)) "yyyy")
                            AND
-                           source.CREATED_AT < parsedatetime (year (now ()) "yyyy"))]
+                           source.CREATED_AT < parsedatetime (extract (year from CAST (now () AS timestamp)) "yyyy"))]
                :group-by [source.PRODUCTS__via__PRODUCT_ID__CATEGORY
                           source.PEOPLE__via__USER_ID__SOURCE
-                          parsedatetime (year (source.CREATED_AT) "yyyy")
+                          parsedatetime (extract (year from CAST (source.CREATED_AT AS timestamp)) "yyyy")
                           source.pivot-grouping]
                :order-by [source.PRODUCTS__via__PRODUCT_ID__CATEGORY ASC
                           source.PEOPLE__via__USER_ID__SOURCE ASC
-                          parsedatetime (year (source.CREATED_AT) "yyyy") ASC
+                          parsedatetime (extract (year from CAST (source.CREATED_AT AS timestamp)) "yyyy") ASC
                           source.pivot-grouping ASC]}
              (-> (mt/mbql-query orders
                    {:aggregation [[:aggregation-options [:count] {:name "count"}]]
diff --git a/test/metabase/query_processor_test/date_time_zone_functions_test.clj b/test/metabase/query_processor_test/date_time_zone_functions_test.clj
index f57c070278e8178a70a0198b969d507c3b3de955..e0cbda44d9c63c2e2c152336d4bf080893053c60 100644
--- a/test/metabase/query_processor_test/date_time_zone_functions_test.clj
+++ b/test/metabase/query_processor_test/date_time_zone_functions_test.clj
@@ -112,7 +112,7 @@
           (testing (format "extract %s function works as expected on %s column for driver %s" op col-type driver/*driver*)
             (is (= (set (expected-fn op)) (set (test-temporal-extract (query-fn op field-id)))))))))
 
-      ;; mongo doesn't supports cast string to date
+     ;; mongo doesn't supports cast string to date
     (mt/test-drivers (disj (mt/normal-drivers-with-feature :temporal-extract) :mongo)
       (testing "with date columns"
         (doseq [[col-type field-id] [[:date (mt/id :times :d)] [:text-as-date (mt/id :times :as_d)]]
@@ -140,7 +140,54 @@
                                           :fields      (into [] (for [op ops] [:expression (name op)]))})
                       (mt/formatted-rows (repeat int))
                       first
-                      (zipmap ops)))))))))
+                      (zipmap ops)))))))
+
+    (testing "with timestamptz columns"
+      (mt/test-drivers (filter mt/supports-timestamptz-type? (mt/normal-drivers-with-feature :temporal-extract))
+        (mt/with-report-timezone-id "Asia/Kabul"
+          (is (= (if (or (= driver/*driver* :sqlserver)
+                         (driver/supports? driver/*driver* :set-timezone))
+                     {:get-year        2004,
+                      :get-quarter     1,
+                      :get-month       1,
+                      :get-day         1,
+                      :get-day-of-week 5,
+                      ;; TIMEZONE FIXME these drivers are returning the extracted hours in
+                      ;; the timezone that they were inserted in
+                      ;; maybe they need explicit convert-timezone to the report-tz before extraction?
+                      :get-hour        (case driver/*driver*
+                                         (:sqlserver :presto :presto-jdbc :snowflake :oracle) 5
+                                         2),
+                      :get-minute      (case driver/*driver*
+                                         (:sqlserver :presto :presto-jdbc :snowflake :oracle) 19
+                                         49),
+                      :get-second      9}
+                     {:get-year        2003,
+                      :get-quarter     4,
+                      :get-month       12,
+                      :get-day         31,
+                      :get-day-of-week 4,
+                      :get-hour        22,
+                      :get-minute      19,
+                      :get-second      9})
+                 (let [ops [:get-year :get-quarter :get-month :get-day
+                            :get-day-of-week :get-hour :get-minute :get-second]]
+                    (->> (mt/mbql-query times {:expressions (into {"shifted-day"  [:datetime-subtract $dt_tz 78 :day]
+                                                                   ;; the idea is to extract a column with value = 2004-01-01 02:49:09 +04:30
+                                                                   ;; this way the UTC value is 2003-12-31 22:19:09 +00:00 which will make sure
+                                                                   ;; the year, quarter, month, day, week is extracted correctly
+                                                                   ;; TODO: it's better to use a literal for this, but the function is not working properly
+                                                                   ;; with OffsetDatetime for all drivers, so we'll go wit this for now
+                                                                   "shifted-hour" [:datetime-subtract [:expression "shifted-day"] 4 :hour]}
+                                                                  (for [op ops]
+                                                                    [(name op) [op [:expression "shifted-hour"]]]))
+                                               :fields      (into [] (for [op ops] [:expression (name op)]))
+                                               :filter      [:= $index 1]
+                                               :limit       1})
+                        mt/process-query
+                        (mt/formatted-rows (repeat int))
+                        first
+                        (zipmap ops))))))))))
 
 (deftest temporal-extraction-with-filter-expresion-tests
   (mt/test-drivers (mt/normal-drivers-with-feature :temporal-extract)
diff --git a/test/metabase/test.clj b/test/metabase/test.clj
index 346fce7299b8a396eff591bbc50c1d9d7a0cd852..1da6de02175cc94ea34656f257fed5ff12a30acb 100644
--- a/test/metabase/test.clj
+++ b/test/metabase/test.clj
@@ -249,7 +249,8 @@
   has-test-extensions?
   metabase-instance
   sorts-nil-first?
-  supports-time-type?]
+  supports-time-type?
+  supports-timestamptz-type?]
 
  [tx.env
   set-test-drivers!
diff --git a/test/metabase/test/data/interface.clj b/test/metabase/test/data/interface.clj
index e9f42d40b18a5e279a63f22eb89de532d4426c1f..8499166b6760f663f49ce4d518ce109937240006 100644
--- a/test/metabase/test/data/interface.clj
+++ b/test/metabase/test/data/interface.clj
@@ -364,6 +364,14 @@
 
 (defmethod supports-time-type? ::test-extensions [_driver] true)
 
+(defmulti supports-timestamptz-type?
+  "Whether this database supports a `timestamp with time zone` data type or equivalent."
+  {:arglists '([driver])}
+  dispatch-on-driver-with-test-extensions
+  :hierarchy #'driver/hierarchy)
+
+(defmethod supports-timestamptz-type? ::test-extensions [_driver] true)
+
 (defmulti aggregate-column-info
   "Return the expected type information that should come back for QP results as part of `:cols` for an aggregation of a
   given type (and applied to a given Field, when applicable)."