X Tutup
(ns java-time.core (:refer-clojure :exclude (zero? range max min abs < > <= >= * - + neg? =)) (:require [clojure.core :as cc]) (:import [java.time.temporal ValueRange] [java.time.chrono Chronology])) (defprotocol Amount (zero? [a] "True if the amount is zero") (negative? [a] "True if the amount is negative") (negate [a] "Negates a temporal amount: (negate (negate x)) == x") (abs [a] "Returns the absolute value of a temporal amount: (abs (negate x)) == (abs x)")) (defprotocol Supporting (supports? [o p] "True if the `o` entity supports the `p` property")) (defprotocol HasChronology (^java.time.chrono.Chronology chronology [o] "The `Chronology` of the entity")) (defprotocol HasFields (fields [o] "Fields present in this temporal entity") (field* [o k] "Internal use")) (defprotocol HasUnits (units [o] "Units present in this temporal entity.") (unit* [o k] "Internal use")) (defprotocol HasProperties (properties [o] "Map of properties present in this temporal entity") (property [o k] "Property of this temporal entity under key `k`")) (defprotocol As (as* [o k] "Value of property/unit identified by key/object `k` of the temporal entity `o`")) (defprotocol ReadableProperty (value [p] "Value of the property")) (defprotocol ReadableRangeProperty (range [p] "Range of values for this property") (min-value [p] "Minimum value of this property") (largest-min-value [p] "Largest minimum value of this property") (smallest-max-value [p] "Smallest maximum value of this property, e.g. 28th of February for months") (max-value [p] "Maximum value of this property, e.g. 29th of February for months")) (defprotocol WritableProperty (with-value [p v] "Underlying temporal entity with the value of this property set to `v`")) (defprotocol WritableRangeProperty (with-min-value [p] "Underlying temporal entity with the value set to the minimum available for this property") (with-largest-min-value [p] "Underlying temporal entity with the value set to the largest minimum available for this property") (with-smallest-max-value [p] "Underlying temporal entity with the value set to the smallest maximum available for this property") (with-max-value [p] "Underlying temporal entity with the value set to the maximum available for this property")) (defprotocol KnowsTimeBetween (time-between [o e u] "Time between temporal entities `o` and `e` in unit `u`. ``` (j/time-between (j/local-date 2015) (j/local-date 2016) :days) => 365 (j/time-between :days (j/local-date 2015) (j/local-date 2016)) => 365 ```")) (defprotocol KnowsIfLeap (leap? [o] "True if the year of this entity is a leap year.")) (defprotocol Truncatable (truncate-to [o u] "Truncates this entity to the specified time unit. Only works for units that divide into the length of standard day without remainder (up to `:days`).")) (defprotocol HasZone (with-zone [o z] "Returns this temporal entity with the specified `ZoneId`")) (defprotocol Plusable "Internal" (seq-plus [o os])) (defprotocol Minusable "Internal" (seq-minus [o os])) (defprotocol Multipliable (multiply-by [o v] "Entity `o` multiplied by the value `v`")) (defprotocol Ordered (single-before? [a b] "Internal use") (single-after? [a b] "Internal use")) (defprotocol Convert (-convert [a b] "Internal use. Convert `b` to a value compatible with `a`'s implementation of single-after? etc, and one that is useable as the first argument to single-after? etc.")) (extend-type Object Convert (-convert [a b] b)) (defn as "Values of property/unit identified by keys/objects `ks` of the temporal entity `o`, e.g. ``` (as (duration 1 :hours) :minutes) => 60 (as (local-date 2015 9) :year :month-of-year) => [2015 9] ```" ([o k] (as* o k)) ([o k1 k2] [(as* o k1) (as* o k2)]) ([o k1 k2 & ks] (into (as o k1 k2) (map #(as* o %)) ks))) (defn before? "Returns `true` if time entities are ordered from the earliest to the latest (same semantics as `<`), otherwise `false`. ``` (before? (local-date 2009) (local-date 2010) (local-date 2011)) => true (before? (interval (instant 10000) (instant 1000000)) (instant 99999999)) => true ```" ([x] true) ([x y] (single-before? x (-convert x y))) ([x y & more] (let [y (-convert x y)] (if (single-before? x y) (if-some [n (next more)] (recur y (first more) n) (single-before? y (-convert y (first more)))) false)))) (defn after? "Returns `true` if time entities are ordered from the latest to the earliest (same semantics as `>`), otherwise `false`. ``` (after? (local-date 2011) (local-date 2010) (local-date 2009)) => true (after? (instant 99999999) (interval (instant 10000) (instant 1000000))) => true ```" ([x] true) ([x y] (single-after? x (-convert x y))) ([x y & more] (let [y (-convert x y)] (if (single-after? x y) (if-some [n (next more)] (recur y (first more) n) (single-after? y (-convert y (first more)))) false)))) (defn ^:private single-not-after? [x y] (or (cc/= x y) (single-before? x y))) (defn not-after? "Returns `true` if time entities are ordered from the earliest to the latest (same semantics as `<=`), otherwise `false`. ``` (not-after? (local-date 2009) (local-date 2010) (local-date 2011)) ;=> true (not-after? (interval (instant 10000) (instant 1000000)) (instant 99999999)) ;=> true ```" ([x] true) ([x y] (single-not-after? x (-convert x y))) ([x y & more] (let [y (-convert x y)] (and (single-not-after? x y) (if-some [n (next more)] (recur y (first more) n) (single-not-after? y (-convert y (first more)))))))) (defn ^:private single-not-before? [x y] (or (cc/= x y) (single-after? x y))) (defn not-before? "Returns `true` if time entities are ordered from the latest to the earliest (same semantics as `>=`), otherwise `false`. ``` (not-before? (local-date 2011) (local-date 2010) (local-date 2009)) ;=> true (not-before? (instant 99999999) (interval (instant 10000) (instant 1000000))) ;=> true ```" ([x] true) ([x y] (single-not-before? x (-convert x y))) ([x y & more] (let [y (-convert x y)] (and (single-not-before? x y) (if-some [n (next more)] (recur y (first more) n) (single-not-before? y (-convert y (first more)))))))) (defn plus "Adds all of the `os` to the time entity `o`. `plus` is not commutative, the first argument is always the entity which will accumulate the rest of the arguments. ``` (j/plus (j/local-date 2015) (j/years 1)) => ```" [o & os] (seq-plus o os)) (defn minus "Subtracts all of the `os` from the time entity `o` ``` (j/minus (j/local-date 2015) (j/years 1)) => ```" [o & os] (if (seq os) (seq-minus o os) (negate o))) ;;;;; Clojure types (extend-type Number ReadableProperty (value [n] n) WritableProperty (with-value [n v] v) Plusable (seq-plus [n xs] (apply cc/+ n xs)) Minusable (seq-minus [n xs] (apply cc/- n xs)) Multipliable (multiply-by [n v] (cc/* n (value v))) Amount (zero? [n] (cc/zero? n)) (negative? [n] (cc/neg? n)) (negate [n] (cc/- n)) (abs [n] (Math/abs (long n)))) (extend-type nil ReadableProperty (value [_] nil) WritableProperty (with-value [_ v] v)) (def readable-range-property-fns {:min-value (fn [p] (.getMinimum ^ValueRange (range p))) :largest-min-value (fn [p] (.getLargestMinimum ^ValueRange (range p))) :smallest-max-value (fn [p] (.getSmallestMaximum ^ValueRange (range p))) :max-value (fn [p] (.getMaximum ^ValueRange (range p)))}) ;; vars named after excluded clojure.core vars (def ^{:arglists '([x] [x y] [x y & more])} <= "Returns `true` if time entities are ordered from the earliest to the latest (same semantics as `<=`), otherwise `false`. ``` (j/<= (local-date 2009) (local-date 2010) (local-date 2011)) ;=> true (j/<= (interval (instant 10000) (instant 1000000)) (instant 99999999)) ;=> true ```" not-after?) (def ^{:arglists '([x] [x y] [x y & more])} >= "Returns `true` if time entities are ordered from the latest to the earliest (same semantics as `>=`), otherwise `false`. ``` (j/>= (local-date 2011) (local-date 2010) (local-date 2009)) ;=> true (j/>= (instant 99999999) (interval (instant 10000) (instant 1000000))) ;=> true ```" not-before?) (def ^{:arglists '([x] [x y] [x y & more])} < "Returns `true` if time entities are ordered from the earliest to the latest (same semantics as `<`), otherwise `false`. ``` (j/< (local-date 2009) (local-date 2010) (local-date 2011)) => true (j/< (interval (instant 10000) (instant 1000000)) (instant 99999999)) => true ```" before?) (def ^{:arglists '([x] [x y] [x y & more])} > "Returns `true` if time entities are ordered from the latest to the earliest (same semantics as `>`), otherwise `false`. ``` (j/> (local-date 2011) (local-date 2010) (local-date 2009)) => true (j/> (instant 99999999) (interval (instant 10000) (instant 1000000))) => true ```" after?) (def ^{:arglists '([o & os])} + "Adds all of the `os` to the time entity `o`. `+` is not commutative, the first argument is always the entity which will accumulate the rest of the arguments. ``` (j/+ (j/local-date 2015) (j/years 1)) => ```" plus) (def ^{:arglists '([o & os])} - "Subtracts all of the `os` from the time entity `o` ``` (j/- (j/local-date 2015) (j/years 1)) => ```" minus) (defn max "Latest/longest of the given time entities. Entities should be of the same type" [o & os] (first (sort #(compare %2 %1) (cons o os)))) (defn min "Earliest/shortest of the given time entities. Entities should be of the same type" [o & os] (first (sort (cons o os)))) (defn = "Returns true if all time entities represent the same time, otherwise false. `j/=` is not commutative, the first argument is always the entity which will accumulate the rest of the arguments. e.g., (j/= (j/day-of-week :thursday) :thursday) => true" ([x] true) ([x y] (cc/= x (-convert x y))) ([x y & more] (let [y (-convert x y)] (and (cc/= x y) (if-some [n (next more)] (recur y (first more) n) (cc/= y (-convert y (first more)))))))) ;; these can't be aliased since they're protocol methods (defn neg? "True if the amount is negative" [a] (negative? a)) (defn * "Entity `o` multiplied by the value `v`" [o v] (multiply-by o v))
X Tutup