Skip to content

Commit bd2eefd

Browse files
authored
Fix tick.alpha.interval/divide-by (#206)
1 parent b96d4c4 commit bd2eefd

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

src/tick/alpha/interval.cljc

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,35 @@
706706
;; Bound by given interval, last will become a remainder.
707707
(map (juxt identity #(t/min (p/forward-duration % period) (t/end ival))))))
708708

709-
(defn divide-by-divisor [ival divisor]
710-
(divide-by-duration ival (cljc.java-time.duration/divided-by (t/duration ival) divisor)))
709+
(defn divide-by-divisor
710+
"Divides the interval specified by `ival` into `divisor` spaced
711+
intervals. The `divisor` must be a positive integer. The calculated
712+
subintervals are exact down to the nearest nanosecond."
713+
[ival divisor]
714+
(assert (pos? divisor))
715+
(assert (integer? divisor))
716+
(let [total-nanos (t/nanos (t/duration ival))
717+
original-beginning (:tick/beginning ival)]
718+
;; Note the algorithm here. We do not calculate a constant
719+
;; duration for each sub-interval. That ends up truncating
720+
;; fractional nanoseconds which can cause us problems with the
721+
;; last interval if we just add the durations togther n
722+
;; times. Instead, we calculate the total duration of the original
723+
;; interval in nanoseconds and compute the ending point of each
724+
;; subinterval as i/n * total duration. The duration of each
725+
;; sub-interval may differ by plus/minus one nanosecond, but the
726+
;; ending point is exactly the end of the original interval (i.e.,
727+
;; n/n * total duration = 1 * total duration = total duration).
728+
(loop [i 1
729+
sub-intervals []
730+
beginning original-beginning]
731+
(let [duration-to-end (t/new-duration (/ (* i total-nanos) divisor) :nanos)
732+
end (t/>> original-beginning duration-to-end)]
733+
(if (= i divisor)
734+
(clojure.core/conj sub-intervals [beginning end])
735+
(recur (inc i)
736+
(clojure.core/conj sub-intervals [beginning end])
737+
end))))))
711738

712739
(defprotocol IDivisibleInterval
713740
(divide-interval [divisor ival] "Divide an interval by a given divisor"))

test/tick/alpha/interval_test.cljc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,11 @@
717717
(is (= 2 (count (ti/divide-by t/year-month (ti/bounds (t/date "2017-09-10") (t/date "2017-10-10"))))))
718718
(is (= 3 (count (ti/divide-by t/year (ti/bounds (t/date-time "2017-09-10T12:00") (t/year "2019"))))))
719719
(is (= 3 (count (ti/divide-by t/year (ti/bounds (t/date-time "2017-09-10T12:00") (t/year-month "2019-02"))))))
720-
(is (= 24 (count (ti/divide-by (t/new-duration 1 :hours) (t/date "2017-09-10"))))))
720+
(is (= 24 (count (ti/divide-by (t/new-duration 1 :hours) (t/date "2017-09-10")))))
721+
;; Issue #203
722+
(is (= 17 (count (ti/divide-by 17
723+
(ti/new-interval #time/zoned-date-time "2025-07-09T09:33-04:00"
724+
#time/zoned-date-time "2025-07-09T14:30-04:00"))))))
721725

722726
;; TODO: Divide by duration
723727

0 commit comments

Comments
 (0)