1use crate::compute::Composition;
2use crate::data::{Hour, Minuter, MonthDay, Second, WeekDay};
3use crate::traits::{FromData, ConfigOperator};
4use anyhow::{bail, Result};
5use chrono::{Datelike, Duration, Local, NaiveDateTime, Timelike};
6use log::debug;
7use std::fmt::{Debug, Formatter};
8use std::ops::{Add, Bound, RangeBounds, Sub};
9
10#[derive(Debug, Clone)]
12pub struct TimerConf {
13 pub(crate) days: Days,
14 pub(crate) hours: Hours,
15 pub(crate) minuters: Minuters,
16 pub(crate) seconds: Seconds,
17}
18
19impl TimerConf {
20
21 pub fn datetimes(&self, range: impl RangeBounds<NaiveDateTime>) -> Result<Vec<NaiveDateTime>> {
23 let mut start = match range.start_bound() {
25 Bound::Unbounded => bail!("不支持该模式"),
26 Bound::Included(first) => first.sub(Duration::seconds(1)),
27 Bound::Excluded(first) => first.clone(),
28 };
29 let end = match range.end_bound() {
30 Bound::Unbounded => bail!("不支持该模式"),
31 Bound::Included(end) => end.clone(),
32 Bound::Excluded(end) => end.sub(Duration::seconds(1)),
33 };
34 if start >= end {
35 bail!("起始-结束日期配置错误")
36 }
37 let mut date_times = Vec::new();
38 while start <= end {
39 let next = self.next_with_time(start);
40 if next <= end {
41 date_times.push(next);
42 start = next;
43 } else {
44 break;
45 }
46 }
47 Ok(date_times)
48 }
49 pub fn next_with_time(&self, now: NaiveDateTime) -> NaiveDateTime {
51 let now = now.add(Duration::seconds(1));
52 let mut composition = Composition::from(
53 now,
54 self.days.clone(),
55 self.hours.clone(),
56 self.minuters.clone(),
57 self.seconds.clone(),
58 );
59 debug!("Composition: {:?}", composition);
60 let next = composition.next();
61 next
62 }
63 pub fn next(&self) -> u64 {
65 let now_local = Local::now().naive_local();
66 let next_local = self.next_with_time(now_local);
67 let times = (next_local.timestamp() - now_local.timestamp()) as u64;
68 debug!(
69 "now : {}-{:02}-{:02} {:02}:{:02}:{:02}",
70 now_local.year(),
71 now_local.month(),
72 now_local.day(),
73 now_local.hour(),
74 now_local.minute(),
75 now_local.second()
76 );
77 debug!(
78 "next: {}-{:02}-{:02} {:02}:{:02}:{:02}",
79 next_local.year(),
80 next_local.month(),
81 next_local.day(),
82 next_local.hour(),
83 next_local.minute(),
84 next_local.second()
85 );
86 times
87 }
88}
89#[derive(Debug, Clone)]
90pub enum Days {
91 MonthDays(MonthDays),
92 WeekDays(WeekDays),
93 MonthAndWeekDays(MonthDays, WeekDays)
94}
95
96impl Days {
97 pub(crate) fn month_days(&self, week_day: WeekDay) -> MonthDays {
98 match self {
99 Days::MonthDays(month_days) => { month_days.clone()}
100 Days::WeekDays(week_days) => {week_days.to_month_days(week_day)}
101 Days::MonthAndWeekDays(month_days, week_days) => {
102 month_days.merge(&week_days.to_month_days(week_day))
103 }
104 }
105 }
106 pub(crate) fn update_month_days(self, month_days: MonthDays) -> Self {
107 match self {
108 Days::MonthDays(_) => {Self::MonthDays(month_days)}
109 Days::WeekDays(week_days) => {Self::MonthAndWeekDays(month_days, week_days)}
110 Days::MonthAndWeekDays(_, week_days) => {Self::MonthAndWeekDays(month_days, week_days)}
111 }
112 }
113 pub(crate) fn update_week_days(self, week_days: WeekDays) -> Self {
114 match self {
115 Days::MonthDays(month_days) => {Self::MonthAndWeekDays(month_days, week_days)}
116 Days::WeekDays(_) => {Self::WeekDays(week_days)}
117 Days::MonthAndWeekDays(month_days, _) => {Self::MonthAndWeekDays(month_days, week_days)}
118 }
119 }
120 }
128
129
130#[derive(Clone)]
132pub struct MonthDays(u64);
133#[derive(Clone)]
135pub struct WeekDays(u64);
136#[derive(Clone)]
138pub struct Hours(u64);
139#[derive(Clone, Eq, PartialEq)]
141pub struct Minuters(u64);
142#[derive(Clone)]
144pub struct Seconds(u64);
145impl ConfigOperator for Hours {
146 const MIN: u64 = 0;
147 const MAX: u64 = 23;
148 const DEFAULT_MAX: u64 = (u32::MAX >> 8) as u64;
149 type DataTy = Hour;
150
151 fn min_val(&self) -> Self::DataTy {
152 Self::DataTy::from_data(self._min_val())
153 }
154 fn _default() -> Self {
155 Self(0)
156 }
157 fn _val(&self) -> u64 {
158 self.0
159 }
160 fn next(&self, index: Self::DataTy) -> Option<Self::DataTy> {
161 self._next(index)
162 .and_then(|x| Some(Self::DataTy::from_data(x)))
163 }
164 fn _val_mut(&mut self, val: u64) {
165 self.0 = val
166 }
167
168}
169impl ConfigOperator for Seconds {
170 const MIN: u64 = 0;
171 const MAX: u64 = 59;
172 const DEFAULT_MAX: u64 = u64::MAX >> 4;
173 type DataTy = Second;
174 fn min_val(&self) -> Self::DataTy {
175 Self::DataTy::from_data(self._min_val())
176 }
177 fn next(&self, index: Self::DataTy) -> Option<Self::DataTy> {
178 self._next(index)
179 .and_then(|x| Some(Self::DataTy::from_data(x)))
180 }
181 fn _default() -> Self {
182 Self(0)
183 }
184 fn _val(&self) -> u64 {
185 self.0
186 }
187 fn _val_mut(&mut self, val: u64) {
188 self.0 = val
189 }
190}
191impl ConfigOperator for Minuters {
192 const MIN: u64 = 0;
193 const MAX: u64 = 59;
194 const DEFAULT_MAX: u64 = u64::MAX >> 4;
195 type DataTy = Minuter;
196 fn min_val(&self) -> Self::DataTy {
197 Self::DataTy::from_data(self._min_val())
198 }
199 fn next(&self, index: Self::DataTy) -> Option<Self::DataTy> {
200 self._next(index)
201 .and_then(|x| Some(Self::DataTy::from_data(x)))
202 }
203 fn _default() -> Self {
204 Self(0)
205 }
206 fn _val(&self) -> u64 {
207 self.0
208 }
209 fn _val_mut(&mut self, val: u64) {
210 self.0 = val
211 }
212}
213
214impl ConfigOperator for MonthDays {
215 const MIN: u64 = 1;
216 const MAX: u64 = 31;
217 const DEFAULT_MAX: u64 = (u32::MAX << 1) as u64;
218 type DataTy = MonthDay;
219 fn next(&self, index: Self::DataTy) -> Option<Self::DataTy> {
220 self._next(index)
221 .and_then(|x| Some(Self::DataTy::from_data(x)))
222 }
223 fn _default() -> Self {
224 Self(0)
225 }
226 fn min_val(&self) -> Self::DataTy {
227 Self::DataTy::from_data(self._min_val())
228 }
229 fn _val(&self) -> u64 {
230 self.0
231 }
232 fn _val_mut(&mut self, val: u64) {
233 self.0 = val
234 }
235}
236
237impl Minuters {
238 pub fn every(interval: u64) -> Self {
239 if interval == 0 {
240 Self::_default()
241 } else {
242 let mut val = 0u64;
243 let mut minuters = Self::_default();
244 while val <= Self::MAX {
245 minuters = minuters.add(Minuter::from_data(val));
246 val += interval
247 }
248 minuters
249 }
250 }
251}
252
253impl ConfigOperator for WeekDays {
254 const DEFAULT_MAX: u64 = (u8::MAX << 1) as u64;
255 const MIN: u64 = 1;
256 const MAX: u64 = 7;
257
258 type DataTy = WeekDay;
259
260 fn _default() -> Self {
261 Self(0)
262 }
263
264 fn min_val(&self) -> Self::DataTy {
265 Self::DataTy::from_data(self._min_val())
266 }
267 fn next(&self, index: Self::DataTy) -> Option<Self::DataTy> {
268 self._next(index)
269 .and_then(|x| Some(Self::DataTy::from_data(x)))
270 }
271
272 fn _val(&self) -> u64 {
273 self.0
274 }
275
276 fn _val_mut(&mut self, val: u64) {
277 self.0 = val;
278 }
279}
280
281#[allow(dead_code)]
283impl WeekDays {
284 pub(crate) fn to_month_days(&self, start: WeekDay) -> MonthDays {
285 let week_unit = self.0 >> 1;
287 let days = (week_unit | week_unit << 7 | week_unit << 14 | week_unit << 21 | week_unit << 28 | week_unit << 35) >> (start as u64 - 1) << 1;
291
292 let mut month_days = MonthDays::_default();
293 month_days._val_mut(days);
294 month_days
295 }
311}
312impl Debug for Seconds {
335 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
336 if self.0 == u64::MAX >> 4 {
337 write!(f, "all seconds.")
338 } else {
339 write!(f, "seconds: {:?}.", self.to_vec())
340 }
341 }
342}
343impl Debug for Minuters {
344 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
345 if self.0 == u64::MAX >> 4 {
346 write!(f, "all minuters.")
347 } else {
348 write!(f, "minuters: {:?}.", self.to_vec())
349 }
350 }
351}
352impl Debug for Hours {
353 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
354 if self.0 == (u32::MAX >> 8) as u64 {
355 write!(f, "all hours.")
356 } else {
357 write!(f, "hours: {:?}.", self.to_vec())
358 }
359 }
360}
361impl Debug for MonthDays {
362 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
363 if self.0 == (u32::MAX << 1) as u64 {
364 write!(f, "all month days.")
365 } else {
366 write!(f, "month days: {:?}.", self.to_vec())
367 }
368 }
369}
370impl Debug for WeekDays {
371 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
372 if self.0 == (u8::MAX << 1) as u64 {
373 write!(f, "all week days.")
374 } else {
375 write!(f, "week day's array index: {:?}.", self.to_vec())
376 }
377 }
378}
379
380#[cfg(test)]
381mod test {
382 use super::{Hours, Minuters, MonthDays, ConfigOperator, Seconds, WeekDays};
383 use crate::conf::TimerConf;
384 #[allow(unused_imports)]
385 use crate::data::{DateTime, Hour::*, Minuter::*, MonthDay::*, Second::*, WeekDay::*};
386 use crate::*;
387 use anyhow::Result;
388 use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime};
389 use log::debug;
390 use std::ops::Sub;
391
392
393 pub(crate) fn datetime(
394 year: i32,
395 month: u32,
396 day: u32,
397 hour: u32,
398 min: u32,
399 second: u32,
400 ) -> NaiveDateTime {
401 NaiveDateTime::new(
402 NaiveDate::from_ymd(year, month, day),
403 NaiveTime::from_hms(hour, min, second),
404 )
405 }
406
407 #[test]
408 fn test_auto() -> anyhow::Result<()> {
409 let conf = configure_weekday(WeekDays::default_array(&[W1, W3, W5]))
411 .conf_month_days(
412 MonthDays::default_range(D5..D10)?
413 .add_range(D15..D20)?
414 .add_range(D25..D30)?,
415 )
416 .build_with_hours(Hours::default_array(&[H5, H10, H15, H20]))
417 .build_with_minuter(Minuters::default_array(&[M15, M30, M45]))
418 .build_with_second(Seconds::default_value(S0));
419
420 let mut start = datetime(2022, 7, 4, 20, 15, 0);
421 let end = datetime(2033, 8, 15, 12, 30, 45);
422
423 let datetimes = conf.datetimes(start.clone()..end)?;
425 start = start.sub(Duration::seconds(1));
426 let mut next = end;
427 for datetime in datetimes {
428 next = conf.next_with_time(start.clone());
429 assert_eq!(datetime, next, "{:?} - {:?}", start, next);
430 start = datetime;
431 }
432 assert_eq!(
433 datetime(2033, 8, 15, 10, 45, 0),
434 next,
435 "{:?} - {:?}",
436 end,
437 next
438 );
439
440 let mut start = datetime(2022, 7, 4, 20, 15, 0);
441 let end = datetime(2033, 8, 15, 15, 30, 0);
442 let datetimes = conf.datetimes(start.clone()..end.clone())?;
444 start = start.sub(Duration::seconds(1));
445 let mut next = start.clone();
446 for datetime in datetimes {
447 next = conf.next_with_time(start.clone());
448 assert_eq!(datetime, next, "{:?} - {:?}", start, next);
449 start = datetime;
450 }
451 assert_eq!(
452 datetime(2033, 8, 15, 15, 15, 0),
453 next,
454 "{:?} - {:?}",
455 end,
456 next
457 );
458 Ok(())
459 }
460 #[test]
461 fn test_auto_pre() -> anyhow::Result<()> {
462 let conf = configure_weekday(WeekDays::default_array(&[W1, W3, W5]))
464 .conf_month_days(
465 MonthDays::default_range(D5..D10)?
466 .add_range(D15..D20)?
467 .add_range(D25..D30)?,
468 )
469 .build_with_hours(Hours::default_array(&[H5, H10, H15, H20]))
470 .build_with_minuter(Minuters::default_array(&[M15, M30, M45]))
471 .build_with_second(Seconds::default_value(S0));
472
473 let start = datetime(2022, 7, 4, 22, 17, 10);
474 let end = datetime(2022, 7, 5, 12, 30, 45);
475
476 let datetimes = conf.datetimes(start.clone()..end)?;
478 debug!("{:?}", datetimes);
479 Ok(())
480 }
481
482 #[test]
484 fn test_to_month_days() {
485 let month_days0 = WeekDays::default_array(&[W1, W3, W5, W7]).to_month_days(W3);
486 assert_eq!(
487 month_days0.to_vec(),
488 vec![1, 3, 5, 6, 8, 10, 12, 13, 15, 17, 19, 20, 22, 24, 26, 27, 29, 31]
489 );
490
491 let month_days1 = WeekDays::default_array(&[W1, W3, W5]).to_month_days(W1);
492 assert_eq!(
493 month_days1.to_vec(),
494 vec![1, 3, 5, 8, 10, 12, 15, 17, 19, 22, 24, 26, 29, 31]
495 );
496 let month_days2 = month_days0.merge(&month_days1);
497 assert_eq!(
498 month_days2.to_vec(),
499 vec![1, 3, 5, 6, 8, 10, 12, 13, 15, 17, 19, 20, 22, 24, 26, 27, 29, 31]
500 );
501 }
503 #[test]
504 fn test_datetimes() -> Result<()> {
505 let some_datetimes = [
507 datetime(2020, 5, 15, 10, 30, 30),
508 datetime(2020, 5, 15, 10, 30, 45),
509 datetime(2020, 5, 15, 10, 45, 15),
510 datetime(2020, 5, 15, 10, 45, 30),
511 datetime(2020, 5, 15, 10, 45, 45),
512 datetime(2020, 5, 15, 15, 15, 15),
513 datetime(2020, 5, 15, 15, 15, 30),
514 datetime(2020, 5, 15, 15, 15, 45),
515 datetime(2020, 5, 15, 15, 30, 15),
516 datetime(2020, 5, 15, 15, 30, 30),
517 ];
518 let conf = configure_weekday(WeekDays::default_array(&[W5, W3]))
519 .conf_month_days(MonthDays::default_array(&[D5, D15, D24]))
520 .build_with_hours(Hours::default_array(&[H5, H10, H15]))
521 .build_with_minuter(Minuters::default_array(&[M15, M30, M45]))
522 .build_with_second(Seconds::default_array(&[S15, S30, S45]));
523 debug!("2020-5-15 10:30:17");
524 let datetimes =
527 conf.datetimes(datetime(2020, 5, 15, 10, 30, 17)..=datetime(2020, 5, 15, 15, 30, 30))?;
528
529 assert_eq!(datetimes.as_slice(), &some_datetimes[..]);
530
531 Ok(())
532 }
533
534 #[test]
535 fn test() -> Result<()> {
536 let conf = configure_weekday(WeekDays::default_array(&[W5, W3]))
538 .conf_month_days(MonthDays::default_array(&[D5, D15, D24]))
539 .build_with_hours(Hours::default_array(&[H5, H10, H15]))
540 .build_with_minuter(Minuters::default_array(&[M15, M30, M45]))
541 .build_with_second(Seconds::default_array(&[S15, S30, S45]));
542
543 compare(
544 &conf,
545 &[
546 datetime(2020, 5, 15, 10, 30, 30),
547 datetime(2020, 5, 15, 10, 30, 45),
548 datetime(2020, 5, 15, 10, 45, 15),
549 datetime(2020, 5, 15, 10, 45, 30),
550 datetime(2020, 5, 15, 10, 45, 45),
551 datetime(2020, 5, 15, 15, 15, 15),
552 datetime(2020, 5, 15, 15, 15, 30),
553 datetime(2020, 5, 15, 15, 15, 45),
554 datetime(2020, 5, 15, 15, 30, 15),
555 datetime(2020, 5, 15, 15, 30, 30),
556 ],
557 );
558 let dt0 = DateTime {
560 date: NaiveDate::from_ymd_opt(2022, 5, 20).unwrap(),
561 month_day: D20,
562 week_day: W5,
563 hour: H15,
564 minuter: M45,
565 second: S45,
566 };
567 {
568 let dist: DateTime = conf.next_with_time(dt0.into()).into();
569 let mut dt0_dist = dt0.clone();
570 dt0_dist.week_day = W2;
571 dt0_dist.month_day = D24;
572 dt0_dist.second = S15;
573 dt0_dist.minuter = M15;
574 dt0_dist.hour = H5;
575 dt0_dist.date = NaiveDate::from_ymd_opt(2022, 5, 24).unwrap();
576 assert_eq!(dist, dt0_dist);
577 }
578 let conf = configure_weekday(WeekDays::default_array(&[W5, W3]))
580 .conf_month_days(MonthDays::default_array(&[D5, D15, D31]))
581 .build_with_hours(Hours::default_array(&[H5, H10, H15]))
582 .build_with_minuter(Minuters::default_array(&[M15, M30, M45]))
583 .build_with_second(Seconds::default_array(&[S15, S30, S45]));
584 debug!("{:?}", conf);
585 let dt0 = DateTime {
586 date: NaiveDate::from_ymd_opt(2022, 4, 29).unwrap(),
587 month_day: D29,
588 week_day: W5,
589 hour: H15,
590 minuter: M45,
591 second: S45,
592 };
593 {
594 let dist: DateTime = conf.next_with_time(dt0.into()).into();
595 let mut dt0_dist = dt0.clone();
596 dt0_dist.week_day = W3;
597 dt0_dist.month_day = D4;
598 dt0_dist.second = S15;
599 dt0_dist.minuter = M15;
600 dt0_dist.hour = H5;
601 dt0_dist.date = NaiveDate::from_ymd_opt(2022, 5, 4).unwrap();
602 assert_eq!(dist, dt0_dist);
603 }
604 Ok(())
605 }
606
607 #[test]
608 fn test_year() -> Result<()> {
609 let conf = configure_monthday(MonthDays::default_value(D31))
611 .build_with_hours(Hours::default_array(&[H12]))
612 .build_with_minuter(Minuters::default_array(&[M30]))
613 .build_with_second(Seconds::default_array(&[S0]));
614 let dt0 = DateTime {
615 date: NaiveDate::from_ymd_opt(2021, 12, 31).unwrap(),
616 month_day: D31,
617 week_day: W5,
618 hour: H12,
619 minuter: M30,
620 second: S30,
621 };
622 {
623 let dist: DateTime = conf.next_with_time(dt0.into()).into();
624 let mut dt0_dist = dist.clone();
625 dt0_dist.second = S0;
626 dt0_dist.minuter = M30;
627 dt0_dist.hour = H12;
628 dt0_dist.week_day = W1;
629 dt0_dist.month_day = D31;
630 assert!(dist == dt0_dist, "{:?}", dist);
631 assert!(dist.date.year() == 2022, "{:?}", dist.date);
632 assert!(dist.date.month() == 1, "{:?}", dist.date);
633 }
634 Ok(())
635 }
636 #[test]
637 fn test_month() -> Result<()> {
638 let conf = configure_monthday(MonthDays::default_value(D31))
640 .build_with_hours(Hours::default_array(&[H12]))
641 .build_with_minuter(Minuters::default_array(&[M30]))
642 .build_with_second(Seconds::default_array(&[S0]));
643 let dt0 = DateTime {
644 date: NaiveDate::from_ymd_opt(2022, 1, 31).unwrap(),
645 month_day: D31,
646 week_day: W5,
647 hour: H12,
648 minuter: M30,
649 second: S30,
650 };
651 {
652 let dist: DateTime = conf.next_with_time(dt0.into()).into();
653 let mut dt0_dist = dist.clone();
654 dt0_dist.second = S0;
655 dt0_dist.minuter = M30;
656 dt0_dist.hour = H12;
657 dt0_dist.week_day = W4;
658 dt0_dist.month_day = D31;
659 assert!(dist == dt0_dist, "{:?}", dist);
660 assert!(dist.date.year() == 2022, "{:?}", dist.date);
661 assert!(dist.date.month() == 3, "{:?}", dist.date);
662 }
663 Ok(())
664 }
665
666 fn compare(conf: &TimerConf, times: &[NaiveDateTime]) {
667 let len = times.len() - 1;
668 let mut index = 0;
669 loop {
670 assert_eq!(
671 conf.next_with_time(times[index].clone()),
672 times[index + 1].clone()
673 );
674 index += 1;
675 if index == len {
676 break;
677 }
678 }
679 }
680 #[test]
681 fn test_every() {
682 use Minuter::*;
683 let minuters = Minuters::every(11);
684 assert_eq!(minuters, Minuters::default_array(&[M0, M11, M22, M33, M44, M55]));
685 let minuters = Minuters::every(0);
686 assert_eq!(minuters, Minuters::_default());
687 let minuters = Minuters::every(30);
688 assert_eq!(minuters, Minuters::default_array(&[M0, M30]));
689 let minuters = Minuters::every(31);
690 assert_eq!(minuters, Minuters::default_array(&[M0, M31]));
691 let minuters = Minuters::every(60);
692 assert_eq!(minuters, Minuters::default_array(&[M0]));
693
694 let minuters = Minuters::every(500);
695 assert_eq!(minuters, Minuters::default_array(&[M0]));
696 }
697}