Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions src/tick/alpha/interval.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -706,8 +706,35 @@
;; Bound by given interval, last will become a remainder.
(map (juxt identity #(t/min (p/forward-duration % period) (t/end ival))))))

(defn divide-by-divisor [ival divisor]
(divide-by-duration ival (cljc.java-time.duration/divided-by (t/duration ival) divisor)))
(defn divide-by-divisor
"Divides the interval specified by `ival` into `divisor` spaced
intervals. The `divisor` must be a positive integer. The calculated
subintervals are exact down to the nearest nanosecond."
[ival divisor]
(assert (pos? divisor))
(assert (integer? divisor))
(let [total-nanos (t/nanos (t/duration ival))
original-beginning (:tick/beginning ival)]
;; Note the algorithm here. We do not calculate a constant
;; duration for each sub-interval. That ends up truncating
;; fractional nanoseconds which can cause us problems with the
;; last interval if we just add the durations togther n
;; times. Instead, we calculate the total duration of the original
;; interval in nanoseconds and compute the ending point of each
;; subinterval as i/n * total duration. The duration of each
;; sub-interval may differ by plus/minus one nanosecond, but the
;; ending point is exactly the end of the original interval (i.e.,
;; n/n * total duration = 1 * total duration = total duration).
(loop [i 1
sub-intervals []
beginning original-beginning]
(let [duration-to-end (t/new-duration (/ (* i total-nanos) divisor) :nanos)
end (t/>> original-beginning duration-to-end)]
(if (= i divisor)
(clojure.core/conj sub-intervals [beginning end])
(recur (inc i)
(clojure.core/conj sub-intervals [beginning end])
end))))))

(defprotocol IDivisibleInterval
(divide-interval [divisor ival] "Divide an interval by a given divisor"))
Expand Down
6 changes: 5 additions & 1 deletion test/tick/alpha/interval_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,11 @@
(is (= 2 (count (ti/divide-by t/year-month (ti/bounds (t/date "2017-09-10") (t/date "2017-10-10"))))))
(is (= 3 (count (ti/divide-by t/year (ti/bounds (t/date-time "2017-09-10T12:00") (t/year "2019"))))))
(is (= 3 (count (ti/divide-by t/year (ti/bounds (t/date-time "2017-09-10T12:00") (t/year-month "2019-02"))))))
(is (= 24 (count (ti/divide-by (t/new-duration 1 :hours) (t/date "2017-09-10"))))))
(is (= 24 (count (ti/divide-by (t/new-duration 1 :hours) (t/date "2017-09-10")))))
;; Issue #203
(is (= 17 (count (ti/divide-by 17
(ti/new-interval #time/zoned-date-time "2025-07-09T09:33-04:00"
#time/zoned-date-time "2025-07-09T14:30-04:00"))))))

;; TODO: Divide by duration

Expand Down