github.com/simpleiot/simpleiot@v0.18.3/client/schedule.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 "strconv" 8 "time" 9 ) 10 11 type schedule struct { 12 startTime string 13 endTime string 14 // A Weekday specifies a day of the week (Sunday = 0, ...). 15 weekdays []time.Weekday 16 dates []string 17 } 18 19 func newSchedule(start, end string, weekdays []time.Weekday, dates []string) *schedule { 20 return &schedule{ 21 startTime: start, 22 endTime: end, 23 weekdays: weekdays, 24 dates: dates, 25 } 26 } 27 28 func (s *schedule) activeForTime(t time.Time) (bool, error) { 29 tUTC := t.UTC() 30 31 // parse out hour/minute 32 matches := reHourMin.FindStringSubmatch(s.startTime) 33 if len(matches) < 3 { 34 return false, fmt.Errorf("TimeRange: invalid start: %v ", s.startTime) 35 } 36 37 startHour, err := strconv.Atoi(matches[1]) 38 if err != nil { 39 return false, fmt.Errorf("TimeRange: error parsing start hour: %v", matches[1]) 40 } 41 42 startMin, err := strconv.Atoi(matches[2]) 43 if err != nil { 44 return false, fmt.Errorf("TimeRange: error parsing start hour: %v", matches[1]) 45 } 46 47 matches = reHourMin.FindStringSubmatch(s.endTime) 48 if len(matches) < 3 { 49 return false, fmt.Errorf("TimeRange: invalid end: %v ", s.endTime) 50 } 51 52 endHour, err := strconv.Atoi(matches[1]) 53 54 if err != nil { 55 return false, fmt.Errorf("TimeRange: error parsing end hour: %v", matches[1]) 56 } 57 58 endMin, err := strconv.Atoi(matches[2]) 59 60 if err != nil { 61 return false, fmt.Errorf("TimeRange: error parsing end hour: %v", matches[1]) 62 } 63 64 y := tUTC.Year() 65 m := tUTC.Month() 66 d := tUTC.Day() 67 68 start := time.Date(y, m, d, startHour, startMin, 0, 0, time.UTC) 69 end := time.Date(y, m, d, endHour, endMin, 0, 0, time.UTC) 70 71 timeRanges := timeRanges{ 72 {start, end}, 73 } 74 75 // adjust time ranges if end time is before start 76 if !end.After(start) { 77 timeRanges[0].end = timeRanges[0].end.AddDate(0, 0, 1) 78 79 timeRanges = append(timeRanges, 80 timeRange{start.AddDate(0, 0, -1), end}, 81 ) 82 } 83 84 timeRanges.filterWeekdays(s.weekdays) 85 err = timeRanges.filterDates(s.dates) 86 if err != nil { 87 return false, err 88 } 89 90 if timeRanges.in(t) { 91 return true, nil 92 } 93 94 return false, nil 95 } 96 97 var reHourMin = regexp.MustCompile(`(\d{1,2}):(\d\d)`) 98 var reDate = regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`) 99 100 type timeRange struct { 101 start time.Time 102 end time.Time 103 } 104 105 // in returns true if date is in time range 106 func (tr *timeRange) in(t time.Time) bool { 107 if tr.start.After(tr.end) { 108 log.Println("BUG: LocalTimeRange.In -- start is before end") 109 return false 110 } 111 112 // normal situation 113 if t.Before(tr.start) { 114 return false 115 } 116 117 if t.Before(tr.end) { 118 return true 119 } 120 121 return false 122 } 123 124 type timeRanges []timeRange 125 126 // in returns true if time is in any of the time ranges 127 func (trs *timeRanges) in(t time.Time) bool { 128 for _, tr := range *trs { 129 if tr.in(t) { 130 return true 131 } 132 } 133 134 return false 135 } 136 137 func (trs *timeRanges) filterDates(dates []string) error { 138 if len(dates) <= 0 { 139 return nil 140 } 141 142 var trsNew timeRanges 143 for _, tr := range *trs { 144 for _, d := range dates { 145 matches := reDate.FindStringSubmatch(d) 146 if len(matches) < 4 { 147 return fmt.Errorf("Invalid date: %v", d) 148 } 149 150 year, err := strconv.Atoi(matches[1]) 151 if err != nil { 152 return fmt.Errorf("Invalid year: %v", d) 153 } 154 155 month, err := strconv.Atoi(matches[2]) 156 if err != nil { 157 return fmt.Errorf("Invalid month: %v", d) 158 } 159 160 day, err := strconv.Atoi(matches[3]) 161 if err != nil { 162 return fmt.Errorf("Invalid day: %v", d) 163 } 164 165 if year != tr.start.UTC().Year() { 166 continue 167 } 168 169 if month != int(tr.start.UTC().Month()) { 170 continue 171 } 172 173 if day != tr.start.UTC().Day() { 174 continue 175 } 176 177 trsNew = append(trsNew, tr) 178 } 179 } 180 181 *trs = trsNew 182 183 return nil 184 } 185 186 // filterWeekdays removes time ranges that do not have a Start time in the provided list of weekdays 187 func (trs *timeRanges) filterWeekdays(weekdays []time.Weekday) { 188 if len(weekdays) <= 0 { 189 return 190 } 191 192 var trsNew timeRanges 193 for _, tr := range *trs { 194 wdFound := false 195 for _, wd := range weekdays { 196 if tr.start.Weekday() == wd { 197 wdFound = true 198 break 199 } 200 } 201 if wdFound { 202 trsNew = append(trsNew, tr) 203 } 204 } 205 206 *trs = trsNew 207 }