std.time
time coercion, durations, zones, formats, and representations
std.time is a higher-level standard library family in foundation-base. This page explains when to use it, how it fits internally, and where to find the API surface.
1 Motivation
Use this layer when application or tooling code needs the behavior described by the page title without reaching directly into implementation namespaces. The top-level namespace is the starting point; subnamespaces expose more focused building blocks.
2 How to use it
Require the top-level namespace for common workflows, then move to subnamespaces when you need a lower-level primitive. Existing tests under test/std/time and test/std/time_test.clj are the best executable examples for edge cases.
coerce instants between representations
(time/coerce-instant 0 {:type java.util.Date})
=> #inst "1970-01-01T00:00:00.000-00:00"
format and parse timestamps
(time/format (java.util.Date. 0) "yyyy-MM-dd")
=> "1970-01-01"
(time/parse "1970-01-01" "yyyy-MM-dd")
=> #(instance? java.util.Date %)
duration calculations
(time/to-ms 1 :s)
=> 1000
3 Internal usage
This library family is used across source, tests, generated examples, and docs tooling. During detailed documentation passes, collect concrete usage with code.manage/find-usages and code.manage/locate-code, then keep only high-signal examples in the page narrative.
4 API
- adjust
- after
- before
- calendar
- coerce
- day
- day-of-week
- default-timezone
- default-type
- duration?
- earliest
- epoch
- equal
- format
- from-long
- from-map
- get-timezone
- has-timezone?
- hour
- instant?
- latest
- local-timezone
- millisecond
- minus
- minute
- month
- now
- parse
- plus
- representation?
- second
- time-meta
- to-length
- to-long
- to-map
- to-vector
- truncate
- with-timezone
- wrap-proxy
- year
v 3.0
(defn adjust
([t rep]
(adjust t rep {}))
([t rep opts]
(let [m (-> (to-map t opts)
(merge rep)
(dissoc :long))]
(from-long (from-map m {:type Long})
(assoc m :type (class t))))))
link
(t/adjust (Date. 0) {:year 2000 :second 10} {:timezone "GMT"}) => #inst "2000-01-01T00:00:10.000-00:00" (t/adjust {:year 1970, :month 1 :day 1, :day-of-week 4, :hour 0 :minute 0 :second 0 :millisecond 0, :timezone "GMT"} {:year 1999}) => {:type clojure.lang.PersistentHashMap, :timezone "GMT", :long 915148800000, :year 1999, :month 1, :day 1, :hour 0, :minute 0 :second 0, :millisecond 0}
v 3.0
(defn after
([t1 t2]
(> (to-long t1) (to-long t2)))
([t1 t2 & more]
(apply > (map to-long (cons t1 (cons t2 more))))))
link
(t/after 2 (Date. 1) (common/calendar (Date. 0) (TimeZone/getTimeZone "GMT"))) => true
v 3.0
(defn before
([t1 t2]
(< (to-long t1) (to-long t2)))
([t1 t2 & more]
(apply < (map to-long (cons t1 (cons t2 more))))))
link
(t/before 0 (Date. 1) (common/calendar (Date. 2) (TimeZone/getTimeZone "GMT"))) => true
v 3.0
(defn calendar
([^Date date ^TimeZone timezone]
(doto (Calendar/getInstance timezone)
(.setTime date))))
link
(-> ^Calendar (calendar (Date. 0) (TimeZone/getTimeZone "GMT")) (.getTime)) => #inst "1970-01-01T00:00:00.000-00:00"
v 3.0
(defn coerce
([t {:keys [type timezone] :as opts}]
(let [type (or type common/*default-type*)
timezone (or timezone
(get-timezone t))]
(-> (to-long t)
(from-long {:type type :timezone timezone})))))
link
(t/coerce 0 {:type Date}) => #inst "1970-01-01T00:00:00.000-00:00" (t/coerce {:type clojure.lang.PersistentHashMap, :timezone "PST", :long 915148800000, :year 1999, :month 1, :day 1, :hour 0, :minute 0 :second 0, :millisecond 0} {:type Date}) => #inst "1999-01-01T08:00:00.000-00:00"
v 3.0
(defn day
([t] (day t {}))
([t opts]
((wrap-proxy protocol.time/-day) t opts)))
link
(t/day 0 {:timezone "GMT"}) => 1 (t/day (Date. 0) {:timezone "EST"}) => 31
v 3.0
(defn day-of-week
([t] (day-of-week t {}))
([t opts]
((wrap-proxy protocol.time/-day-of-week) t opts)))
link
(t/day-of-week 0 {:timezone "GMT"}) => 4 (t/day-of-week (Date. 0) {:timezone "EST"}) => 3
v 3.0
(defn default-timezone
([]
(or *default-timezone*
(local-timezone)))
([tz]
(alter-var-root #'*default-timezone*
(constantly (protocol.string/-to-string tz)))))
link
(default-timezone) ;; getter => "Asia/Ho_Chi_Minh" (default-timezone "GMT") ;; setter => "GMT"
v 3.0
(defn default-type
([] *default-type*)
([cls]
(alter-var-root #'*default-type*
(constantly cls))))
link
(default-type) ;; getter => clojure.lang.PersistentArrayMap (default-type Long) ;; setter => java.lang.Long
v 3.0
(defn duration?
([obj]
(satisfies? protocol.time/IDuration obj)))
link
(t/duration? 0) => true (t/duration? {:weeks 1}) => true
v 3.0
(defn earliest
([t1 t2 & more]
(first (sort-by protocol.time/-to-long (apply vector t1 t2 more)))))
link
(t/earliest (Date. 0) (Date. 1000) (Date. 20000)) => #inst "1970-01-01T00:00:00.000-00:00"
v 3.0
(defn epoch
([] (epoch {}))
([opts] (from-long 0 (merge {:type common/*default-type*}
opts))))
link
(t/epoch {:type Date}) => #inst "1970-01-01T00:00:00.000-00:00" (t/epoch {:type clojure.lang.PersistentArrayMap :timezone "GMT"}) {:type clojure.lang.PersistentArrayMap, :timezone "GMT", :long 0, :year 1970, :month 1, :day 1, :hour 0, :minute 0, :second 0, :millisecond 0}
equal ^
[t1 t2] [t1 t2 & more]
compares dates, retruns true if all inputs are the same
v 3.0
(defn equal
([t1 t2]
(= (to-long t2) (to-long t1)))
([t1 t2 & more]
(apply = (map to-long (cons t1 (cons t2 more))))))
link
(t/equal 1 (Date. 1) (common/calendar (Date. 1) (TimeZone/getTimeZone "GMT"))) => true
format ^
[t pattern] [t pattern {:keys [cached], :as opts}]
converts a date into a string
v 3.0
(defn format
([t pattern] (format t pattern {}))
([t pattern {:keys [cached] :as opts}]
(let [tmeta (protocol.time/-time-meta (class t))
ftype (-> tmeta :formatter :type)
fmt (cache +format-cache+
(fn [] (protocol.time/-formatter pattern (assoc opts :type ftype)))
[ftype pattern]
cached)]
(protocol.time/-format fmt t opts))))
link
(f/format (Date. 0) "HH MM dd Z" {:timezone "GMT" :cached true}) => "00 01 01 +0000" (f/format (common/calendar (Date. 0) (TimeZone/getTimeZone "GMT")) "HH MM dd Z" {}) => "00 01 01 +0000" (f/format (Timestamp. 0) "HH MM dd Z" {:timezone "PST"}) => "16 12 31 -0800" (f/format (Date. 0) "HH MM dd Z") => string?
v 3.0
(defn from-long
([t]
(from-long t nil))
([t opts]
(protocol.time/-from-long t (merge {:type common/*default-type*} opts))))
link
(-> (t/from-long 0 {:timezone "Asia/Kolkata" :type Calendar}) (t/to-map)) => {:type java.util.GregorianCalendar, :timezone "Asia/Kolkata", :long 0 :year 1970, :month 1, :day 1, :hour 5, :minute 30 :second 0, :millisecond 0}
from-map ^
[t] [t opts] [t {:keys [type timezone], :as opts} fill]
creates an map from an instant
v 3.0
(defn from-map
([t] (from-map t {}))
([t opts] (from-map t opts common/+zero-values+))
([t {:keys [type timezone] :as opts} fill]
(cond (#{PersistentArrayMap PersistentHashMap} type)
(protocol.time/-with-timezone t timezone)
:else
(map/from-map t opts fill))))
link
(t/from-map {:type java.util.GregorianCalendar, :timezone "Asia/Kolkata", :long 0 :year 1970, :month 1, :day 1, :hour 5, :minute 30 :second 0, :millisecond 0} {:timezone "Asia/Kolkata" :type Date}) => #inst "1970-01-01T00:00:00.000-00:00" (t/from-map {:type Long, :timezone "Asia/Kolkata", :long 0 :year 1970, :month 1, :day 1, :hour 5, :minute 30 :second 0, :millisecond 0}) => 0 (t/from-map {:type Long :timezone "Asia/Kolkata", :year 1970, :month 1, :day 1, :hour 5, :minute 30 :second 0, :millisecond 0} {:type clojure.lang.PersistentHashMap :timezone "GMT"}) => {:type clojure.lang.PersistentHashMap :timezone "GMT", :long 0 :year 1970, :month 1, :day 1, :hour 0, :minute 0 :second 0, :millisecond 0}
v 3.0
(defn get-timezone
([t]
(protocol.time/-get-timezone t)))
link
(t/get-timezone 0) => nil (t/get-timezone (common/calendar (Date. 0) (TimeZone/getTimeZone "EST"))) => "EST"
v 3.0
(defn has-timezone?
([t]
(protocol.time/-has-timezone? t)))
link
(t/has-timezone? 0) => false (t/has-timezone? (common/calendar (Date. 0) (TimeZone/getDefault))) => true
v 3.0
(defn hour
([t] (hour t {}))
([t opts]
((wrap-proxy protocol.time/-hour) t opts)))
link
(t/hour 0 {:timezone "GMT"}) => 0 (t/hour (Date. 0) {:timezone "Asia/Kolkata"}) => 5
v 3.0
(defn instant?
([obj]
(satisfies? protocol.time/IInstant obj)))
link
(t/instant? 0) => true (t/instant? (Date.)) => true
v 3.0
(defn latest
([t1 t2 & more]
(last (sort-by protocol.time/-to-long (apply vector t1 t2 more)))))
link
(t/latest (Date. 0) (Date. 1000) (Date. 20000)) => #inst "1970-01-01T00:00:20.000-00:00"
v 3.0
(defn local-timezone
([]
(.getID (TimeZone/getDefault))))
link
(local-timezone) => "Asia/Ho_Chi_Minh"
v 3.0
(defn millisecond
([t] (millisecond t {}))
([t opts]
((wrap-proxy protocol.time/-millisecond) t opts)))
link
(t/millisecond 1010 {:timezone "GMT"}) => 10
v 3.0
(defn minus
([t duration]
(minus t duration {}))
([t duration opts]
(from-long (- (to-long t)
(to-length duration
(-> (to-map t opts [:day :month :year])
(assoc :backward true))))
(assoc opts :type (class t)))))
link
(t/minus (Date. 0) {:years 1}) => #inst "1969-01-01T00:00:00.000-00:00" (-> (t/from-map {:type java.time.ZonedDateTime :timezone "GMT", :year 1970, :month 1, :day 1, :hour 0, :minute 0, :second 0, :millisecond 0}) (t/minus {:years 10 :months 1 :weeks 4 :days 2}) (t/to-map {:timezone "GMT"})) => {:type java.time.ZonedDateTime, :timezone "GMT", :long -320803200000 :year 1959, :month 11, :day 2, :hour 0, :minute 0, :second 0, :millisecond 0}
v 3.0
(defn minute
([t] (minute t {}))
([t opts]
((wrap-proxy protocol.time/-minute) t opts)))
link
(t/minute 0 {:timezone "GMT"}) => 0 (t/minute (Date. 0) {:timezone "Asia/Kolkata"}) => 30
v 3.0
(defn month
([t] (month t {}))
([t opts]
((wrap-proxy protocol.time/-month) t opts)))
link
(t/month 0 {:timezone "GMT"}) => 1 (t/month (Date. 0) {:timezone "EST"}) => 12
v 3.0
(defn now
([] (now {}))
([opts] (protocol.time/-now (merge {:type common/*default-type*}
opts))))
link
(t/now) ;; => #(instance? (t/default-type) %) (t/now {:type Date}) => #(instance? Date %) (t/now {:type Calendar}) => #(instance? Calendar %)
parse ^
[s pattern] [s pattern {:keys [cached], :as opts}]
testing format for java.time datastructures
NONE
(defn parse
([s pattern] (parse s pattern {}))
([s pattern {:keys [cached] :as opts}]
(let [opts (merge {:type common/*default-type*} opts)
type (:type opts)
tmeta (protocol.time/-time-meta type)
ptype (-> tmeta :parser :type)
parser (cache +parse-cache+
(fn [] (protocol.time/-parser pattern (assoc opts :type ptype)))
[ptype pattern]
cached)]
(protocol.time/-parse parser s opts))))
link
(-> (f/parse "00 00 01 01 01 1989 +0000" "ss mm HH dd MM yyyy Z" {:type Clock}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Etc/GMT" :long 599619600000, :year 1989, :month 1, :day 1, :hour 1, :minute 0, :second 0, :millisecond 0} (-> (f/parse "00 00 01 01 01 1989 -1000" "ss mm HH dd MM yyyy Z" {:type Clock}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Etc/GMT-10", :long 599583600000, :year 1989, :month 1, :day 1, :hour 1, :minute 0, :second 0, :millisecond 0} (-> (f/parse "00 00 01 01 01 1989 +1000" "ss mm HH dd MM yyyy Z" {:type Clock}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Etc/GMT+10", :long 599655600000, :year 1989, :month 1, :day 1, :hour 1, :minute 0, :second 0, :millisecond 0}
v 3.0
(defn plus
([t duration]
(plus t duration {}))
([t duration opts]
(from-long (+ (to-long t)
(to-length duration
(to-map t opts [:day :month :year])))
(assoc opts :type (class t)))))
link
(t/plus (Date. 0) {:weeks 2}) => #inst "1970-01-15T00:00:00.000-00:00" (t/plus (Date. 0) 1000) => #inst "1970-01-01T00:00:01.000-00:00" (t/plus (java.util.Date. 0) {:years 10 :months 1 :weeks 4 :days 2}) => #inst "1980-03-02T00:00:00.000-00:00"
v 3.0
(defn representation?
([obj]
(satisfies? protocol.time/IRepresentation obj)))
link
(t/representation? 0) => false (t/representation? (common/calendar (Date. 0) (TimeZone/getTimeZone "GMT"))) => true
v 3.0
(defn second
([t] (second t {}))
([t opts]
((wrap-proxy protocol.time/-second) t opts)))
link
(t/second 1000 {:timezone "GMT"}) => 1
v 3.0
(defn time-meta
([cls]
(protocol.time/-time-meta cls)))
link
(t/time-meta TimeZone) => {:base :zone} (t/time-meta Date) => (contains {:base :instant, :map (contains {:from (contains {:proxy java.util.Calendar, :via fn?}), :to (contains {:proxy java.util.Calendar, :via fn?})})})
v 3.0
(defn to-length
([t]
(to-length t {:year 0 :month 1 :day 1}))
([t rep]
(protocol.time/-to-length t rep)))
link
(t/to-length {:days 1}) => 86400000
v 3.0
(defn to-long
([t]
(protocol.time/-to-long t)))
link
(t/to-long #inst "1970-01-01T00:00:10.000-00:00") => 10000
to-map ^
[t] [t opts] [t {:keys [timezone], :as opts} ks]
creates an map from an instant
v 3.0
(defn to-map
([t] (to-map t {}))
([t opts] (to-map t opts common/+default-keys+))
([t {:keys [timezone] :as opts} ks]
(cond (map? t)
(if timezone
(protocol.time/-with-timezone t timezone)
t)
:else
(map/to-map t opts ks))))
link
(-> (t/from-long 0 {:timezone "Asia/Kolkata" :type Date}) (t/to-map {:timezone "GMT"} [:year :month :day])) => {:type java.util.Date, :timezone "GMT", :long 0, :year 1970, :month 1, :day 1}
to-vector ^
[t {:keys [timezone], :as opts} ks]
converts an instant to an array representation
v 3.0
(defn to-vector
([t {:keys [timezone] :as opts} ks]
(cond (map? t)
(if (or (nil? timezone)
(= timezone (:timezone opts)))
(mapv t ks)
(-> (map/from-map t (assoc opts :type java.util.Calendar))
(to-vector opts ks)))
:else
(let [tmeta (protocol.time/-time-meta (class t))
[p pmeta] (if-let [{:keys [proxy via]} (-> tmeta :map :to)]
[(via t opts) (protocol.time/-time-meta proxy)]
[t tmeta])
p (if timezone
(protocol.time/-with-timezone p timezone)
p)
ks (cond (vector? ks) ks
(= :all ks)
(reverse common/+default-keys+)
(keyword? ks)
(->> common/+default-keys+
(drop-while #(not= % ks))
(reverse)))
rep (reduce (fn [out k]
(let [t-fn (get common/+default-fns+ k)]
(conj out (t-fn p opts))))
[]
ks)]
rep))))
link
(to-vector 0 {:timezone "GMT"} :all) => [1970 1 1 0 0 0 0] (to-vector (Date. 0) {:timezone "GMT"} :day) => [1970 1 1] (to-vector (common/calendar (Date. 0) (TimeZone/getTimeZone "EST")) {} [:month :day :year]) => [12 31 1969] (to-vector (common/calendar (Date. 0) (TimeZone/getTimeZone "EST")) {:timezone "GMT"} [:month :day :year]) => [1 1 1970]
v 3.0
(defn truncate
([t col]
(truncate t col {}))
([t col opts]
(let [rep (to-map t opts)
trep (->> common/+default-keys+
(drop-while #(not= col %))
(concat [:type :timezone])
(select-keys rep))]
(from-map (merge common/+zero-values+ (dissoc trep :long))
(assoc opts :type (class t))))))
link
(t/truncate #inst "1989-12-28T12:34:00.000-00:00" :hour {:timezone "GMT"}) => #inst "1989-12-28T12:00:00.000-00:00" (t/truncate #inst "1989-12-28T12:34:00.000-00:00" :year {:timezone "GMT"}) => #inst "1989-01-01T00:00:00.000-00:00" (t/truncate (t/to-map #inst "1989-12-28T12:34:00.000-00:00" {:timezone "GMT"}) :hour) => {:type clojure.lang.PersistentHashMap, :timezone "GMT", :long 630849600000, :year 1989, :month 12, :day 28, :hour 12, :minute 0, :second 0, :millisecond 0}
v 3.0
(defn with-timezone
([t tz]
(protocol.time/-with-timezone t tz)))
link
(t/with-timezone 0 "EST") => 0 (t/to-map (t/with-timezone (common/calendar (Date. 0) (TimeZone/getTimeZone "GMT")) "EST")) => {:type java.util.GregorianCalendar, :timezone "EST", :long 0, :year 1969, :month 12, :day 31, :hour 19, :minute 0, :second 0, :millisecond 0}
wrap-proxy ^
[f]
converts one representation to another via a third object, used by java.sql.Timestamp and others
v 3.0
(defn wrap-proxy
([f]
(fn [t opts]
(cond (representation? t)
(f t opts)
:else
(let [tmeta (protocol.time/-time-meta (class t))]
(if-let [p-fn (-> tmeta :map :to :via)]
(f (p-fn t opts) opts)
(throw (Exception. (str "No proxy method for type " (class t))))))))))
link
NONE
(defn coerce-instant
([value {:keys [type] :as opts}]
(cond (instance? type value)
value
(or (= type Long) (nil? type))
(protocol.time/-to-long value)
(instance? Long value)
(protocol.time/-from-long value opts)
(instance? clojure.lang.APersistentMap value)
(map/from-map value opts)
:else
(-> value
(protocol.time/-to-long)
(protocol.time/-from-long opts)))))
link
(coerce-instant 0 {:type Long :timezone "GMT"}) => 0 (-> (coerce-instant 0 {:type ZonedDateTime :timezone "GMT"}) (map/to-map {} common/+default-keys+)) => {:type ZonedDateTime :timezone "GMT", :long 0, :year 1970, :month 1, :day 1, :hour 0, :minute 0, :second 0 :millisecond 0} (-> (time/-from-long 0 {:type ZonedDateTime :timezone "GMT"}) (coerce-instant {:type Clock :timezone "Asia/Kolkata"}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Asia/Kolkata", :long 0, :year 1970, :month 1, :day 1, :hour 5, :minute 30, :second 0 :millisecond 0} (-> (time/-from-long 0 {:type Clock :timezone "GMT"}) (coerce-instant {:type Calendar :timezone "Asia/Kolkata"}) (map/to-map {} common/+default-keys+)) => {:type java.util.GregorianCalendar :timezone "Asia/Kolkata", :long 0, :year 1970, :month 1, :day 1, :hour 5, :minute 30, :second 0 :millisecond 0}
v 3.0
(defn coerce-zone
([value {:keys [type] :as opts}]
(cond (nil? value)
(protocol.string/-from-string (common/default-timezone)
type
opts)
(instance? type value)
value
(string? value)
(protocol.string/-from-string value type opts)
:else
(-> value
(protocol.string/-to-string)
(protocol.string/-from-string type opts)))))
link
(-> (coerce-zone "Asia/Kolkata" {:type TimeZone}) (string/-to-string)) => "Asia/Kolkata" (-> (coerce-zone nil {:type TimeZone}) (string/-to-string)) => (-> (TimeZone/getDefault) (string/-to-string))
- adjust-days
- adjust-month-days
- adjust-options
- adjust-year-days
- backward-month-array
- backward-year-array
- forward-month-array
- forward-year-array
- map-to-length
- ms-length
- to-fixed-length
- variable-keys
adjust-days ^
[{:keys [years months], :or {years 0, months 0}} {:keys [year month day], :as rep}]
calculates the number of days to be forwarded based on year and month
v 3.0
(defn adjust-days
([{:keys [years months]
:or {years 0 months 0}}
{:keys [year month day] :as rep}]
(if-not (and year month day)
(throw (Exception. (format "Year (%s), Month (%s) and Day (%s) are required:" year month day)))) (let [year-days (adjust-year-days years rep)
month-days (adjust-month-days years months rep)]
(+ year-days month-days))))
link
(adjust-days {:years 0 :months 2} {:year 2012 :month 1 :day 31}) => 60 (adjust-days {:years 1 :months 2} {:year 2012 :month 1 :day 31}) => 425 (adjust-days {:months 2} {:year 2012 :month 1 :day 31 :backward true}) => 62 (adjust-days {:years 1} {:year 2013 :month 1 :day 31 :backward true}) => 366
adjust-month-days ^
[years months {:keys [year month day backward]}]
calculates the number of days to be adjusted based on month
v 3.0
(defn adjust-month-days
([years months {:keys [year month day backward]}]
(let [midx (rem (-> (+ year years)
(* 12)
(+ (dec month)))
48)
month-opts (-> adjust-options
((if backward :backward :forward))
:month)]
(->> (cycle (:array month-opts))
(drop midx)
(take months)
(apply +)))))
link
(adjust-month-days 0 2 {:year 2012 :month 3 :day 1}) => 61 (adjust-month-days 0 2 {:year 2012 :month 3 :day 1 :backward true}) => 60 (adjust-month-days 1 2 {:year 2011 :month 1 :day 3}) => 60 (adjust-month-days 1 2 {:year 2012 :month 1 :day 3}) => 59
adjust-year-days ^
[years {:keys [year month day backward]}]
calculates the number of days to be adjusted based on year
v 3.0
(defn adjust-year-days
([years {:keys [year month day backward]}]
(let [yidx (rem year 4)
year-opts (-> adjust-options
((if backward :backward :forward))
:year)
year-days (->> (cycle (:array year-opts))
(drop yidx)
(take years)
(apply +))]
(or (if (zero? year-days) year-days)
(if-let [[_ f]
(first (filter (fn [[pred _]]
(pred yidx month day))
(:pred year-opts)))]
(f year-days)
year-days)))))
link
(adjust-year-days 1 {:year 2012 :month 2 :day 28}) => 366 (adjust-year-days 1 {:year 2012 :month 2 :day 29}) => 365 (adjust-year-days 1 {:year 2012 :month 3 :day 1}) => 365 (adjust-year-days 1 {:year 2011 :month 3 :day 1}) => 366
backward-month-array ^
NONE
(def backward-month-array [31 31 29 31 30 31 30 31 31 30 31 30
31 31 28 31 30 31 30 31 31 30 31 30
31 31 28 31 30 31 30 31 31 30 31 30
31 31 28 31 30 31 30 31 31 30 31 30])
link
forward-month-array ^
NONE
(def forward-month-array [31 29 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31
31 28 31 30 31 30 31 31 30 31 30 31])
link
v 3.0
(defn map-to-length
([d rep]
(+ (if (some d [:months :years])
(* 86400000 (adjust-days d rep))
0)
(to-fixed-length d))))
link
(map-to-length {:months 2} {:year 2012 :month 1 :day 31 :backward true}) => 5356800000
ms-length ^
NONE
(def ms-length [[:weeks 604800000]
[:days 86400000]
[:hours 3600000]
[:minutes 60000]
[:seconds 1000]
[:milliseconds 1]])
link
v 3.0
(defn cache
([cache constructor ks flag]
(cond flag
(or (get-in @cache ks)
(let [obj (constructor)]
(swap! cache assoc-in ks obj)
obj))
:else
(constructor))))
link
(f/cache f/+format-cache+ nil [java.text.SimpleDateFormat "HH MM dd Z"] true)
format ^
[t pattern] [t pattern {:keys [cached], :as opts}]
converts a date into a string
v 3.0
(defn format
([t pattern] (format t pattern {}))
([t pattern {:keys [cached] :as opts}]
(let [tmeta (protocol.time/-time-meta (class t))
ftype (-> tmeta :formatter :type)
fmt (cache +format-cache+
(fn [] (protocol.time/-formatter pattern (assoc opts :type ftype)))
[ftype pattern]
cached)]
(protocol.time/-format fmt t opts))))
link
(f/format (Date. 0) "HH MM dd Z" {:timezone "GMT" :cached true}) => "00 01 01 +0000" (f/format (common/calendar (Date. 0) (TimeZone/getTimeZone "GMT")) "HH MM dd Z" {}) => "00 01 01 +0000" (f/format (Timestamp. 0) "HH MM dd Z" {:timezone "PST"}) => "16 12 31 -0800" (f/format (Date. 0) "HH MM dd Z") => string?
parse ^
[s pattern] [s pattern {:keys [cached], :as opts}]
testing format for java.time datastructures
NONE
(defn parse
([s pattern] (parse s pattern {}))
([s pattern {:keys [cached] :as opts}]
(let [opts (merge {:type common/*default-type*} opts)
type (:type opts)
tmeta (protocol.time/-time-meta type)
ptype (-> tmeta :parser :type)
parser (cache +parse-cache+
(fn [] (protocol.time/-parser pattern (assoc opts :type ptype)))
[ptype pattern]
cached)]
(protocol.time/-parse parser s opts))))
link
(-> (f/parse "00 00 01 01 01 1989 +0000" "ss mm HH dd MM yyyy Z" {:type Clock}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Etc/GMT" :long 599619600000, :year 1989, :month 1, :day 1, :hour 1, :minute 0, :second 0, :millisecond 0} (-> (f/parse "00 00 01 01 01 1989 -1000" "ss mm HH dd MM yyyy Z" {:type Clock}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Etc/GMT-10", :long 599583600000, :year 1989, :month 1, :day 1, :hour 1, :minute 0, :second 0, :millisecond 0} (-> (f/parse "00 00 01 01 01 1989 +1000" "ss mm HH dd MM yyyy Z" {:type Clock}) (map/to-map {} common/+default-keys+)) => {:type java.time.Clock$FixedClock, :timezone "Etc/GMT+10", :long 599655600000, :year 1989, :month 1, :day 1, :hour 1, :minute 0, :second 0, :millisecond 0}
by-offset ^
NONE
(def by-offset
(->> (reduce (fn [out ^String id]
(update-in out [(.getRawOffset (TimeZone/getTimeZone id))]
(fnil #(conj % id) #{})))
{}
(TimeZone/getAvailableIDs))
(reduce-kv (fn [out k ids]
(let [id (or (first (filter (fn [^String id] (.startsWith id "Etc")) ids))
(first (sort-by count ids)))]
(assoc out k id)))
{})))
link
by-string-offset ^
NONE
(def by-string-offset
(let [half (generate-offsets)]
(->> (concat (map (fn [[s val]] [(str "+" s) (- val)]) half)
(map (fn [[s val]] [(str "-" s) val]) half))
(into {"Z" 0}))))
link
5 std.time: A Comprehensive Summary
The std.time module in foundation-base provides a comprehensive and extensible framework for handling time-related data in Clojure. It focuses on abstracting various Java time representations (e.g., java.util.Date, java.util.Calendar, java.time.Instant, java.time.ZonedDateTime, Long milliseconds) into a unified system. This is achieved through a set of protocols for instants, durations, and representations, along with multimethods for coercion, formatting, and parsing. The module aims to provide flexible and type-agnostic time manipulation capabilities.
The module is organized into several sub-namespaces:
5.1 std.time.coerce
This namespace handles the conversion of time-related objects between different types, including timezones and instants.
coerce-zone [value opts]: Coerces a timezone object from one type to another (e.g.,Stringtojava.util.TimeZone). It handlesnilvalues by defaulting to the system's timezone.n*coerce-instant [value opts]: Coerces an instant object between various Java time types (e.g.,LongtoZonedDateTime,ClocktoCalendar). It supports conversion to/fromLongmilliseconds and map representations.
5.2 std.time.common
This namespace provides common definitions and utility functions used across the std.time module, including default settings and helper functions for java.util.Calendar.
*default-type*: A dynamic var holding the default time representation type (initiallyclojure.lang.PersistentArrayMap).n*default-timezone*: A dynamic var holding the default timezone string (initiallynil, defaulting to local timezone).n+default-keys+: A list of common time components (millisecond, second, minute, hour, day, month, year) used for map and vector representations.n+zero-values+: A map of zero/default values for time components.n+default-fns+: A map linking time component keywords to their respective protocol functions (e.g.,:millisecondtoprotocol.time/-millisecond).ncalendar [date timezone]: Creates ajava.util.Calendarinstance from ajava.util.Dateandjava.util.TimeZone.ndefault-type [& [cls]]: Getter/setter for*default-type*.nlocal-timezone []: Returns the ID of the system's default timezone.ndefault-timezone [& [tz]]: Getter/setter for*default-timezone*.
5.3 std.time.data
This namespace primarily serves to load and initialize various time-related extensions by requiring other sub-namespaces. It acts as a central point for ensuring all necessary time data structures and formatters are available.
- It requires namespaces for
java.text.SimpleDateFormat,java.sql.Timestamp,java.util.Calendar,java.util.Date,Long,Map,java.util.TimeZone,java.time.format.DateTimeFormatter,java.time.Clock,java.time.Instant,java.time.ZonedDateTime, andjava.time.ZoneId.
5.4 std.time.duration
This namespace provides functionalities for working with time durations, including calculations for adjusting days based on years and months, and converting duration maps to fixed millisecond lengths.
forward-year-array,forward-month-array,backward-year-array,backward-month-array: Arrays used for calculating days in years and months, considering leap years and month lengths.nvariable-keys: Keys representing variable-length durations (e.g.,:months,:years).nms-length: A list of time units and their equivalent milliseconds, used for fixed-length duration conversions.nadjust-options: A map containing options for adjusting days based on year and month, including prediction functions for leap years.nadjust-year-days [years rep]: Calculates the number of days to adjust based on a number of years and a time representation.nadjust-month-days [years months rep]: Calculates the number of days to adjust based on a number of months and a time representation.nadjust-days [duration-map rep]: Calculates the total number of days to adjust based on a duration map (containing:yearsand:months) and a time representation.nto-fixed-length [m]: Converts a duration map (containing fixed units like:days,:hours) to its equivalent length in milliseconds.nmap-to-length [d rep]: Converts a duration mapdto its total length in milliseconds, considering variable units like:monthsand:yearsbased on a time representationrep.nprotocol.time/-from-lengthextension: Extends the default implementation to convert a long duration into a map of time units.nprotocol.time/IDurationextension: Extendsclojure.lang.APersistentMapto implementIDuration, allowing duration maps to be converted to lengths.
5.5 std.time.format
This namespace provides functions for formatting time objects into strings and parsing strings into time objects, supporting various Java formatters.
+format-cache+,+parse-cache+: Atoms used for caching formatter and parser instances for performance.ncache [cache constructor ks flag]: A helper function to manage caching of formatter/parser instances.nformat [t pattern & [opts]]: Formats a time objecttinto a string according to apatternand optionalopts(including caching). It uses the appropriate formatter based on the type oft.n*parse [s pattern & [opts]]: Parses a stringsinto a time object according to apatternand optionalopts(including caching and specifying the target type).
5.6 std.time.long
This namespace provides extensions for java.lang.Long to integrate it into the std.time protocol system, treating Long values as milliseconds since the epoch.
long-meta: A map defining the metadata forLongas a time instant, including its base type, and map conversion proxies.nprotocol.time/-time-metaextension: ExtendsLongto returnlong-meta.nprotocol.time/IInstantextension: ExtendsLongto implementIInstant, defining-to-long,-has-timezone?,-get-timezone, and-with-timezone.nprotocol.time/IDurationextension: ExtendsLongto implementIDuration, defining-to-length.nprotocol.time/-from-longextension: ExtendsLongto return theLongvalue itself.nprotocol.time/-nowextension: ExtendsLongto return the current time in milliseconds.nprotocol.time/-from-lengthextension: ExtendsLongto return theLongvalue itself.
5.7 std.time.map
This namespace provides functionalities for converting time objects to and from map representations, allowing for easy manipulation of individual time components. It also extends clojure.lang.PersistentArrayMap and clojure.lang.PersistentHashMap to be time instants.
to-map [t opts & [ks]]: Converts a time objecttinto a map representation, extracting specified keys (ks) and including timezone and long value.nfrom-map [m opts fill]: Converts a map representationmback into a time object of a specified type, filling in missing values withfill.nwith-timezone [m tz]: Adds or changes the timezone of a map-based time representation.narraymap-meta: Metadata forPersistentArrayMapas a time instant.nprotocol.time/-time-metaextension: ExtendsPersistentArrayMapto returnarraymap-meta.nprotocol.time/IInstantextension: ExtendsPersistentArrayMapto implementIInstant.nprotocol.time/IRepresentationextension: ExtendsPersistentArrayMapto implementIRepresentation, allowing access to individual time components.nprotocol.time/-from-longextension: ExtendsPersistentArrayMapto create a map representation from a long.nprotocol.time/-nowextension: ExtendsPersistentArrayMapto create a map representation of the current time.nhashmap-meta: Metadata forPersistentHashMapas a time instant.nprotocol.time/-time-metaextension: ExtendsPersistentHashMapto returnhashmap-meta.nprotocol.time/-from-longextension: ExtendsPersistentHashMapto create a map representation from a long.nprotocol.time/-nowextension: ExtendsPersistentHashMapto create a map representation of the current time.nprotocol.time/IInstantextension: ExtendsPersistentHashMapto implementIInstant.nprotocol.time/IRepresentationextension: ExtendsPersistentHashMapto implementIRepresentation.
5.8 std.time.vector
This namespace provides functionalities for converting time objects to and from vector representations, allowing for ordered access to time components.
to-vector [t opts ks]: Converts a time objecttinto a vector representation, extracting specified keys (ks) in order. It supports:alland keyword-based selection forks.
5.9 std.time.zone
This namespace provides utilities for working with timezones, including mapping offsets to timezone IDs and generating offset strings.
by-offset: A map that groups timezone IDs by their raw offset in milliseconds.npad-zeros [s]: Pads a single-digit string with a leading zero.ngenerate-offsets []: Generates a sequence of common time offsets (e.g., "00:00", "00:15") and their millisecond equivalents.n*by-string-offset: A map that maps string representations of offsets (e.g., "+00:00", "-05:00", "Z") to their millisecond values.
Overall Importance:
The std.time module is a critical component for any application within the foundation-base project that deals with dates, times, or durations. Its key contributions include:
- Unified Time Handling: Provides a consistent API for working with diverse Java time types, simplifying time manipulation logic.n Flexibility and Extensibility: The protocol-based design allows for easy integration of new time representations and custom behaviors.n Timezone Awareness: Comprehensive support for timezones ensures accurate calculations and displays across different geographical regions.n Human-Readable Formatting: Tools for formatting and parsing time strings enhance user interaction and data exchange.n Duration Management: Capabilities for calculating and converting time durations are essential for scheduling, logging, and performance analysis.
By offering a robust and flexible time management system, std.time significantly contributes to the foundation-base project's ability to handle complex temporal data accurately and efficiently.