diff --git a/backend/src/tasks/lights.rs b/backend/src/tasks/lights.rs index 0c2a4ab..78affce 100644 --- a/backend/src/tasks/lights.rs +++ b/backend/src/tasks/lights.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use chrono::{Datelike, Local, NaiveTime, Weekday}; +use chrono::{DateTime, Datelike, Local, NaiveTime, Weekday}; use common::{ClientMessage, ServerMessage}; use lighter_lib::{BulbColor, BulbId}; use lighter_manager::manager::{BulbCommand, BulbManager, BulbSelector}; @@ -143,25 +143,11 @@ async fn wake_task( time: NaiveTime, ) { let now = Local::now(); - let day_num = day.num_days_from_monday(); - let now_day = now.weekday(); - let now_day_num = now_day.num_days_from_monday(); - - let mut alarm = now; - if day_num >= now_day_num { - // next alarm is this week - alarm += chrono::Duration::days((day_num - now_day_num).into()); - alarm = alarm.date().and_time(time).unwrap(); - } else { - // next alarm is next week - alarm += chrono::Duration::weeks(1); - alarm -= chrono::Duration::days((now_day_num - day_num).into()); - alarm = alarm.date().and_time(time).unwrap(); - } + let mut alarm = next_alarm(now, day, time); loop { info!("sleeping until {alarm}"); - sleep((alarm - Local::now()).to_std().unwrap()).await; + sleep((alarm - now).to_std().unwrap()).await; alarm += chrono::Duration::weeks(1); // slowly turn up brightness of bulb @@ -189,6 +175,21 @@ async fn wake_task( } } +/// Get the next alarm, from a weekday+time schedule. +fn next_alarm(now: DateTime, day: Weekday, time: NaiveTime) -> DateTime { + let day_of_alarm = day.num_days_from_monday() as i64; + let day_now = now.weekday().num_days_from_monday() as i64; + + let alarm = now + chrono::Duration::days(day_of_alarm - day_now); + let mut alarm = alarm.date().and_time(time).unwrap(); + + if alarm <= now { + alarm += chrono::Duration::weeks(1); + } + + alarm +} + /// Wait until we receive a client request that mutates the given bulb async fn wait_for_bulb_command( bulb_id: &BulbId, @@ -210,3 +211,31 @@ async fn wait_for_bulb_command( } } } + +#[cfg(test)] +mod test { + use chrono::{offset::TimeZone, Local, NaiveTime, Weekday}; + + use super::next_alarm; + + #[test] + fn test_alarm_date() { + const FMT: &str = "%Y-%m-%d %H:%M"; + let now = Local.datetime_from_str("2022-10-18 15:30", FMT).unwrap(); + let test_values = [ + (Weekday::Tue, (16, 30), "2022-10-18 16:30"), + (Weekday::Tue, (14, 30), "2022-10-25 14:30"), + (Weekday::Wed, (15, 30), "2022-10-19 15:30"), + (Weekday::Mon, (15, 30), "2022-10-24 15:30"), + ]; + + for (day, (hour, min), expected) in test_values { + let expected = Local.datetime_from_str(expected, FMT).unwrap(); + + assert_eq!( + next_alarm(now, day, NaiveTime::from_hms(hour, min, 0)), + expected + ); + } + } +}