Skip to content
Snippets Groups Projects
Unverified Commit d1019fc7 authored by Cal Herries's avatar Cal Herries Committed by GitHub
Browse files

Fix incorrect datetime-diff results for bigquery on leap years (#26815)

* Add failing tests

* Fix bigquery

* Formatting
parent f15a4d8f
No related branches found
No related tags found
No related merge requests found
......@@ -555,14 +555,16 @@
(defmethod sql.qp/->honeysql [:bigquery-cloud-sdk :datetime-diff]
[driver [_ x y unit]]
(let [x' (sql.qp/->honeysql driver x)
y' (sql.qp/->honeysql driver y)
(let [x (sql.qp/->honeysql driver x)
y (sql.qp/->honeysql driver y)
disallowed-types (keep
(fn [x]
(when-not (contains? #{:timestamp :date :datetime} (temporal-type x))
(or (some-> (temporal-type x) name)
(hx/type-info->db-type (hx/type-info x)))))
[x' y'])]
[x y])
x (hx/->timestamp x)
y (hx/->timestamp y)]
(when (seq disallowed-types)
(throw
(ex-info (tru "Only datetime, timestamp, or date types allowed. Found {0}"
......@@ -571,41 +573,47 @@
:found disallowed-types
:type qp.error-type/invalid-query})))
(case unit
(:year :month)
(let [; timestamp_diff doesn't support months or years, so convert to datetime to use datetime_diff
x' (hx/->datetime (trunc :day (hx/->timestamp x')))
y' (hx/->datetime (trunc :day (hx/->timestamp y')))
raw-unit (hsql/raw (name unit))
positive-diff (fn [a b] ; precondition: a <= b
:year
(let [positive-diff (fn [a b] ; precondition: a <= b
(hx/-
(hsql/call :datetime_diff b a raw-unit)
(hx/- (extract :year b) (extract :year a))
;; decrement if a is later than b in the year calendar
(hx/cast
:integer
(hsql/call
:>
(hsql/call :datetime_diff a (hsql/call :date_trunc a raw-unit) (hsql/raw "day"))
(hsql/call :datetime_diff b (hsql/call :date_trunc b raw-unit) (hsql/raw "day"))))))]
(hsql/call :case (hsql/call :<= x' y') (positive-diff x' y') :else (hx/* -1 (positive-diff y' x'))))
:or
(hsql/call :> (extract :month a) (extract :month b))
(hsql/call
:and
(hsql/call := (extract :month a) (extract :month b))
(hsql/call :> (extract :day a) (extract :day b)))))))]
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x))))
:month
(let [positive-diff (fn [a b] ; precondition: a <= b
(hx/-
;; timestamp_diff doesn't support months, so convert to datetime to use datetime_diff
(hsql/call :datetime_diff (hx/->datetime b) (hx/->datetime a) (hsql/raw (name unit)))
(hx/cast :integer (hsql/call :> (extract :day a) (extract :day b)))))]
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x))))
:week
(let [x' (trunc :day (hx/->timestamp x'))
y' (trunc :day (hx/->timestamp y'))
(let [x (trunc :day x)
y (trunc :day y)
positive-diff (fn [a b]
(hx/cast
:integer
(hx/floor
(hx// (hsql/call :timestamp_diff b a (hsql/raw "day")) 7))))]
(hsql/call :case (hsql/call :<= x' y') (positive-diff x' y') :else (hx/* -1 (positive-diff y' x'))))
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x))))
:day
(let [x' (trunc :day (hx/->timestamp x'))
y' (trunc :day (hx/->timestamp y'))]
(hsql/call :timestamp_diff y' x' (hsql/raw (name unit))))
(let [x (trunc :day x)
y (trunc :day y)]
(hsql/call :timestamp_diff y x (hsql/raw (name unit))))
(:hour :minute :second)
(let [x' (hx/->timestamp x')
y' (hx/->timestamp y')]
(hsql/call :timestamp_diff y' x' (hsql/raw (name unit)))))))
(hsql/call :timestamp_diff y x (hsql/raw (name unit))))))
(defmethod driver/escape-alias :bigquery-cloud-sdk
[driver s]
......
......@@ -665,6 +665,8 @@
first))]
(doseq [[unit cases] [[:year [["2021-10-03T09:18:09" "2022-10-02T09:18:09" 0 "day under a year"]
["2021-10-03T09:19:09" "2022-10-03T09:18:09" 1 "ignores time"]
["2016-02-03T09:19:09" "2017-02-02T09:19:09" 0 "starts in leap year before leap day"]
["2016-10-03T09:19:09" "2017-10-03T09:19:09" 1 "starts in leap year after leap day"]
["2017-06-10T08:30:00" "2019-07-10T08:30:00" 2 "multiple years"]]]
[:month [["2022-10-03T09:18:09" "2022-11-02T09:18:09" 0 "day under a month"]
["2022-10-02T09:19:09" "2022-11-02T09:18:09" 1 "minute under a month"]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment