Skip to content

Commit b4f31b5

Browse files
committed
Add tests for checking active_at for daily schedules
1 parent 0988731 commit b4f31b5

1 file changed

Lines changed: 218 additions & 5 deletions

File tree

crates/hotfix/src/session_schedule.rs

Lines changed: 218 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ impl SessionSchedule {
3535
SessionSchedule::Daily {
3636
start_time,
3737
end_time,
38-
timezone: _,
39-
} => Self::check_daily_schedule(datetime, start_time, end_time),
38+
timezone,
39+
} => {
40+
let adjusted_datetime = datetime.with_timezone(timezone);
41+
Self::check_daily_schedule(&adjusted_datetime, start_time, end_time)
42+
}
4043
SessionSchedule::Weekdays {
4144
weekdays,
4245
start_time,
@@ -48,14 +51,14 @@ impl SessionSchedule {
4851
}
4952

5053
fn check_daily_schedule(
51-
datetime: &DateTime<Utc>,
54+
datetime: &DateTime<Tz>,
5255
start_time: &NaiveTime,
5356
end_time: &NaiveTime,
5457
) -> bool {
5558
if start_time < end_time {
56-
&datetime.time() >= start_time && &datetime.time() <= end_time
59+
&datetime.time() >= start_time && &datetime.time() < end_time
5760
} else {
58-
&datetime.time() >= start_time || &datetime.time() <= end_time
61+
&datetime.time() >= start_time || &datetime.time() < end_time
5962
}
6063
}
6164

@@ -184,6 +187,216 @@ mod tests {
184187
use super::*;
185188
use chrono::{NaiveTime, Weekday};
186189

190+
#[test]
191+
fn test_active_at_non_stop_schedule() {
192+
// non-stop schedules are always active
193+
let schedule = SessionSchedule::NonStop;
194+
assert!(schedule.is_active_at(&Utc::now()))
195+
}
196+
197+
#[test]
198+
fn test_active_at_daily_schedule_utc() {
199+
let schedule = SessionSchedule::Daily {
200+
start_time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
201+
end_time: NaiveTime::from_hms_opt(17, 0, 0).unwrap(),
202+
timezone: Tz::UTC,
203+
};
204+
205+
// just before start time (8:59:59)
206+
let before_start = DateTime::from_naive_utc_and_offset(
207+
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
208+
.unwrap()
209+
.and_hms_opt(8, 59, 59)
210+
.unwrap(),
211+
Utc,
212+
);
213+
assert!(!schedule.is_active_at(&before_start));
214+
215+
// just after start time (9:00:01)
216+
let after_start = DateTime::from_naive_utc_and_offset(
217+
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
218+
.unwrap()
219+
.and_hms_opt(9, 0, 1)
220+
.unwrap(),
221+
Utc,
222+
);
223+
assert!(schedule.is_active_at(&after_start));
224+
225+
// in the middle (13:00:00)
226+
let middle = DateTime::from_naive_utc_and_offset(
227+
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
228+
.unwrap()
229+
.and_hms_opt(13, 0, 0)
230+
.unwrap(),
231+
Utc,
232+
);
233+
assert!(schedule.is_active_at(&middle));
234+
235+
// just before end time (16:59:59)
236+
let before_end = DateTime::from_naive_utc_and_offset(
237+
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
238+
.unwrap()
239+
.and_hms_opt(16, 59, 59)
240+
.unwrap(),
241+
Utc,
242+
);
243+
assert!(schedule.is_active_at(&before_end));
244+
245+
// at end time (17:00:00) - we expect false at exactly the end time (non-inclusive)
246+
let at_end = DateTime::from_naive_utc_and_offset(
247+
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
248+
.unwrap()
249+
.and_hms_opt(17, 0, 0)
250+
.unwrap(),
251+
Utc,
252+
);
253+
assert!(!schedule.is_active_at(&at_end));
254+
255+
// after end time (17:00:01)
256+
let after_end = DateTime::from_naive_utc_and_offset(
257+
chrono::NaiveDate::from_ymd_opt(2024, 1, 1)
258+
.unwrap()
259+
.and_hms_opt(17, 0, 1)
260+
.unwrap(),
261+
Utc,
262+
);
263+
assert!(!schedule.is_active_at(&after_end));
264+
}
265+
266+
#[test]
267+
fn test_active_at_daily_schedule_london() {
268+
// we'll use 27/06/2025 as the date
269+
// London is an hour ahead of UTC
270+
let schedule = SessionSchedule::Daily {
271+
start_time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
272+
end_time: NaiveTime::from_hms_opt(17, 0, 0).unwrap(),
273+
timezone: Tz::Europe__London,
274+
};
275+
276+
let before_start = DateTime::from_naive_utc_and_offset(
277+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
278+
.unwrap()
279+
.and_hms_opt(7, 59, 59)
280+
.unwrap(),
281+
Utc,
282+
);
283+
assert!(!schedule.is_active_at(&before_start));
284+
285+
// 8AM UTC is 9AM London time, so already in session
286+
let at_start = DateTime::from_naive_utc_and_offset(
287+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
288+
.unwrap()
289+
.and_hms_opt(8, 0, 0)
290+
.unwrap(),
291+
Utc,
292+
);
293+
assert!(schedule.is_active_at(&at_start));
294+
295+
// 4PM UTC is 5PM London time, so already out of session
296+
let at_end = DateTime::from_naive_utc_and_offset(
297+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
298+
.unwrap()
299+
.and_hms_opt(16, 0, 0)
300+
.unwrap(),
301+
Utc,
302+
);
303+
assert!(!schedule.is_active_at(&at_end));
304+
}
305+
306+
#[test]
307+
fn test_active_at_daily_schedule_london_end_before_start() {
308+
// we'll use 27/06/2025 as the date
309+
// London is an hour ahead of UTC
310+
let schedule_1 = SessionSchedule::Daily {
311+
start_time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
312+
end_time: NaiveTime::from_hms_opt(2, 0, 0).unwrap(),
313+
timezone: Tz::Europe__London,
314+
};
315+
316+
let before_start = DateTime::from_naive_utc_and_offset(
317+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
318+
.unwrap()
319+
.and_hms_opt(7, 59, 59)
320+
.unwrap(),
321+
Utc,
322+
);
323+
assert!(!schedule_1.is_active_at(&before_start));
324+
325+
let at_start = DateTime::from_naive_utc_and_offset(
326+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
327+
.unwrap()
328+
.and_hms_opt(8, 0, 0)
329+
.unwrap(),
330+
Utc,
331+
);
332+
assert!(schedule_1.is_active_at(&at_start));
333+
334+
let before_end = DateTime::from_naive_utc_and_offset(
335+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
336+
.unwrap()
337+
.and_hms_opt(0, 59, 59)
338+
.unwrap(),
339+
Utc,
340+
);
341+
assert!(schedule_1.is_active_at(&before_end));
342+
343+
let at_end = DateTime::from_naive_utc_and_offset(
344+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
345+
.unwrap()
346+
.and_hms_opt(1, 0, 0)
347+
.unwrap(),
348+
Utc,
349+
);
350+
assert!(!schedule_1.is_active_at(&at_end));
351+
}
352+
353+
#[test]
354+
fn test_active_at_daily_schedule_london_end_before_start_tz_crossing_midnight() {
355+
// we'll use 27/06/2025 as the date
356+
// London is an hour ahead of UTC
357+
let schedule_1 = SessionSchedule::Daily {
358+
start_time: NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
359+
end_time: NaiveTime::from_hms_opt(0, 30, 0).unwrap(),
360+
timezone: Tz::Europe__London,
361+
};
362+
363+
let before_start = DateTime::from_naive_utc_and_offset(
364+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
365+
.unwrap()
366+
.and_hms_opt(7, 59, 59)
367+
.unwrap(),
368+
Utc,
369+
);
370+
assert!(!schedule_1.is_active_at(&before_start));
371+
372+
let at_start = DateTime::from_naive_utc_and_offset(
373+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
374+
.unwrap()
375+
.and_hms_opt(8, 0, 0)
376+
.unwrap(),
377+
Utc,
378+
);
379+
assert!(schedule_1.is_active_at(&at_start));
380+
381+
let before_end = DateTime::from_naive_utc_and_offset(
382+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
383+
.unwrap()
384+
.and_hms_opt(23, 29, 59)
385+
.unwrap(),
386+
Utc,
387+
);
388+
assert!(schedule_1.is_active_at(&before_end));
389+
390+
let at_end = DateTime::from_naive_utc_and_offset(
391+
chrono::NaiveDate::from_ymd_opt(2025, 6, 27)
392+
.unwrap()
393+
.and_hms_opt(23, 30, 0)
394+
.unwrap(),
395+
Utc,
396+
);
397+
assert!(!schedule_1.is_active_at(&at_end));
398+
}
399+
187400
#[test]
188401
fn test_into_non_stop_no_config() {
189402
let config = ScheduleConfig {

0 commit comments

Comments
 (0)