Shared core of time utils used by the internal CLJ and CLJS implementations. See [[metabase.util.time]] for the public interface.

(ns metabase.util.time.impl-common)
(defn- by-unit [_ {:keys [unit]}] (keyword unit))

Given a datetime and a unit (eg. "hour"), returns an inclusive datetime range as a pair of datetimes. For a unit of an hour, and a datetime for 13:49:28, that means [13:00:00 13:59:59.999], ie. 1 ms before the end.

(defmulti to-range
  by-unit)

Given a string representation of a datetime and the options map, parses the string as a representation of the :unit option (eg. "hour"). Returns a platform-specific datetime.

(defmulti string->timestamp
  by-unit)

Given a numeric representation of a datetime and the options map, interprets the number based on the :unit option (eg. "day-of-week").

Note that for two relative units - day-of-month and day-of-year - an arbitrary date is generated, not necessarily one in the current month or year. When grouping user data by day-of-month, it doesn't matter whether the current month has 31 days or not.

Returns a platform-specific datetime.

(defmulti number->timestamp
  by-unit)
(def ^:private year-part
  "\\d{4}")
(def ^:private month-part
  "\\d{2}")
(def ^:private day-part
  "\\d{2}")
(def ^:private date-part
  (str year-part \- month-part \- day-part))
(def ^:private hour-part
  "\\d{2}")
(def ^:private minutes-part
  "\\d{2}")
(defn- optional [& parts]
  (str "(?:" (apply str parts) ")?"))
(def ^:private seconds-milliseconds-part
  (str ":\\d{2}" (optional "\\.\\d{1,6}")))
(def ^:private time-part
  (str hour-part \: minutes-part (optional seconds-milliseconds-part)))
(def ^:private date-time-part
  (str date-part "[T ]" time-part))
(def ^:private offset-part
  (str "(?:Z|(?:[+-]" time-part "))"))

Regex for a zone-offset string.

(def zone-offset-part-regex
  (re-pattern offset-part))

Regex for a local-date string.

(def ^:const local-date-regex
  (re-pattern (str \^ date-part \$)))

Regex for a local-time string.

(def ^:const local-time-regex
  (re-pattern (str \^ time-part \$)))

Regex for an offset-time string.

(def ^:const offset-time-regex
  (re-pattern (str \^ time-part offset-part \$)))

Regex for a local-datetime string.

(def ^:const local-datetime-regex
  (re-pattern (str \^ date-time-part \$)))

Regex for an offset-datetime string.

(def ^:const offset-datetime-regex
  (re-pattern (str \^ date-time-part offset-part \$)))

Regex for a year-month literal string.

(def ^:const year-month-regex
  (re-pattern (str \^ year-part \- month-part \$)))

Regex for a year literal string.

(def ^:const year-regex
  (re-pattern (str \^ year-part \$)))

Matches a local time string.

(defn matches-time?
  [input]
  (boolean (re-matches local-time-regex input)))

Matches a local date string.

(defn matches-date?
  [input]
  (boolean (re-matches local-date-regex input)))

Matches a local AND offset date time string.

(defn matches-date-time?
  [input]
  (boolean (re-matches (re-pattern (str date-time-part (optional offset-part))) input)))

Strips off a trailing +0500, -0430, or Z from a time string.

(defn drop-trailing-time-zone
  [time-str]
  (or (second (re-matches (re-pattern (str "(.*?)" (optional offset-part) \$)) time-str))
      time-str))

Mapping of human-friendly keywords to literal month numbers.

1 = January.

(def ^:const month-keywords
  {:jan 1
   :feb 2
   :mar 3
   :apr 4
   :may 5
   :jun 6
   :jul 7
   :aug 8
   :sep 9
   :oct 10
   :nov 11
   :dec 12})