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  }