| 85 | 85 | | |
| 86 | 86 | | /// Whether it fires in the minute starting at `ms` since the epoch, UTC. |
| 87 | 87 | | pub fn fires_at(&self, ms: u64) -> bool { |
| 88 | + | self.minutes[(ms / 60_000 % 60) as usize] && self.hour_and_date_at(ms) |
| 89 | + | } |
| 90 | + | |
| 91 | + | /// Whether the minute starting at `ms` is in its hours, days and |
| 92 | + | /// months, whatever its minute field says. |
| 93 | + | fn hour_and_date_at(&self, ms: u64) -> bool { |
| 88 | 94 | | let minutes_total = ms / 60_000; |
| 89 | | − | let minute = (minutes_total % 60) as usize; |
| 90 | 95 | | let hour = (minutes_total / 60 % 24) as usize; |
| 91 | 96 | | let days_since_epoch = (minutes_total / 60 / 24) as i64; |
| 92 | 97 | | // 1970-01-01 was a Thursday. |
| ⋯ |
| 98 | 103 | | (true, true) => day_ok || weekday_ok, |
| 99 | 104 | | _ => day_ok && weekday_ok, |
| 100 | 105 | | }; |
| 101 | | − | self.minutes[minute] && self.hours[hour] && self.months[month as usize] && date_ok |
| 106 | + | self.hours[hour] && self.months[month as usize] && date_ok |
| 102 | 107 | | } |
| 103 | 108 | | |
| 104 | 109 | | /// Whether its minutes come closer together than |
| ⋯ |
| 113 | 118 | | /// Whether a workflow on this schedule runs in the minute starting at |
| 114 | 119 | | /// `ms`. A schedule no more frequent than every |
| 115 | 120 | | /// [`MIN_INTERVAL_MINUTES`] runs when it fires. A more frequent one |
| 116 | | − | /// runs on the five-minute marks, at each one it fired at or since the |
| 117 | | − | /// last: at most every five minutes. |
| 121 | + | /// runs on the five-minute marks: at a mark that is itself in its |
| 122 | + | /// hours, days and months, when it fired in the five minutes the mark |
| 123 | + | /// ends. At most every five minutes, and never outside its own hours. |
| 118 | 124 | | pub fn runs_at(&self, ms: u64) -> bool { |
| 119 | 125 | | if !self.too_frequent() { |
| 120 | 126 | | return self.fires_at(ms); |
| 121 | 127 | | } |
| 122 | 128 | | let minute = ms / 60_000; |
| 123 | 129 | | minute % MIN_INTERVAL_MINUTES == 0 |
| 130 | + | && self.hour_and_date_at(ms) |
| 124 | 131 | | && (0..MIN_INTERVAL_MINUTES).any(|back| minute >= back && self.fires_at((minute - back) * 60_000)) |
| 125 | 132 | | } |
| 126 | 133 | | } |
| ⋯ |
| 210 | 217 | | // Close across the hour counts too. |
| 211 | 218 | | assert!(Schedule::parse("2,58 * * * *").unwrap().too_frequent()); |
| 212 | 219 | | assert!(!Schedule::parse("0 9 * * mon").unwrap().too_frequent()); |
| 213 | | − | // Every minute of one hour: the marks of that hour, and the one |
| 214 | | − | // that closes it. |
| 220 | + | // Every minute of one hour: its own marks, 09:00 to 09:55, and |
| 221 | + | // nothing after. |
| 215 | 222 | | let nine = Schedule::parse("* 9 * * *").unwrap(); |
| 216 | | − | assert!(nine.runs_at(at(MONDAY, 9, 0))); |
| 217 | | − | assert!(!nine.runs_at(at(MONDAY, 9, 1))); |
| 218 | | − | assert!(nine.runs_at(at(MONDAY, 10, 0))); |
| 219 | | − | assert!(!nine.runs_at(at(MONDAY, 10, 5))); |
| 223 | + | let day: Vec<(u64, u64)> = |
| 224 | + | (0..24 * 60).filter(|&m| nine.runs_at(at(MONDAY, m / 60, m % 60))).map(|m| (m / 60, m % 60)).collect(); |
| 225 | + | assert_eq!(day, (0..12).map(|i| (9, i * 5)).collect::<Vec<_>>()); |
| 226 | + | assert!(!nine.runs_at(at(MONDAY, 10, 0))); |
| 227 | + | // Every minute of Mondays: the last run is 23:55, none on Tuesday. |
| 228 | + | let mondays = Schedule::parse("*/1 * * * 1").unwrap(); |
| 229 | + | assert!(mondays.runs_at(at(MONDAY, 0, 0))); |
| 230 | + | assert!(mondays.runs_at(at(MONDAY, 23, 55))); |
| 231 | + | assert!(!mondays.runs_at(at(MONDAY + 1, 0, 0))); |
| 232 | + | assert!(!(0..24 * 60).any(|m| mondays.runs_at(at(MONDAY + 1, m / 60, m % 60)))); |
| 233 | + | // Close minutes: one run for each window they fall in, at its mark. |
| 234 | + | let close = Schedule::parse("0,3 * * * *").unwrap(); |
| 235 | + | let hour: Vec<u64> = (0..60).filter(|&m| close.runs_at(at(MONDAY, 3, m))).collect(); |
| 236 | + | assert_eq!(hour, [0, 5]); |
| 237 | + | assert!(close.runs_at(at(MONDAY, 4, 0))); |
| 220 | 238 | | } |
| 221 | 239 | | |
| 222 | 240 | | #[test] |