diff --git a/src/metabase/driver/druid/query_processor.clj b/src/metabase/driver/druid/query_processor.clj
index c67db71992d269fcf91e4a2ff163c3c47ed981d0..26404b8fd325cd6e2cc95eb0923266fb2f491134 100644
--- a/src/metabase/driver/druid/query_processor.clj
+++ b/src/metabase/driver/druid/query_processor.clj
@@ -1,5 +1,7 @@
 (ns metabase.driver.druid.query-processor
   (:require [cheshire.core :as json]
+            [clj-time.core :as time]
+            [clj-time.format :as tformat]
             [clojure.core.match :refer [match]]
             [clojure.math.numeric-tower :as math]
             [clojure.string :as s]
@@ -9,7 +11,9 @@
              [annotate :as annotate]
              [interface :as i]]
             [metabase.util :as u])
-  (:import [metabase.query_processor.interface AgFieldRef DateTimeField DateTimeValue Expression Field RelativeDateTimeValue Value]))
+  (:import java.util.TimeZone
+           [metabase.query_processor.interface AgFieldRef DateTimeField DateTimeValue Expression Field RelativeDateTimeValue Value]
+           org.joda.time.DateTimeZone))
 
 (def ^:private ^:const topN-max-results
   "Maximum number of rows the topN query in Druid should return. Huge values cause significant issues with the engine.
@@ -724,24 +728,34 @@
   {:projections projections
    :results     results})
 
-(defmethod post-process ::select  [_ projections results]
-  (->> results
-       first
-       :result
-       :events
-       (map :event)
-       (post-process-map projections)))
-
-(defmethod post-process ::total   [_ projections results]
+(def ^:private druid-ts-format (tformat/formatters :date-time))
+
+(defn- reformat-timestamp [timestamp target-formatter]
+  (->> timestamp
+       (tformat/parse druid-ts-format)
+       (tformat/unparse target-formatter)))
+
+(defmethod post-process ::select  [_ projections timezone results]
+  (let [update-ts-fn (if-let [target-formater (and timezone (tformat/with-zone druid-ts-format timezone))]
+                       #(update % :timestamp reformat-timestamp target-formater)
+                       identity)]
+    (->> results
+         first
+         :result
+         :events
+         (map (comp update-ts-fn :event))
+         (post-process-map projections))))
+
+(defmethod post-process ::total   [_ projections timezone results]
   (post-process-map projections (map :result results)))
 
-(defmethod post-process ::topN    [_ projections results]
+(defmethod post-process ::topN    [_ projections timezone results]
   (post-process-map projections (-> results first :result)))
 
-(defmethod post-process ::groupBy [_ projections results]
+(defmethod post-process ::groupBy [_ projections timezone results]
   (post-process-map projections (map :event results)))
 
-(defmethod post-process ::timeseries [_ projections results]
+(defmethod post-process ::timeseries [_ projections timezone results]
   (post-process-map (conj projections :timestamp)
                     (for [event results]
                       (conj {:timestamp (:timestamp event)} (:result event)))))
@@ -791,9 +805,26 @@
                                     k)
             k))))
 
+(defn- utc?
+  "There are several timezone ids that mean UTC. This will create a
+  TimeZone object from `TIMEZONE` and check to see if it's a UTC
+  timezone"
+  [^DateTimeZone timezone]
+  (.hasSameRules (TimeZone/getTimeZone "UTC")
+                 (.toTimeZone timezone)))
+
+(defn- resolve-timezone
+  "Returns the timezone object (either report-timezone or JVM
+  timezone). Returns nil if the timezone is UTC as the timestamps from
+  Druid are already in UTC and don't need to be converted"
+  [{:keys [settings]}]
+  (let [tz (time/time-zone-for-id (:report-timezone settings (System/getProperty "user.timezone")))]
+    (when-not (utc? tz)
+      tz)))
+
 (defn execute-query
   "Execute a query for a Druid DB."
-  [do-query {database :database, {:keys [query query-type mbql? projections]} :native}]
+  [do-query {database :database, {:keys [query query-type mbql? projections]} :native :as query-ctx}]
   {:pre [database query]}
   (let [details       (:details database)
         query         (if (string? query)
@@ -802,7 +833,7 @@
         query-type    (or query-type (keyword "metabase.driver.druid.query-processor" (name (:queryType query))))
         post-proc-map (->> query
                            (do-query details)
-                           (post-process query-type projections))
+                           (post-process query-type projections (resolve-timezone query-ctx)))
         columns       (if mbql?
                         (->> post-proc-map
                              :projections
diff --git a/test/metabase/driver/druid_test.clj b/test/metabase/driver/druid_test.clj
index 8df4e288da7d0e9bb96c6a910d7e1b9766a8e326..d78643e4600d0031d831fdede04d875cec316526 100644
--- a/test/metabase/driver/druid_test.clj
+++ b/test/metabase/driver/druid_test.clj
@@ -1,5 +1,6 @@
 (ns metabase.driver.druid-test
   (:require [cheshire.core :as json]
+            [clj-time.core :as time]
             [expectations :refer [expect]]
             [medley.core :as m]
             [metabase
@@ -11,10 +12,12 @@
             metabase.driver.druid
             [metabase.models
              [field :refer [Field]]
-             [table :refer [Table]]
-             [metric :refer [Metric]]]
+             [metric :refer [Metric]]
+             [table :refer [Table]]]
             [metabase.query-processor.middleware.expand :as ql]
-            [metabase.test.data :as data]
+            [metabase.test
+             [data :as data]
+             [util :as tu]]
             [metabase.test.data.datasets :as datasets :refer [expect-with-engine]]
             [toucan.util.test :as tt])
   (:import metabase.driver.druid.DruidDriver))
@@ -28,11 +31,39 @@
    ["1000" "Tito's Tacos"                "2014-06-03T07:00:00.000Z"]
    ["101"  "Golden Road Brewing"         "2015-09-04T07:00:00.000Z"]]
   (->> (driver/table-rows-sample (Table (data/id :checkins))
-         [(Field (data/id :checkins :id))
-          (Field (data/id :checkins :venue_name))])
+                                 [(Field (data/id :checkins :id))
+                                  (Field (data/id :checkins :venue_name))])
        (sort-by first)
        (take 5)))
 
+(datasets/expect-with-engine :druid
+  ;; druid returns a timestamp along with the query, but that shouldn't really matter here :D
+  [["1"    "The Misfit Restaurant + Bar" "2014-04-07T00:00:00.000-07:00"]
+   ["10"   "Dal Rae Restaurant"          "2015-08-22T00:00:00.000-07:00"]
+   ["100"  "PizzaHacker"                 "2014-07-26T00:00:00.000-07:00"]
+   ["1000" "Tito's Tacos"                "2014-06-03T00:00:00.000-07:00"]
+   ["101"  "Golden Road Brewing"         "2015-09-04T00:00:00.000-07:00"]]
+  (tu/with-temporary-setting-values [report-timezone "America/Los_Angeles"]
+    (->> (driver/table-rows-sample (Table (data/id :checkins))
+                                   [(Field (data/id :checkins :id))
+                                    (Field (data/id :checkins :venue_name))])
+         (sort-by first)
+         (take 5))))
+
+(datasets/expect-with-engine :druid
+  ;; druid returns a timestamp along with the query, but that shouldn't really matter here :D
+  [["1"    "The Misfit Restaurant + Bar" "2014-04-07T02:00:00.000-05:00"]
+   ["10"   "Dal Rae Restaurant"          "2015-08-22T02:00:00.000-05:00"]
+   ["100"  "PizzaHacker"                 "2014-07-26T02:00:00.000-05:00"]
+   ["1000" "Tito's Tacos"                "2014-06-03T02:00:00.000-05:00"]
+   ["101"  "Golden Road Brewing"         "2015-09-04T02:00:00.000-05:00"]]
+  (tu/with-jvm-tz (time/time-zone-for-id "America/Chicago")
+    (->> (driver/table-rows-sample (Table (data/id :checkins))
+                                   [(Field (data/id :checkins :id))
+                                    (Field (data/id :checkins :venue_name))])
+         (sort-by first)
+         (take 5))))
+
 (def ^:const ^:private ^String native-query-1
   (json/generate-string
     {:intervals   ["1900-01-01/2100-01-01"]
diff --git a/test/metabase/query_processor_test/date_bucketing_test.clj b/test/metabase/query_processor_test/date_bucketing_test.clj
index 02b172aa2fd4f4f514eeb3622e4b812b7630eac1..5e5694809c3675bb731008a8736f7fccdc178e7a 100644
--- a/test/metabase/query_processor_test/date_bucketing_test.clj
+++ b/test/metabase/query_processor_test/date_bucketing_test.clj
@@ -58,35 +58,6 @@
   [engine]
   (contains? #{:oracle} engine))
 
-(defn- call-with-jvm-tz
-  "Invokes the thunk `F` with the JVM timezone set to `DTZ`, puts the
-  various timezone settings back the way it found it when it exits."
-  [^DateTimeZone dtz f]
-  (let [orig-tz (TimeZone/getDefault)
-        orig-dtz (time/default-time-zone)
-        orig-tz-prop (System/getProperty "user.timezone")]
-    (try
-      ;; It looks like some DB drivers cache the timezone information
-      ;; when instantiated, this clears those to force them to reread
-      ;; that timezone value
-      (reset! @#'metabase.driver.generic-sql/database-id->connection-pool {})
-      ;; Used by JDBC, and most JVM things
-      (TimeZone/setDefault (.toTimeZone dtz))
-      ;; Needed as Joda time has a different default TZ
-      (DateTimeZone/setDefault dtz)
-      ;; We read the system property directly when formatting results, so this needs to be changed
-      (System/setProperty "user.timezone" (.getID dtz))
-      (f)
-      (finally
-        ;; We need to ensure we always put the timezones back the way
-        ;; we found them as it will cause test failures
-        (TimeZone/setDefault orig-tz)
-        (DateTimeZone/setDefault orig-dtz)
-        (System/setProperty "user.timezone" orig-tz-prop)))))
-
-(defmacro ^:private with-jvm-tz [dtz & body]
-  `(call-with-jvm-tz ~dtz (fn [] ~@body)))
-
 (defn- sad-toucan-incidents-with-bucketing
   "Returns 10 sad toucan incidents grouped by `UNIT`"
   ([unit]
@@ -233,7 +204,7 @@
     :else
     (sad-toucan-result (source-date-formatter utc-tz) (result-date-formatter pacific-tz)))
 
-  (with-jvm-tz pacific-tz
+  (tu/with-jvm-tz pacific-tz
     (sad-toucan-incidents-with-bucketing :default eastern-tz)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -528,7 +499,7 @@
                     (result-date-formatter pacific-tz)
                     [6 10 4 9 9 8 8 9 7 9]))
 
-  (with-jvm-tz pacific-tz
+  (tu/with-jvm-tz pacific-tz
     (sad-toucan-incidents-with-bucketing :day pacific-tz)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -724,7 +695,7 @@
     (results-by-week date-formatter-without-time
                      (result-date-formatter pacific-tz)
                      [46 47 40 60 7]))
-  (with-jvm-tz pacific-tz
+  (tu/with-jvm-tz pacific-tz
     (sad-toucan-incidents-with-bucketing :week pacific-tz)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/test/metabase/test/util.clj b/test/metabase/test/util.clj
index d401b3e88f7e68d70bbb54ace94f9031e448f4d5..de256a6b769650eacc1bf2f4188159fc6c2ce853 100644
--- a/test/metabase/test/util.clj
+++ b/test/metabase/test/util.clj
@@ -1,6 +1,7 @@
 (ns metabase.test.util
   "Helper functions and macros for writing unit tests."
   (:require [cheshire.core :as json]
+            [clj-time.core :as time]
             [clojure.tools.logging :as log]
             [clojure.walk :as walk]
             [clojurewerkz.quartzite.scheduler :as qs]
@@ -28,7 +29,8 @@
             [metabase.test.data :as data]
             [metabase.test.data.datasets :refer [*driver*]]
             [toucan.util.test :as test])
-  (:import org.joda.time.DateTime
+  (:import java.util.TimeZone
+           [org.joda.time DateTime DateTimeZone]
            [org.quartz CronTrigger JobDetail JobKey Scheduler Trigger]))
 
 ;; ## match-$
@@ -418,3 +420,34 @@
         .getChronology
         .getZone
         .getID)))
+
+(defn call-with-jvm-tz
+  "Invokes the thunk `F` with the JVM timezone set to `DTZ`, puts the
+  various timezone settings back the way it found it when it exits."
+  [^DateTimeZone dtz f]
+  (let [orig-tz (TimeZone/getDefault)
+        orig-dtz (time/default-time-zone)
+        orig-tz-prop (System/getProperty "user.timezone")]
+    (try
+      ;; It looks like some DB drivers cache the timezone information
+      ;; when instantiated, this clears those to force them to reread
+      ;; that timezone value
+      (reset! @#'metabase.driver.generic-sql/database-id->connection-pool {})
+      ;; Used by JDBC, and most JVM things
+      (TimeZone/setDefault (.toTimeZone dtz))
+      ;; Needed as Joda time has a different default TZ
+      (DateTimeZone/setDefault dtz)
+      ;; We read the system property directly when formatting results, so this needs to be changed
+      (System/setProperty "user.timezone" (.getID dtz))
+      (f)
+      (finally
+        ;; We need to ensure we always put the timezones back the way
+        ;; we found them as it will cause test failures
+        (TimeZone/setDefault orig-tz)
+        (DateTimeZone/setDefault orig-dtz)
+        (System/setProperty "user.timezone" orig-tz-prop)))))
+
+(defmacro with-jvm-tz
+  "Invokes `BODY` with the JVM timezone set to `DTZ`"
+  [dtz & body]
+  `(call-with-jvm-tz ~dtz (fn [] ~@body)))