Functions for fetching the timezone for the current query. | (ns metabase.query-processor.timezone (:require [java-time.api :as t] [metabase.config :as config] [metabase.driver :as driver] [metabase.driver.util :as driver.u] [metabase.lib.metadata :as lib.metadata] [metabase.query-processor.store :as qp.store] [metabase.util.log :as log]) (:import (java.time ZonedDateTime))) |
(set! *warn-on-reflection* true) | |
(def ^:private ^:dynamic *report-timezone-id-override* nil) | |
(def ^:private ^:dynamic *database-timezone-id-override* nil) | |
(def ^:private ^:dynamic *results-timezone-id-override* nil) | |
TODO - consider making this | (defn- valid-timezone-id [timezone-id]
(when (and (string? timezone-id)
(seq timezone-id))
(try
(t/zone-id timezone-id)
timezone-id
(catch Throwable _
(log/warnf "Invalid timezone ID '%s'" timezone-id)
nil)))) |
(defn- report-timezone-id* []
(or *report-timezone-id-override*
(driver/report-timezone))) | |
+----------------------------------------------------------------------------------------------------------------+ | Public Interface | +----------------------------------------------------------------------------------------------------------------+ | |
Timezone ID for the report timezone, if the current driver and database supports it. (If the current driver supports it, this is
bound by the | (defn report-timezone-id-if-supported
(^String []
(report-timezone-id-if-supported driver/*driver* (lib.metadata/database (qp.store/metadata-provider))))
(^String [driver database]
(when (driver.u/supports? driver :set-timezone database)
(valid-timezone-id (report-timezone-id*))))) |
The timezone that the current database is in, as determined by the most recent sync. | (defn database-timezone-id
(^String []
(database-timezone-id ::db-from-store))
(^String [database]
(valid-timezone-id
(or *database-timezone-id-override*
(let [database (if (= database ::db-from-store)
(lib.metadata/database (qp.store/metadata-provider))
database)]
(:timezone database)))))) |
The system timezone of this Metabase instance. | (defn system-timezone-id ^String [] (.. (t/system-clock) getZone getId)) |
The timezone that we would like to run a query in, regardless of whether we are actually able to do so. This is
always equal to the value of the | (defn requested-timezone-id ^String [] (valid-timezone-id (report-timezone-id*))) |
The timezone that a query is actually ran in  report timezone, if set and supported by the current driver;
otherwise the timezone of the database (if known), otherwise the system timezone. Guaranteed to always return a
timezone ID Â never returns | (defn results-timezone-id
(^String []
(results-timezone-id driver/*driver* ::db-from-store))
(^String [database]
(results-timezone-id (:engine database) database))
(^String [driver database & {:keys [use-report-timezone-id-if-unsupported?]
:or {use-report-timezone-id-if-unsupported? false}}]
(valid-timezone-id
(or *results-timezone-id-override*
(if use-report-timezone-id-if-unsupported?
(valid-timezone-id (report-timezone-id*))
(report-timezone-id-if-supported driver database))
;; don't actually fetch DB from store unless needed — that way if `*results-timezone-id-override*` is set we
;; don't need to init a store during tests
(database-timezone-id database)
;; NOTE: if we don't have an explicit report-timezone then use the JVM timezone
;; this ensures alignment between the way dates are processed by JDBC and our returned data
;; GH issues: #2282, #2035
(system-timezone-id))))) |
Get the current moment in time adjusted to the results timezone ID, e.g. for relative datetime calculations. | (def ^ZonedDateTime now
(comp (fn [timezone-id]
(t/with-zone-same-instant (t/zoned-date-time) (t/zone-id timezone-id)))
results-timezone-id)) |
normally I'd do this inline with the | (when config/is-dev? (alter-meta! #'now assoc :arglists (:arglists (meta #'results-timezone-id)))) |