github.com/iDigitalFlame/xmt@v0.5.4/c2/cfg/workhours.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 package cfg 18 19 import ( 20 "time" 21 22 "github.com/iDigitalFlame/xmt/data" 23 "github.com/iDigitalFlame/xmt/util/xerr" 24 ) 25 26 // WorkHours is a struct that can be used to indicate to a Session when it should 27 // and shouldn't operate. When a WorkHours struct is set, a Session will check it 28 // before operating and will wait until the next set timeslot (depending on WorkHours 29 // values). 30 // 31 // This struct is also compatible with the Setting interface and can be used 32 // directly in Pack or Build functions. 33 // 34 // Using this in a Pack or Build function when it is empty will clear an other 35 // previous and/or set WorkHours values. 36 type WorkHours struct { 37 // Days is a bitmask of the days that this WorkHours struct allows us to work 38 // on. The bit values are 0 (Sunday) to 7 (Saturday). Values 0, 255 and anything 39 // over 126 are treated as all days selected. 40 Days uint8 41 // StartHour is the 0-23 value of the hour that this WorkHours struct allows 42 // us to work on. If this value is greater than 23, an error will be returned 43 // when attempting to use it. 44 // 45 // If this value and StartMin are zero, the WorkHours will start after midnight 46 // of the next day (if Day or EndHour and EndMin are set). 47 StartHour uint8 48 // StartMin is the 0-59 value of the minute that this WorkHours struct allows 49 // us to work on. If this value is greater than 59, an error will be returned 50 // when attempting to use it. 51 // 52 // If this value and StartHour are zero, the WorkHours will start after midnight 53 // of the next day (if Day or EndHour and EndMin are set). 54 StartMin uint8 55 // EndHour is the 0-23 value of the hour that this WorkHours struct stops us 56 // from working. If this value is greater than 23, an error will be returned 57 // when attempting to use it. 58 // 59 // If this value and EndMin are zero, the WorkHours will continue unchanged. 60 EndHour uint8 61 // EndMin is the 0-59 value of the minute that this WorkHours struct stops us 62 // from working. If this value is greater than 59, an error will be returned 63 // when attempting to use it. 64 // 65 // If this value and EndHour are zero, the WorkHours will continue unchanged. 66 EndMin uint8 67 } 68 69 func (w WorkHours) id() cBit { 70 return valWorkHours 71 } 72 73 // Empty returns true if this WorkHours struct is considered empty as nothing 74 // is set and all values are zero, false otherwise. 75 func (w WorkHours) Empty() bool { 76 return w.StartHour == 0 && w.StartMin == 0 && w.EndHour == 0 && w.EndMin == 0 && (w.Days == 0 || w.Days > 126) 77 } 78 func (w WorkHours) args() []byte { 79 return []byte{byte(valWorkHours), w.Days, w.StartHour, w.StartMin, w.EndHour, w.EndMin} 80 } 81 82 // Verify checks the values set in this WorkHours struct and returns any errors 83 // due to the number/time values being invalid. 84 func (w WorkHours) Verify() error { 85 switch { 86 case w.EndMin > 59: 87 return xerr.Sub("invalid EndMin value", 0x73) 88 case w.EndHour > 23: 89 return xerr.Sub("invalid EndHour value", 0x72) 90 case w.StartMin > 59: 91 return xerr.Sub("invalid StartMin value", 0x71) 92 case w.StartHour > 23: 93 return xerr.Sub("invalid StartHour value", 0x70) 94 } 95 return nil 96 } 97 98 // Work returns the time that should be waitied for this WorkHours to be active. 99 // If zero, then this means that the WorkHours applies currently and work can be 100 // done. 101 // 102 // This function will not return no more than time to reach the next day. 103 func (w WorkHours) Work() time.Duration { 104 if (w.Days == 0 || w.Days > 126) && w.StartHour == 0 && w.StartMin == 0 && w.EndHour == 0 && w.EndMin == 0 { 105 return 0 106 } 107 n := time.Now() 108 // Bit-shit to see if we have that day enabled. 109 // Fastpath check if we need to check days at all. 110 if w.Days > 0 && w.Days < 127 && (w.Days&(1<<uint(n.Weekday()))) == 0 { 111 // Figure out how much time until the next day. 112 y, m, d := n.Date() 113 return time.Date(y, m, d+1, 0, 0, 0, 0, n.Location()).Sub(n) 114 } 115 // End == 0 is valid 116 // - This can be used to lazy-mans end at midnight as the next check for day 117 // will tell it to wait (so next day). 118 // 119 // End == 0 && Days == 0 is also valid 120 // - This means don't start UNTIL after Start for the given day. 121 if w.StartHour == 0 && w.StartMin == 0 && w.EndHour == 0 && w.EndMin == 0 { 122 return 0 123 } 124 var ( 125 y, m, d = n.Date() 126 l = n.Location() 127 s time.Time 128 ) 129 if (w.StartHour == 0 && w.StartMin == 0) || w.StartHour > 23 || w.StartMin > 60 { 130 // Set start to today at zero 131 s = time.Date(y, m, d, 0, 0, 0, 0, l) 132 } else { 133 if s = time.Date(y, m, d, int(w.StartHour), int(w.StartMin), 0, 0, l); s.After(n) { // Wait until start time 134 return s.Sub(n) 135 } 136 } 137 if (w.EndHour == 0 && w.EndMin == 0) || w.EndHour > 23 || w.EndMin > 60 { 138 return 0 139 } 140 e := time.Date(y, m, d, int(w.EndHour), int(w.EndMin), 0, 0, l) 141 if e.Before(s) { // End is before start, bail. 142 return 0 143 } 144 if n.After(e) { // Wait until start for next 145 return s.AddDate(0, 0, 1).Sub(n) 146 } 147 return 0 148 } 149 150 // MarshalStream writes the data for this WorkHours struct to the supplied Writer. 151 func (w WorkHours) MarshalStream(s data.Writer) error { 152 if err := s.WriteUint8(w.Days); err != nil { 153 return err 154 } 155 if err := s.WriteUint8(w.StartHour); err != nil { 156 return err 157 } 158 if err := s.WriteUint8(w.StartMin); err != nil { 159 return err 160 } 161 if err := s.WriteUint8(w.EndHour); err != nil { 162 return err 163 } 164 return s.WriteUint8(w.EndMin) 165 } 166 167 // UnmarshalStream transforms this struct from a binary format that is read from 168 func (w *WorkHours) UnmarshalStream(r data.Reader) error { 169 if err := r.ReadUint8(&w.Days); err != nil { 170 return err 171 } 172 if err := r.ReadUint8(&w.StartHour); err != nil { 173 return err 174 } 175 if err := r.ReadUint8(&w.StartMin); err != nil { 176 return err 177 } 178 if err := r.ReadUint8(&w.EndHour); err != nil { 179 return err 180 } 181 return r.ReadUint8(&w.EndMin) 182 }