使用#inst的Clojure日期时间算术

时间:2019-01-23 05:33:26

标签: datetime clojure

我有很多日期,例如

(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")

我需要将它们之间的时间差计算为分钟的整数。

我搜索了Clojure时间库,但除了如何将转换为外,对#inst的库知之甚少。

如何使用#inst的seq进行时间算术以获取差异并将diff转换为分钟的整数?

感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

#inst "..."只是一个java.util.Date对象,因此您可以获取到距纪元以来的毫秒数:

(->> (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)

答案 1 :(得分:1)

略微改善@akond答案(并且不使用任何外部库):

(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)

答案 2 :(得分:1)

到目前为止,所有答案在第一个和最后一个分组中似乎相去甚远。另外,此解决方案将返回Integers作为必需的问题。

请注意,它需要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)))))

将返回列表:

(53176 3 1370 37347 53156)

使用几个库clojure.math.numeric-towerClojure.Java-Time,我们可以执行以下操作

(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))))

这将返回相同的列表。

引入xforms将使我们能够使用换能器。

(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)

并获得相同的最终结果。

答案 3 :(得分:1)

还使用本机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))))]

结果:

durations => [53176 3 1370 37347 53156]

请注意:如原始问题所述,这些值将被删减整分钟。


讨论:

由于前两个答案是错误的,因此这表明使用java.time的内置时间实用程序而不是尝试快速编写本地解决方案的价值。在大多数情况下,我发现使用本机Java类和函数要比使用clj-time这样的Clojure包装器要容易得多,后者可以包装已弃用的库Joda-Time。

警告:

从Java 8开始,clj-time(及其包装的Joda Time库)应被视为已弃用。来自the clj-time homepage

  

Clojure的日期和时间库,包装了乔达时间库。   Joda Time网站说:

     

请注意,从Java SE 8开始,要求用户迁移到   java.time(JSR-310)-JDK的核心部分取代了   项目。

还请注意,Joda-Time的作者还是java.time程序包的作者,该程序包扩展了Joda-Time并纠正了一些仅随年龄增长而明显的缺点。 The Joda-Time homepage本身说:

  

请注意,从Java SE 8开始,要求用户迁移到   java.time(JSR-310)-JDK的核心部分取代了   项目。


更新

由于原始时间戳记(大部分是字符串),因此我认为OP可能会更轻松地解决该问题。如果要使用Clojure #inst语法,则答案甚至更简单:

  ; 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))))]

请注意,新的映射功能#(.toInstant %)是唯一需要更改的东西。