     ["=" field param-value]
     ;; otherwise we need to handle date filtering
     (if-not (contains? relative-dates param-value)
-      ;; absolute date range such as: "2014-05-10,2014-05-16"
-      (let [[start end] (s/split param-value #"~" 2)]
+      ;; absolute date range
+      (let [{:keys [start end]} (absolute-date->range param-value)]
         ["BETWEEN" field start end])
-      ;; relative date range, so build appropriate MBQL clause
+      ;; relative date range
       (condp = param-value
         "past7days"  ["TIME_INTERVAL" field -7 "day"]
         "past30days" ["TIME_INTERVAL" field -30 "day"]
+        "thisweek"   ["TIME_INTERVAL" field "current" "week"]
+        "thismonth"  ["TIME_INTERVAL" field "current" "month"]
+        "thisyear"   ["TIME_INTERVAL" field "current" "year"]
+        "lastweek"   ["TIME_INTERVAL" field "last" "week"]
+        "lastmonth"  ["TIME_INTERVAL" field "last" "month"]
+        "lastyear"   ["TIME_INTERVAL" field "last" "year"]
         "yesterday"  ["=" field ["relative_datetime" -1 "day"]]
         "today"      ["=" field ["relative_datetime" "current"]]))))
 

;;; +-------------------------------------------------------------------------------------------------------+


(defn- extract-dates [value report-timezone]
  (if-not (contains? relative-dates value)
    ;; absolute date range such as: "2014-05-10,2014-05-16"
    ;; TODO: other absolute options?  year-quarter?  year-month?
    (zipmap [:start :end] (s/split value #"~" 2))
    ;; relative date range
    (let [tz        (t/time-zone-for-id report-timezone)
          formatter (tf/formatter "YYYY-MM-dd" tz)
          today     (t/today-at-midnight tz)]
      (->> (condp = value
             "past7days"  {:end   (t/minus today (t/days 1))
                           :start (t/minus today (t/days 7))}
             "past30days" {:end   (t/minus today (t/days 1))
                           :start (t/minus today (t/days 30))}
             "yesterday"  {:end   (t/minus today (t/days 1))
                           :start (t/minus today (t/days 1))}
             "today"      {:end   today
                           :start today})
           ;; the above values are JodaTime objects, so unparse them to iso8601 strings
           (m/map-vals (partial tf/unparse formatter))))))

(defn- expand-date-range-param [report-timezone {[target param-name] :target, param-type :type, param-value :value, :as param}]
  (if-not (= param-type "date")

            [clj-time.core :as t]
            [clj-time.format :as tf]
            [medley.core :as m]
            [metabase.driver :as driver])
  (:import (org.joda.time DateTimeConstants)))


(def ^:private ^:const relative-dates
  #{"today"
    "yesterday"
    "past7days"
    "past30days"
    "thisweek"
    "thismonth"
    "thisyear"
    "lastweek"
    "lastmonth"
    "lastyear"})

(defn- start-of-quarter [quarter year]
  (condp = quarter
    "Q1" (t/first-day-of-the-month (.withMonthOfYear (t/date-time year) DateTimeConstants/JANUARY))
    "Q2" (t/first-day-of-the-month (.withMonthOfYear (t/date-time year) DateTimeConstants/APRIL))
    "Q3" (t/first-day-of-the-month (.withMonthOfYear (t/date-time year) DateTimeConstants/JULY))
    "Q4" (t/first-day-of-the-month (.withMonthOfYear (t/date-time year) DateTimeConstants/OCTOBER))))

(defn- week-range [dt]
  ;; weeks always start on SUNDAY and end on SATURDAY
  {:end   (.withDayOfWeek dt DateTimeConstants/SATURDAY)
   :start (.withDayOfWeek dt DateTimeConstants/SUNDAY)})

(defn- month-range [dt]
  {:end   (t/last-day-of-the-month dt)
   :start (t/first-day-of-the-month dt)})

;; NOTE: this is perhaps a little hacky, but we are assuming that `dt` will be in the first month of the quarter
(defn- quarter-range [dt]
  {:end   (t/last-day-of-the-month (t/plus dt (t/months 2)))
   :start (t/first-day-of-the-month dt)})

(defn- year-range [dt]
  {:end   (t/last-day-of-the-month (.withMonthOfYear dt DateTimeConstants/DECEMBER))
   :start (t/first-day-of-the-month (.withMonthOfYear dt DateTimeConstants/JANUARY))})

(defn- absolute-date->range
  "Take a given string description of an absolute date range and return a MAP with a given `:start` and `:end`.
   Supported formats:
      \"2014-05-10~2014-05-16\"
      \"Q1-2016\"
      \"2016-04\""
  [value]
  (if (s/includes? value "~")
    ;; these values are already expected to be iso8601 strings, so we are done
    (zipmap [:start :end] (s/split value #"~" 2))
    ;; these cases represent fixed date ranges, but we need to calculate start/end still
    (->> (if (s/starts-with? value "Q")
           (let [[quarter year] (s/split value #"-" 2)]
             (quarter-range (start-of-quarter quarter (Integer/parseInt year))))
           (month-range (tf/parse (tf/formatters :year-month) value)))
         (m/map-vals (partial tf/unparse (tf/formatters :year-month-day))))))


;;; +-------------------------------------------------------------------------------------------------------+
