For this week’s Clojure Cookbook preview recipe we’ll be taking a dive
clj-time, one of my
favorite libraries for dealing with time in Clojure.
Most commonly I use
clj-time for its parsing and
capabilities. The library is a trove of goodies, however; it handles leap
time, coercions, time zones and even predicates about time. One of my
favorite ancillary function, though, is
This is a love story about that function…
Generating Ranges of Dates and Times
You need to generate a lazy sequence covering a range of dates and/or times.
This problem has no easy solution in Java, nor does it have one in
Clojure–third-party libraries included. It is possible to use
clj-time to get close, though.
you can create a function
time-range that mimics
capabilities but for
(require '[clj-time.core :as time]) (require '[clj-time.periodic :as time-period]) (defn time-range "Return a lazy sequence of DateTime's from start to end, incremented by 'step' units of time." [start end step] (let [inf-range (time-period/periodic-seq start step) below-end? (fn [t] (time/within? (time/interval start end) t))] (take-while below-end? inf-range)))
This is how you can use the
(def months-of-the-year (time-range (time/date-time 2012 01) (time/date-time 2013 01) (time/months 1))) ;; months-of-the-year is an unrealized lazy sequence (realized? months-of-the-year) ;; -> false (count months-of-the-year) ;; -> 12 ;; now realized (realized? months-of-the-year) ;; -> true
While there is no ready-made, out-of-the-box
time-range solution in
Clojure, it is trivial to construct such a function with purely lazy
The basis for our lazy
time-range function is an infinite sequences of values
with a fixed starting time:
(defn time-range "Return a lazy sequence of DateTime's from start to end, incremented by 'step' units of time." [start end step] (let [inf-range (time-period/periodic-seq start step) ; <1> below-end? (fn [t] (time/within? (time/interval start end) ; <2> t))] (take-while below-end? inf-range))) ; <3>
- Acquire a lazy infinite sequence.
- Create a predicate to terminate the sequence.
- Modify the infinite sequence to terminate when
below-end?fails (lazily, of course).
step returns an infinite lazy
sequence of values beginning at
start, each subsequent value one
later than the last.
Having a lazy sequence to infinite is one thing, but we need a lazy way to
stop acquiring values when
end is reached. The
clj-time.core/interval to construct an interval from
clj-time.core/within? to test if a time
t falls within
that interval. This function is passed as the predicate to
will lazily consume values until
time-range returns a lazy
DateTime objects that stretches from a start time to an end time,
stepped appropriately by the provided
Imagine trying to build something similar in a language without first-class laziness.
- Comparing Dates
- Calculating the Length of a Time Interval
- Generating Ranges of Dates and Times using Native Java Types for an alternative that uses only native types
- Retrieving Dates Relative to One Another