Skip to content

Commit 45842a4

Browse files
committed
fix: take event duration into account for series range
1 parent e034fbb commit 45842a4

2 files changed

Lines changed: 79 additions & 7 deletions

File tree

src/series.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ where
128128
}
129129

130130
/// Returns the range from series start (inclusive) to series end (exclusive).
131+
///
132+
/// If the series has a non-zero event duration configured, the returned range will end at
133+
/// `initial_end - event_duration`.
131134
pub fn range(&self) -> &Range<DateTime> {
132135
&self.range
133136
}
@@ -142,7 +145,10 @@ where
142145
/// Returns the `DateTime` at which the series ends (exclusive).
143146
///
144147
/// Don't confuse this with the time of the last event in the series. It is merely an upper
145-
/// bound until which the series will yield events.
148+
/// bound until after which the series will stop yielding events.
149+
///
150+
/// If the series has a non-zero event duration configured, this will return `initial_end -
151+
/// event_duration`.
146152
pub fn end(&self) -> DateTime {
147153
self.range.end
148154
}
@@ -578,6 +584,9 @@ where
578584
/// If `.event_duration()` is not called with a custom value, events will not have an end
579585
/// datetime.
580586
///
587+
/// The event duration may be longer than the time between the dates produces by the recurrence
588+
/// pattern, in which case events will overlap.
589+
///
581590
/// # Example
582591
///
583592
/// ```
@@ -625,8 +634,9 @@ where
625634
///
626635
/// # Errors
627636
///
628-
/// Returns an `Error` if the configured `end` is less than or equal to `start`, or if the
629-
/// configured `event_duration` is negative.
637+
/// Returns an `Error` if the configured `end` is less than or equal to `start`, if the
638+
/// configured `event_duration` is negative, or if the `event_duration` greater or equal to the
639+
/// range (`start..end`) of the series.
630640
///
631641
/// # Example
632642
///
@@ -642,15 +652,20 @@ where
642652
/// # Ok::<(), Box<dyn core::error::Error>>(())
643653
/// ```
644654
pub fn build(self) -> Result<Series<P>, Error> {
645-
let range = try_simplify_range(self.bounds)?;
646-
if range.start >= range.end {
647-
return Err(Error::from(ErrorKind::InvalidBounds));
648-
}
655+
let mut range = try_simplify_range(self.bounds)?;
649656

650657
if self.event_duration.is_negative() {
651658
return Err(Error::from(ErrorKind::InvalidEventDuration));
652659
}
653660

661+
if self.event_duration.is_positive() {
662+
range.end = range.end.checked_sub(self.event_duration)?;
663+
}
664+
665+
if range.start >= range.end {
666+
return Err(Error::from(ErrorKind::InvalidBounds));
667+
}
668+
654669
Ok(Series {
655670
pattern: self.pattern,
656671
range,

tests/series_test.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,60 @@ fn series_get_closest_event() {
285285
Some(Event::at(datetime(9999, 12, 31, 23, 0, 0, 0)))
286286
);
287287
}
288+
289+
#[test]
290+
fn series_overlapping_last_event() {
291+
let start = date(2025, 1, 1).at(0, 0, 0, 0);
292+
let end = date(2025, 2, 1).at(0, 0, 0, 0);
293+
let series = Series::new(start..end, daily(1))
294+
.with()
295+
.event_duration(50.hours())
296+
.build()
297+
.unwrap();
298+
299+
assert_eq!(series.end(), date(2025, 1, 29).at(22, 0, 0, 0));
300+
assert_eq!(
301+
series.last_event(),
302+
Some(
303+
Event::new(
304+
date(2025, 1, 29).at(0, 0, 0, 0),
305+
date(2025, 1, 31).at(2, 0, 0, 0)
306+
)
307+
.unwrap()
308+
)
309+
);
310+
}
311+
312+
#[test]
313+
fn series_event_durations() {
314+
let start = date(2025, 1, 1).at(0, 0, 0, 0);
315+
let end = date(2025, 2, 1).at(0, 0, 0, 0);
316+
let series = Series::new(start..end, daily(1));
317+
318+
assert!(
319+
series
320+
.clone()
321+
.with()
322+
.event_duration(30.days().hours(23).minutes(59).seconds(59).nanoseconds(999))
323+
.build()
324+
.is_ok()
325+
);
326+
327+
assert!(
328+
series
329+
.clone()
330+
.with()
331+
.event_duration(1.month())
332+
.build()
333+
.is_err()
334+
);
335+
336+
assert!(
337+
series
338+
.clone()
339+
.with()
340+
.event_duration(1.year())
341+
.build()
342+
.is_err()
343+
);
344+
}

0 commit comments

Comments
 (0)