Reputation: 63
I have lots of dates in the #inst format like
(list #inst "2016-04-30T10:29:17.000-00:00"
#inst "2016-03-24T12:13:12.000-00:00"
#inst "2016-03-24T12:09:43.000-00:00"
#inst "2016-03-23T13:19:03.000-00:00"
#inst "2016-02-26T14:51:37.000-00:00"
#inst "2016-01-20T16:55:24.000-00:00")
I need to calculate time differences between them as integers of minutes.
I searched for Clojure time libraries but can't see much about #inst's other than how to convert to them.
How can I do time arithmetic with the seq's of #inst's to get the diffs and convert the diffs to ints of minutes?
Thanks for your help.
Upvotes: 3
Views: 1500
Reputation: 29958
Yet another solution using the native java.time
:
(ns tst.tupelo.java-time
(:import [ java.time Duration ZoneId ZonedDateTime ]))
; note that instants are in DESCENDING order
(let [instants ["2016-04-30T10:29:17.000-00:00"
"2016-03-24T12:13:12.000-00:00"
"2016-03-24T12:09:43.000-00:00"
"2016-03-23T13:19:03.000-00:00"
"2016-02-26T14:51:37.000-00:00"
"2016-01-20T16:55:24.000-00:00"]
zoned-date-times (mapv #(ZonedDateTime/parse %) instants)
zdt-pairs (partition 2 1 zoned-date-times)
durations (vec (for [[interval-stop interval-start] zdt-pairs]
(.toMinutes ; *** truncates ***
(Duration/between interval-start interval-stop))))]
with result:
durations => [53176 3 1370 37347 53156]
PLEASE NOTE: these values are TRUNCATED to whole minutes, as the original problem stated.
Since the first 2 answers were wrong, this shows the value of using the built-in capabilities of java.time
instead of attempting to quickly write a home-grown solution. In most instances, I find it easier to use the native Java classes & functions vs a Clojure wrapper like clj-time
, which wraps the deprecated library Joda-Time.
Beginning with Java 8, clj-time
(and the Joda Time library it wraps) should be considered deprecated. From the clj-time
homepage:
A date and time library for Clojure, wrapping the Joda Time library. The Joda Time website says:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Note also that the author of Joda-Time is also the author of the java.time
package, which extends Joda-Time and corrects some shortcomings that were only apparent with age. The Joda-Time homepage itself says:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Since the original timestamps were (mostly) strings, I thought the OP might fine that solution easier. If you want to use the Clojure #inst
syntax, the answer is even easier:
; note that instants are in DESCENDING order
(let [instants [ #inst "2016-04-30T10:29:17.000-00:00"
#inst "2016-03-24T12:13:12.000-00:00"
#inst "2016-03-24T12:09:43.000-00:00"
#inst "2016-03-23T13:19:03.000-00:00"
#inst "2016-02-26T14:51:37.000-00:00"
#inst "2016-01-20T16:55:24.000-00:00"]
instants (mapv #(.toInstant %) instants)
inst-pairs (partition 2 1 instants)
durations (vec (for [[interval-stop interval-start] inst-pairs]
(.toMinutes ; *** truncates ***
(Duration/between interval-start interval-stop))))]
Note that the new mapping function #(.toInstant %)
is the only thing that needs to change.
Upvotes: 3
Reputation: 113
to slightly improve @akond answer (and use no external libraries):
(defn diff-minutes [[x y]]
(quot (- x y) 60000))
(def l (list #inst "2016-04-30T10:29:17.000-00:00"
#inst "2016-03-24T12:13:12.000-00:00"
#inst "2016-03-24T12:09:43.000-00:00"
#inst "2016-03-23T13:19:03.000-00:00"
#inst "2016-02-26T14:51:37.000-00:00"
#inst "2016-01-20T16:55:24.000-00:00"))
(->> l (map #(.getTime %))
(partition 2 1)
(map diff-minutes))
;; or, an anonymous version
(->> l (map #(.getTime %))
(partition 2 1)
(map (fn [[x y]] (quot (- x y) 60000))))
(53176 3 1370 37347 53156)
Upvotes: 1
Reputation: 1306
All answers so far seem to be off quite a bit in the first and last groupings. Also, this solution will return Integers as the question required.
Please note that it requires Java 8+.
(def l (list #inst "2016-04-30T10:29:17.000-00:00"
#inst "2016-03-24T12:13:12.000-00:00"
#inst "2016-03-24T12:09:43.000-00:00"
#inst "2016-03-23T13:19:03.000-00:00"
#inst "2016-02-26T14:51:37.000-00:00"
#inst "2016-01-20T16:55:24.000-00:00"))
(import 'java.time.temporal.ChronoUnit)
(->> l
(map #(.toInstant %))
(partition 2 1)
(map (fn [[a b]] (Math/abs (.between ChronoUnit/MINUTES b a)))))
will return the list:
(53176 3 1370 37347 53156)
Using a couple libraries clojure.math.numeric-tower and Clojure.Java-Time, we can do the following
(require '[java-time])
(require '[clojure.math.numeric-tower :refer [abs]])
(->> l
(map java-time/instant)
(partition 2 1)
(map (comp abs (partial apply java-time/time-between :minutes))))
which will return the same list.
Introducing xforms will allow us to use transducers.
(require '[java-time])
(require '[clojure.math.numeric-tower :refer [abs]])
(require '[net.cgrand.xforms :as x])
(sequence
(comp
(map java-time/instant)
(x/partition 2 1)
(map (partial apply java-time/time-between :minutes))
(map abs))
l)
and achieve the same end result.
Upvotes: 1
Reputation: 16035
#inst "..."
is just a java.util.Date
object, so what you can do is get its number of milliseconds since the epoch:
(->> (list #inst "2016-04-30T10:29:17.000-00:00"
#inst "2016-03-24T12:13:12.000-00:00"
#inst "2016-03-24T12:09:43.000-00:00"
#inst "2016-03-23T13:19:03.000-00:00"
#inst "2016-02-26T14:51:37.000-00:00"
#inst "2016-01-20T16:55:24.000-00:00")
(map #(.. %
toInstant
(atZone (ZoneId/systemDefault))
toLocalDateTime))
(partition 2 1)
(map (fn [[a b]] (Math/abs (.until a b ChronoUnit/MINUTES)))))
=> (53236 3 1370 37347 53156)
Upvotes: 1