1package v2 2 3import ( 4 "strings" 5 "time" 6) 7 8// Validate ensures that all the time windows in t can be parsed. 9func (t *TimeWindowWhen) Validate() error { 10 if t == nil { 11 return nil 12 } 13 for _, windows := range t.MapTimeWindows() { 14 for _, window := range windows { 15 if err := window.Validate(); err != nil { 16 return err 17 } 18 } 19 } 20 return nil 21} 22 23// Validate ensures the TimeWindowTimeRange is valid. 24func (t *TimeWindowTimeRange) Validate() error { 25 _, err := t.InWindow(time.Now()) 26 return err 27} 28 29// MapTimeWindows returns a map of all the time windows in t. 30func (t *TimeWindowWhen) MapTimeWindows() map[string][]*TimeWindowTimeRange { 31 d := t.Days 32 return map[string][]*TimeWindowTimeRange{ 33 "All": d.All, 34 "Sunday": d.Sunday, 35 "Monday": d.Monday, 36 "Tuesday": d.Tuesday, 37 "Wednesday": d.Wednesday, 38 "Thursday": d.Thursday, 39 "Friday": d.Friday, 40 "Saturday": d.Saturday, 41 } 42} 43 44// InWindow determines if the current time falls between the provided time 45// window. Current should typically be time.Now() but to allow easier tests, it 46// must be provided as a parameter. Begin and end parameters must be strings 47// representing an hour of the day in the time.Kitchen format (e.g. "3:04PM") 48func (t *TimeWindowTimeRange) InWindow(current time.Time) (bool, error) { 49 // Get the year, month and day of the provided current time (e.g. 2016, 01 & 50 // 02) 51 year, month, day := current.Date() 52 53 // Remove any whitespaces in the begin and end times, for backward 54 // compatibility with Sensu v1 so "3:00 PM" becomes "3:00PM" and satisfies the 55 // time.Kitchen format 56 begin := strings.Replace(t.Begin, " ", "", -1) 57 end := strings.Replace(t.End, " ", "", -1) 58 59 // Parse the beginning of the provided time window in order to retrieve the 60 // hour and minute and apply it to current year, month and day so we end up 61 // with a date that corresponds to today (e.g. 2006-01-02T15:00:00Z) 62 beginTime, err := time.Parse(time.Kitchen, begin) 63 if err != nil { 64 return false, err 65 } 66 beginHour, beginMin, _ := beginTime.Clock() 67 beginTime = time.Date(year, month, day, beginHour, beginMin, 0, 0, time.UTC) 68 69 // Parse the ending of the provided time window in order to retrieve the 70 // hour and minute and apply it to current year, month and day so we end up 71 // with a date that corresponds to today (e.g. 2006-01-02T21:00:00Z) 72 endTime, err := time.Parse(time.Kitchen, end) 73 if err != nil { 74 return false, err 75 } 76 endHour, endMin, _ := endTime.Clock() 77 endTime = time.Date(year, month, day, endHour, endMin, 0, 0, time.UTC) 78 79 // Verify if the end of the time window is actually before the beginning of 80 // it, which means that the window ends the next day (e.g. 3:00PM to 8:00AM) 81 if endTime.Before(beginTime) { 82 // Verify if the current time is before the end of the time window, which 83 // means that we are already on the second day of the specified time window, 84 // therefore we just need to move the start of this window to the beginning 85 // of this second day (e.g. 3:00PM to 8:00AM, it's currently 5:00AM so let's 86 // move the beginning to 0:00AM) 87 if current.Before(endTime) { 88 beginTime = time.Date(year, month, day, 0, 0, 0, 0, time.UTC) 89 } else { 90 // We are currently on the first day of the window so we just need to move 91 // the end of this window to the end of the first day (e.g. 3:00PM to 92 // 8:00AM, it's currently 5:00PM so let's move the ending to 11:59PM) 93 endTime = time.Date(year, month, day, 23, 59, 59, 999999999, time.UTC) 94 } 95 } 96 97 return (current.After(beginTime) || current.Equal(beginTime)) && 98 (current.Before(endTime) || current.Equal(endTime)), nil 99} 100 101// InWindows determines if the current time falls between the provided time 102// windows. Current should typically be time.Now() but to allow easier tests, it 103// must be provided as a parameter. The function returns a positive value as 104// soon the current time falls within a time window 105func (t *TimeWindowWhen) InWindows(current time.Time) (bool, error) { 106 windowsByDay := t.MapTimeWindows() 107 108 var windows []*TimeWindowTimeRange 109 windows = append(windows, windowsByDay["All"]...) 110 windows = append(windows, windowsByDay[current.Weekday().String()]...) 111 112 // Go through the set of matching windows and process all the individual 113 // time windows. If the current time is within a time window in the selection, 114 // then the loop returns early with true and nil error. 115 for _, window := range windows { 116 // Determine if the current time falls between this specific time window 117 isInWindow, err := window.InWindow(current) 118 if err != nil { 119 return false, err 120 } 121 122 // Immediately return with a positive value if this time window conditions are 123 // met 124 if isInWindow { 125 return true, nil 126 } 127 } 128 129 // At this point no time windows conditions were met, return a negative value 130 return false, nil 131} 132