Skip to content

Commit

Permalink
shift with temporal amounts
Browse files Browse the repository at this point in the history
  • Loading branch information
henryw374 committed Mar 18, 2024
1 parent b7f6558 commit a5fefef
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 44 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ can decide on a case by case basis how to tackle that.

### What about Existing Cross-platform date/time APIs?

[Tick](https://github.com/juxt/tick) (which I help maintain) is great for application developers who want a
[Tick](https://github.com/juxt/tick) is great for application developers who want a
cross-platform date-time library based on the java.time API. Tick provides much useful functionality
on top of java.time, but users know they can always drop
to [cljc.java-time](https://github.com/henryw374/cljc.java-time),
Expand Down Expand Up @@ -224,6 +224,7 @@ aka construction a new temporal from one of the same type

;; move date forward 3 days
(t/>> a-date 3 t/days-property)
(t/>> a-date 3 a-temporal-amount)

;; set a particular field
(t/with a-yearmonth 3030 t/years-property)
Expand Down
32 changes: 25 additions & 7 deletions gen_in/tempo.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@

(defn clock-fixed
"create a stopped clock"
[instant ^String zone-str]
#?(:cljay (Clock/fixed ^Instant instant (ZoneId/of zone-str))
:cljs (clock/clock (constantly instant) zone-str)))
([^ZonedDateTime zdt]
#?(:cljay (Clock/fixed (.toInstant zdt) (.getZone zdt))
:cljs (clock/clock (constantly (.toInstant zdt)) (.-timeZoneId zdt))))
([^Instant instant ^String zone-str]
#?(:cljay (Clock/fixed instant (ZoneId/of zone-str))
:cljs (clock/clock (constantly instant) zone-str))))

(defn clock-with-zone
"ticking clock in given timezone_id"
Expand Down Expand Up @@ -305,6 +308,21 @@
"see guardrails section at https://github.com/henryw374/tempo?tab=readme-ov-file#guardrails"
{}))))

(defn throw-if-months-or-years-in-amount [temporal temporal-amount]
(when
(and *block-non-commutative-operations*
(not (or (monthday? temporal) (yearmonth? temporal)))
#?(:cljay (and (instance? Period temporal-amount)
(or
(not (zero? (.getYears ^Period temporal-amount)))
(not (zero? (.getMonths ^Period temporal-amount)))))
:cljs (or
(not (zero? (.-years ^js temporal-amount)))
(not (zero? (.-months ^js temporal-amount))))))
(throw (ex-info
"see guardrails section at https://github.com/henryw374/tempo?tab=readme-ov-file#guardrails"
{}))))

(defn with [temporal value property]
(throw-if-set-months-or-years temporal property)
#?(:cljay (.with ^Temporal temporal ^TemporalField (field property) ^long value)
Expand All @@ -319,8 +337,8 @@
(goog.object/get (str property "s")))))

(defn >>
#_([temporal temporal-property]
(throw-if-set-months-or-years temporal temporal-amount)
([temporal temporal-amount]
(throw-if-months-or-years-in-amount temporal temporal-amount)
#?(:cljay (.plus ^Temporal temporal ^TemporalAmount temporal-amount)
:cljs (.add ^js temporal temporal-amount)))
([temporal amount temporal-property]
Expand All @@ -329,8 +347,8 @@
:cljs (.add ^js temporal (js-obj (str temporal-property "s") amount)))))

(defn <<
#_([temporal temporal-amount]
(throw-if-set-months-or-years temporal temporal-amount)
([temporal temporal-amount]
(throw-if-months-or-years-in-amount temporal temporal-amount)
#?(:cljay (.minus ^Temporal temporal ^TemporalAmount temporal-amount)
:cljs (.subtract ^js temporal temporal-amount)))
([temporal amount temporal-property]
Expand Down
55 changes: 42 additions & 13 deletions src/com/widdindustries/tempo.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
(ns
com.widdindustries.tempo
""
(:refer-clojure :exclude [min max > < >= <= >> <<])
(:import
[java.time
Expand Down Expand Up @@ -34,7 +33,10 @@

(set! *warn-on-reflection* true)

(defn extend-all-cljs-protocols [])
(defn
extend-all-cljs-protocols
"in cljs envs, this makes `=`, `compare` and `hash` work on the value of Temporal entities.\r\n It is optional, so that if this behaviour is not required, the resulting build size can be reduced. \r\n "
[])

(defn legacydate? [v] (instance? java.util.Date v))

Expand All @@ -58,24 +60,28 @@

(defn zdt? [v] (instance? ZonedDateTime v))

(defn
clock-system-default-zone
"a ticking clock having the ambient zone. "
[]
(Clock/systemDefaultZone))

(defn
clock-fixed
[instant ^String zone-str]
(Clock/fixed ^Instant instant (ZoneId/of zone-str)))
"create a stopped clock"
([^ZonedDateTime zdt] (Clock/fixed (.toInstant zdt) (.getZone zdt)))
([^Instant instant ^String zone-str]
(Clock/fixed instant (ZoneId/of zone-str))))

(defn
clock-with-zone
"ticking clock in given timezone_id"
[^String timezone_id]
(Clock/system (ZoneId/of timezone_id)))

(defn
clock-system-default-zone
"a ticking clock having the ambient zone. "
[]
(Clock/systemDefaultZone))

(defn
clock-offset-millis
"offset an existing clock by offset-millis"
[clock offset-millis]
(Clock/offset clock (Duration/ofMillis offset-millis)))

Expand All @@ -91,7 +97,7 @@
[arg & args]
(assert (every? some? (cons arg args)))
(reduce
(fn* [p1__112291# p2__112292#] (greater p1__112291# p2__112292#))
(fn* [p1__36691# p2__36692#] (greater p1__36691# p2__36692#))
arg
args))

Expand All @@ -103,7 +109,7 @@
[arg & args]
(assert (every? some? (cons arg args)))
(reduce
(fn* [p1__112293# p2__112294#] (lesser p1__112293# p2__112294#))
(fn* [p1__36693# p2__36694#] (lesser p1__36693# p2__36694#))
arg
args))

Expand Down Expand Up @@ -337,7 +343,24 @@
(not (or (monthday? temporal) (yearmonth? temporal))))
(throw
(ex-info
"shifting by years or months yields odd results depending on input. intead shift a year-month, then set non-yearmonth parts"
"see guardrails section at https://github.com/henryw374/tempo?tab=readme-ov-file#guardrails"
{}))))

(defn
throw-if-months-or-years-in-amount
[temporal temporal-amount]
(when
(and
*block-non-commutative-operations*
(not (or (monthday? temporal) (yearmonth? temporal)))
(and
(instance? Period temporal-amount)
(or
(not (zero? (.getYears ^Period temporal-amount)))
(not (zero? (.getMonths ^Period temporal-amount))))))
(throw
(ex-info
"see guardrails section at https://github.com/henryw374/tempo?tab=readme-ov-file#guardrails"
{}))))

(defn
Expand All @@ -354,6 +377,9 @@

(defn
>>
([temporal temporal-amount]
(throw-if-months-or-years-in-amount temporal temporal-amount)
(.plus ^Temporal temporal ^TemporalAmount temporal-amount))
([temporal amount temporal-property]
(throw-if-set-months-or-years temporal temporal-property)
(.plus
Expand All @@ -364,6 +390,9 @@

(defn
<<
([temporal temporal-amount]
(throw-if-months-or-years-in-amount temporal temporal-amount)
(.minus ^Temporal temporal ^TemporalAmount temporal-amount))
([temporal amount temporal-property]
(throw-if-set-months-or-years temporal temporal-property)
(.minus
Expand Down
55 changes: 42 additions & 13 deletions src/com/widdindustries/tempo.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
(ns
com.widdindustries.tempo
""
(:refer-clojure :exclude [min max > < >= <= >> <<])
(:require
[com.widdindustries.tempo.cljs-protocols :as cljs-protocols]
Expand All @@ -9,7 +8,11 @@
[com.widdindustries.tempo.clock :as clock]
[goog.object]))

(defn extend-all-cljs-protocols [] (cljs-protocols/extend-all))
(defn
extend-all-cljs-protocols
"in cljs envs, this makes `=`, `compare` and `hash` work on the value of Temporal entities.\r\n It is optional, so that if this behaviour is not required, the resulting build size can be reduced. \r\n "
[]
(cljs-protocols/extend-all))

(defn legacydate? [v] (instance? js/Date v))

Expand All @@ -33,24 +36,29 @@

(defn zdt? [v] (instance? entities/zdt v))

(defn
clock-system-default-zone
"a ticking clock having the ambient zone. "
[]
js/Temporal.Now)

(defn
clock-fixed
[instant ^String zone-str]
(clock/clock (constantly instant) zone-str))
"create a stopped clock"
([^ZonedDateTime zdt]
(clock/clock (constantly (.toInstant zdt)) (.-timeZoneId zdt)))
([^Instant instant ^String zone-str]
(clock/clock (constantly instant) zone-str)))

(defn
clock-with-zone
"ticking clock in given timezone_id"
[^String timezone_id]
(clock/clock js/Temporal.Now.instant timezone_id))

(defn
clock-system-default-zone
"a ticking clock having the ambient zone. "
[]
js/Temporal.Now)

(defn
clock-offset-millis
"offset an existing clock by offset-millis"
[clock offset-millis]
(clock/clock
(fn
Expand All @@ -70,7 +78,7 @@
[arg & args]
(assert (every? some? (cons arg args)))
(reduce
(fn* [p1__112295# p2__112296#] (greater p1__112295# p2__112296#))
(fn* [p1__36695# p2__36696#] (greater p1__36695# p2__36696#))
arg
args))

Expand All @@ -82,7 +90,7 @@
[arg & args]
(assert (every? some? (cons arg args)))
(reduce
(fn* [p1__112297# p2__112298#] (lesser p1__112297# p2__112298#))
(fn* [p1__36697# p2__36698#] (lesser p1__36697# p2__36698#))
arg
args))

Expand Down Expand Up @@ -173,7 +181,22 @@
(not (or (monthday? temporal) (yearmonth? temporal))))
(throw
(ex-info
"shifting by years or months yields odd results depending on input. intead shift a year-month, then set non-yearmonth parts"
"see guardrails section at https://github.com/henryw374/tempo?tab=readme-ov-file#guardrails"
{}))))

(defn
throw-if-months-or-years-in-amount
[temporal temporal-amount]
(when
(and
*block-non-commutative-operations*
(not (or (monthday? temporal) (yearmonth? temporal)))
(or
(not (zero? (.-years ^js temporal-amount)))
(not (zero? (.-months ^js temporal-amount)))))
(throw
(ex-info
"see guardrails section at https://github.com/henryw374/tempo?tab=readme-ov-file#guardrails"
{}))))

(defn
Expand All @@ -197,12 +220,18 @@

(defn
>>
([temporal temporal-amount]
(throw-if-months-or-years-in-amount temporal temporal-amount)
(.add ^js temporal temporal-amount))
([temporal amount temporal-property]
(throw-if-set-months-or-years temporal temporal-property)
(.add ^js temporal (js-obj (str temporal-property "s") amount))))

(defn
<<
([temporal temporal-amount]
(throw-if-months-or-years-in-amount temporal temporal-amount)
(.subtract ^js temporal temporal-amount))
([temporal amount temporal-property]
(throw-if-set-months-or-years temporal temporal-property)
(.subtract ^js temporal (js-obj (str temporal-property "s") amount))))
Expand Down
6 changes: 3 additions & 3 deletions src/com/widdindustries/tempo/duration_alpha.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"
#?(:clj (:import [java.time Duration Period])))

#?(:clj (set! *default-data-reader-fn* tagged-literal))
;#?(:clj (set! *default-data-reader-fn* tagged-literal))

(defn duration-parse [p]
#?(:cljs (js/Temporal.Duration.from p)
Expand All @@ -16,13 +16,13 @@
#?(:cljs (js/Temporal.Duration.from p)
:clj (. Period parse p)))

(defn duration->negated [d]
#_(defn duration->negated [d]
#?(:cljs (.negated ^js d)
:clj (.negated ^Duration d)))

;(def duraion-zero (duration-parse "PT0S"))

(defn duration-negative? [d]
#_(defn duration-negative? [d]
#?(:cljay(.isNegative ^Duration d)
:cljs (neg? (.-sign ^js d))))

Expand Down
18 changes: 11 additions & 7 deletions test/com/widdindustries/tempo_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
(deftest equals-hash-compare-duration
(let [make-middle #(d/duration-parse "PT1S")
middle (make-middle)
smallest (d/duration->negated (d/duration-parse "PT1S"))
smallest (d/duration-parse "PT0S")
largest (d/duration-parse "PT2S")]
(is (not= middle smallest))
(is (= middle (make-middle)))
Expand Down Expand Up @@ -138,11 +138,11 @@
(deftest shift
;todo - generate for combinations of duration/period and entity
(let [a-date (t/date-now (t/clock-system-default-zone))
;period (d/period-parse "P3D")
plus3 (t/>> a-date 3 t/days-property)]
(is (= a-date (t/<< plus3 3 t/days-property)))
;todo - also compare >=, > etc not=, hash not=
))
period (d/period-parse "P3D")]
(is (= a-date (-> (t/>> a-date 3 t/days-property)
(t/<< 3 t/days-property))))
(is (= a-date (-> (t/>> a-date period)
(t/<< period))))))

(deftest prop-test
(let [combos [[t/instant-now [t/nanoseconds-property t/microseconds-property t/milliseconds-property
Expand Down Expand Up @@ -283,8 +283,12 @@

(deftest guardrails-test
(is (thrown? #?(:clj Throwable :cljs js/Error) (t/>> (t/date-parse "2020-02-02") 1 t/years-property)))
(is (thrown? #?(:clj Throwable :cljs js/Error) (t/>> (t/date-parse "2020-02-02") (d/period-parse "P1Y"))))
(binding [t/*block-non-commutative-operations* false]
(is (t/>> (t/date-parse "2020-02-02") 1 t/years-property))))
(is (t/>> (t/date-parse "2020-02-02") 1 t/years-property))
(is (t/>> (t/date-parse "2020-02-02") (d/period-parse "P1Y")))

))

(deftest comparison-test
(doseq [{:keys [startf endf]} [
Expand Down

0 comments on commit a5fefef

Please sign in to comment.