Is it possible to iterate the time between an interval's beginning and ending date, one day at a time? Using the clj-time
library for Clojure would be fine as well!
Asked
Active
Viewed 6,271 times
8

Blorgbeard
- 101,031
- 48
- 228
- 272

Mike
- 19,267
- 11
- 56
- 72
4 Answers
14
Yep.
Something like this:
DateTime now = DateTime.now();
DateTime start = now;
DateTime stop = now.plusDays(10);
DateTime inter = start;
// Loop through each day in the span
while (inter.compareTo(stop) < 0) {
System.out.println(inter);
// Go to next
inter = inter.plusDays(1);
}
Additionally, here's the implementation for Clojure's clj-time:
(defn date-interval
([start end] (date-interval start end []))
([start end interval]
(if (time/after? start end)
interval
(recur (time/plus start (time/days 1)) end (concat interval [start])))))

Mike
- 19,267
- 11
- 56
- 72

Oskar Kjellin
- 21,280
- 10
- 54
- 93
-
Was hoping to avoid writing an imperative solution, but I guess it's all we have. Crafting a clj-time Clojure solution, will combine with your answer momentarily. – Mike Aug 19 '12 at 22:06
-
@Mike modified it to use joda-time. You could just create a method that returns a `List
` – Oskar Kjellin Aug 19 '12 at 22:07 -
Thanks. Yeah, my original intent was to find this functionality in the API. Thanks anyhow! – Mike Aug 19 '12 at 22:10
-
@Mike Don't think there is any. Would probably depend a bit on what you wanted it for. Keeping time or not, timezones etc. – Oskar Kjellin Aug 19 '12 at 22:12
-
2I know this is just demo code, but note that you should avoid calling DateTime.now() twice like that in the same function, as it leads to surprises. The now() second time around isn't the same now() as the first time around. This can lead to nasty gremlins if you run over midnight boundaries and take the date of each, or something like that, so it's a bad habit. Make a single local "now" variable to base your arithmetic off instead. – Alastair Maw Aug 19 '12 at 22:13
-
Fantastic catch, @AlastairMaw ! – Mike Aug 19 '12 at 22:15
-
See also http://stackoverflow.com/a/1175134/217893 and http://stackoverflow.com/a/847597/217893. The second one is more general. – Alastair Maw Aug 19 '12 at 22:15
-
Fair warning: you cannot assume that these will be at the same time of day. If there's a daylight savings change between two days? Or a leapsecond? I don't know how the above code will respond, and I suspect you don't, either. (Dates are hard.) – Louis Wasserman Aug 20 '12 at 00:02
-
@LouisWasserman Regarding `plusDays` not handling DST and such anomalies… [Joda-Time is smart](http://www.joda.org/joda-time/apidocs/org/joda/time/DateTime.html#plusDays(int)). No problem. Doc says: `For example, if the cutover is from 01:59 to 03:00 and the result of this method would have been 02:30, then the result will be adjusted to 03:30.` – Basil Bourque Mar 06 '14 at 06:36
-
For large intervals it could be better to use (into) instead of (concat) to avoid blowing the stack. (into interval [start]) ref: http://stuartsierra.com/2015/04/26/clojure-donts-concat – rodnaph May 18 '15 at 19:42
8
This should work.
(take-while (fn [t] (cljt/before? t to)) (iterate (fn [t] (cljt/plus t period)) from))

Shashank Kadne
- 7,993
- 6
- 41
- 54

Hendekagon
- 4,565
- 2
- 28
- 43
-
This is a great solution and deserves more attention. It deserves note that what this is doing is creating a lazy seq of datetimes in the interval. This is super idiomatic and can be *composed* with all sorts of useful stuff like (doseq [t (seq-generator...)]). The only thing different could be a) put this inside a function so that it'll return the seq, e.g. (defn time-seq [...] ...). In that time-seq returning fn, you'd also want to unpack the interval into to/from instead of relying on global vars. – elliot42 Jan 10 '14 at 21:25
2
Using clj-time, the-interval is a Joda Interval :
(use '[clj-time.core :only (days plus start in-days)])
(defn each-day [the-interval f]
(let [days-diff (in-days the-interval)]
(for [x (range 0 (inc days-diff))] (f (plus (start the-interval) (days x))))))

DanLebrero
- 8,545
- 1
- 29
- 30
1
Clj-time has the periodic-seq function. It looks very much like Hendekagon's solution. That could be combined with the begin/end functions to create something like:
(defn interval-seq
"Returns sequence of period over interval.
For example, the individual days in an interval using (clj-time.core/days 1) as the period."
[interval period]
(let [end (clj-time.core/end interval)]
(take-while #(clj-time.core/before? % end) (clj-time.periodic/periodic-seq (clj-time.core/start interval) period ))))

robbie.huffman
- 423
- 4
- 12