github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekatime/calendar_private.go (about)

     1  // Copyright © 2021. All rights reserved.
     2  // Author: Ilya Stroy.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekatime
     7  
     8  //goland:noinspection GoSnakeCaseUsage
     9  const (
    10  	_CALENDAR2_DEFAULT_CAPACITY                    = 366
    11  	_CALENDAR2_CAUSE_DEFAULT_CAPACITY              = 64
    12  	_CALENDAR2_EVENT_DESCRIPTIONS_DEFAULT_CAPACITY = 16
    13  )
    14  
    15  func (wc *Calendar) dateToIndex(dd Date) uint {
    16  
    17  	// WARNING!
    18  	// Method assumes that wc.year == dd.Year().
    19  
    20  	// Calendar always works as in leap year.
    21  	// So, we need to increase doy +1 if it's not leap year and march+ month.
    22  
    23  	doy := dd.DayOfYear()
    24  	if dd.Month() >= MONTH_MARCH && !wc.isLeap {
    25  		doy++
    26  	}
    27  
    28  	return uint(doy)
    29  }
    30  
    31  func (wc *Calendar) rangeOfMonth(m Month) (uint, uint) {
    32  
    33  	d := Days(_Table0[m-1])
    34  	if m == MONTH_FEBRUARY && wc.isLeap {
    35  		d++
    36  	}
    37  
    38  	d1 := _Table2[m-1]
    39  	d2 := d1 + d - 1 // -1 for d2 be the last day of month
    40  
    41  	return uint(d1), uint(d2)
    42  }
    43  
    44  func (wc *Calendar) overrideDate(dd Date, eventID EventID, isDayOff, useEventID bool) {
    45  
    46  	// 3rd bool argument:
    47  	//
    48  	// A: Use provided EventID (`useEventID`),
    49  	// B: Calendar causing is enabled (`wc.cause != nil`).
    50  	//
    51  	// Truth table:
    52  	// A B Res
    53  	// 0 0 1
    54  	// 0 1 1
    55  	// 1 0 0
    56  	// 1 1 1
    57  	//
    58  	// Thus, it's a material conditional (implication).
    59  	// Read more: https://en.wikipedia.org/wiki/Material_conditional
    60  
    61  	if !(wc.IsValid() && dd.Year() == wc.year && (!useEventID || (wc.cause != nil))) {
    62  		return
    63  	}
    64  
    65  	idx := wc.dateToIndex(dd)
    66  	wc.dayOff.Set(idx, isDayOff)
    67  
    68  	// SAFETY:
    69  	// Condition above guarantees if `useEventID` is true,
    70  	// `wc.cause` is also not nil.
    71  	if useEventID {
    72  		wc.cause[idx] = eventID
    73  	}
    74  }
    75  
    76  func (wc *Calendar) nextDay(dd Date, isDayOff bool) Date {
    77  
    78  	if !(wc.IsValid() && dd.IsValid() && dd.Year() == wc.year) {
    79  		return _DATE_INVALID
    80  	}
    81  
    82  	var (
    83  		nextDay uint
    84  		exist   bool
    85  	)
    86  
    87  	if isDayOff {
    88  		nextDay, exist = wc.dayOff.NextUp(wc.dateToIndex(dd))
    89  	} else {
    90  		nextDay, exist = wc.dayOff.NextDown(wc.dateToIndex(dd))
    91  	}
    92  
    93  	if !exist {
    94  		return _DATE_INVALID
    95  	}
    96  
    97  	return NewDateFromDayOfYear(wc.year, Days(nextDay))
    98  }
    99  
   100  func (wc *Calendar) daysIn(m Month, isDayOff bool) []Day {
   101  
   102  	if !(wc.IsValid() && m.IsValid()) {
   103  		return nil
   104  	}
   105  
   106  	// We don't use Month.DaysInForYear() method here,
   107  	// because it treats years not in range [1900..4095] as invalid years.
   108  
   109  	d1, d2 := wc.rangeOfMonth(m)
   110  	ret := make([]Day, 0, 31)
   111  
   112  	if isDayOff {
   113  		for v, e := wc.dayOff.NextUp(d1 - 1); e && v <= d2; v, e = wc.dayOff.NextUp(v) {
   114  			ret = append(ret, Day(v-d1)+1)
   115  		}
   116  	} else {
   117  		for v, e := wc.dayOff.NextDown(d1 - 1); e && v <= d2; v, e = wc.dayOff.NextDown(v) {
   118  			ret = append(ret, Day(v-d1)+1)
   119  		}
   120  	}
   121  
   122  	return ret
   123  }
   124  
   125  func (wc *Calendar) daysInCount(m Month, isDayOff bool) Days {
   126  
   127  	if !(wc.IsValid() && m.IsValid()) {
   128  		return 0
   129  	}
   130  
   131  	// We don't use Month.DaysInForYear() method here,
   132  	// because it treats years not in range [1900..4095] as invalid years.
   133  
   134  	d1, d2 := wc.rangeOfMonth(m)
   135  	d := Days(d2 - d1 + 1)
   136  
   137  	c := Days(wc.dayOff.CountBetween(d1, d2))
   138  	if !isDayOff {
   139  		c = d - c
   140  	}
   141  
   142  	return c
   143  }
   144  
   145  func (wc *Calendar) doSaturdayAndSundayDayOff() {
   146  
   147  	if !wc.IsValid() {
   148  		return
   149  	}
   150  
   151  	w := NewDate(wc.year, MONTH_JANUARY, 1).Weekday()
   152  	var idx uint = 1
   153  
   154  	if w == WEEKDAY_SATURDAY {
   155  		// do nothing, idx already is 1
   156  
   157  	} else if w == WEEKDAY_SUNDAY {
   158  		wc.dayOff.Up(idx)
   159  		idx += 6
   160  
   161  	} else {
   162  		idx += uint(WEEKDAY_SATURDAY.To06() - w.To06())
   163  	}
   164  
   165  	for ; idx <= uint(_Table2[MONTH_MARCH-1]); idx += 7 {
   166  		wc.dayOff.Up(idx)
   167  		wc.dayOff.Up(idx + 1)
   168  	}
   169  
   170  	if !wc.isLeap {
   171  		// Calendar's year is always leap (29Feb).
   172  		// But sometimes the real year might be not leap and weekdays are:
   173  		// 28 feb sat and 1 mar sun.
   174  		// 29 feb will be marked as weekday (in loop above).
   175  		// Index increasing is placed below (after this condition).
   176  		// And the last thing we need to do is mark 1 mar sun as weekday.
   177  		// This is exactly what this code do.
   178  		if idx-6 == uint(_Table2[MONTH_MARCH-1]-1) {
   179  			wc.dayOff.Up(idx - 5)
   180  		}
   181  		idx++
   182  	}
   183  
   184  	for ; idx <= 366; idx += 7 {
   185  		wc.dayOff.Up(idx)
   186  		wc.dayOff.Up(idx + 1)
   187  	}
   188  }