Skip to content
Snippets Groups Projects
Commit a0122b0d authored by Ryan Senior's avatar Ryan Senior
Browse files

Use the date inlcuded in the query to calculate offset in MySQL [ci drivers]

Previously current date was used determine the offset for a given
timezone. This causes issues when the date being queried has a
different offset than today's date. Daylight savings time is an
example of that as depending on when the date is, the offset can be
different.

Fixes #7106
parent b58767c7
No related branches found
No related tags found
No related merge requests found
(ns metabase.driver.mysql
"MySQL driver. Builds off of the Generic SQL driver."
(:require [clj-time
[coerce :as tcoerce]
[core :as t]
[format :as time]]
[clojure
......@@ -108,24 +109,30 @@
(time/formatter "ZZ"))
(defn- timezone-id->offset-str
"Get an appropriate timezone offset string for a timezone with `timezone-id`. MySQL only accepts these offsets as
strings like `-8:00`.
"Get an appropriate timezone offset string for a timezone with `timezone-id` and `date-time`. MySQL only accepts
these offsets as strings like `-8:00`.
(timezone-id->offset-str \"US/Pacific\") ; -> \"-08:00\"
(timezone-id->offset-str \"US/Pacific\", date-time) ; -> \"-08:00\"
Returns `nil` if `timezone-id` is itself `nil`."
[^String timezone-id]
Returns `nil` if `timezone-id` is itself `nil`. The `date-time` must be included as some timezones vary their
offsets at different times of the year (i.e. daylight savings time)."
[^String timezone-id date-time]
(when timezone-id
(time/unparse (.withZone timezone-offset-formatter (t/time-zone-for-id timezone-id)) (t/now))))
(time/unparse (.withZone timezone-offset-formatter (t/time-zone-for-id timezone-id)) date-time)))
(def ^:private ^String system-timezone-offset-str
(timezone-id->offset-str (.getID (TimeZone/getDefault))))
(defn- ^String system-timezone->offset-str
"Get the system/JVM timezone offset specified at `date-time`. The time is needed here as offsets can change for a
given timezone based on the time of year (i.e. daylight savings time)."
[date-time]
(timezone-id->offset-str (.getID (TimeZone/getDefault)) date-time))
;; MySQL doesn't seem to correctly want to handle timestamps no matter how nicely we ask. SAD! Thus we will just
;; convert them to appropriate timestamp literals and include functions to convert timezones as needed
(defmethod sqlqp/->honeysql [MySQLDriver Date]
[_ date]
(let [report-timezone-offset-str (timezone-id->offset-str (driver/report-timezone))]
(let [date-as-dt (tcoerce/from-date date)
report-timezone-offset-str (timezone-id->offset-str (driver/report-timezone) date-as-dt)
system-timezone-offset-str (system-timezone->offset-str date-as-dt)]
(if (and report-timezone-offset-str
(not= report-timezone-offset-str system-timezone-offset-str))
;; if we have a report timezone we want to generate SQL like convert_tz('2004-01-01T12:00:00','-8:00','-2:00')
......
(ns metabase.driver.mysql-test
(:require [expectations :refer :all]
(:require [clj-time.core :as t]
[expectations :refer :all]
[metabase
[sync :as sync]
[util :as u]]
......@@ -97,9 +98,18 @@
(with-redefs [metabase.driver/execute-query (constantly {:rows [["2018-01-08 23:00:00.008 CET"]]})]
(tu/db-timezone-id)))
(expect (#'mysql/timezone-id->offset-str "US/Pacific") "-08:00")
(expect (#'mysql/timezone-id->offset-str "UTC") "+00:00")
(expect (#'mysql/timezone-id->offset-str "America/Los_Angeles") "-08:00")
(def before-daylight-savings (u/str->date-time "2018-03-10 10:00:00"))
(def after-daylight-savings (u/str->date-time "2018-03-12 10:00:00"))
(expect (#'mysql/timezone-id->offset-str "US/Pacific" before-daylight-savings) "-08:00")
(expect (#'mysql/timezone-id->offset-str "US/Pacific" after-daylight-savings) "-07:00")
(expect (#'mysql/timezone-id->offset-str "UTC" before-daylight-savings) "+00:00")
(expect (#'mysql/timezone-id->offset-str "UTC" after-daylight-savings) "+00:00")
(expect (#'mysql/timezone-id->offset-str "America/Los_Angeles" before-daylight-savings) "-08:00")
(expect (#'mysql/timezone-id->offset-str "America/Los_Angeles" after-daylight-savings) "-07:00")
;; make sure DateTime types generate appropriate SQL...
;; ...with no report-timezone set
......
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