Skip to content

Commit

Permalink
cal: perf improvements
Browse files Browse the repository at this point in the history
Avoid calling time functions repeatedly (triggers loc lookup).
If Month is set on Holiday, use that to skip calcs in IsHoliday().

Fixes #77
  • Loading branch information
rickar committed Nov 8, 2021
1 parent 405cd5b commit a2ee0b3
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 30 deletions.
22 changes: 17 additions & 5 deletions v2/cal.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ func (c *Calendar) AddHoliday(h ...*Holiday) {
c.Holidays = make([]*Holiday, 0, 12)
}

for _, hd := range h {
c.Holidays = append(c.Holidays, hd)
}
c.Holidays = append(c.Holidays, h...)
}

// IsHoliday reports whether a given date is a holiday or an observation day.
Expand All @@ -53,9 +51,23 @@ func (c *Calendar) IsHoliday(date time.Time) (actual, observed bool, h *Holiday)

year, month, day := date.Date()
for _, hol := range c.Holidays {

if hol.Month != 0 && hol.Month != month {
continue
}

act, obs := hol.Calc(year)
actMatch := !act.IsZero() && act.Month() == month && act.Day() == day
obsMatch := !obs.IsZero() && obs.Month() == month && obs.Day() == day

actMatch := !act.IsZero()
if actMatch {
_, actMonth, actDay := act.Date()
actMatch = actMonth == month && actDay == day
}
obsMatch := !obs.IsZero()
if obsMatch {
_, obsMonth, obsDay := obs.Date()
obsMatch = obsMonth == month && obsDay == day
}
if actMatch || obsMatch {
return actMatch, obsMatch, hol
}
Expand Down
15 changes: 6 additions & 9 deletions v2/cal_business.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ func (c *BusinessCalendar) IsWorkday(date time.Time) bool {
}

_, obs, _ := c.IsHoliday(date)
if obs {
return false
}

return true
return !obs
}

// IsWorkTime reports whether a given date and time is within working hours.
Expand Down Expand Up @@ -117,8 +113,7 @@ func (c *BusinessCalendar) IsWorkTime(date time.Time) bool {
endMinute = endTime.Minute()
}

h := date.Hour()
m := date.Minute()
h, m, _ := date.Clock()
return (h == startHour && m >= startMinute) ||
(h > startHour && h < endHour) ||
(h == endHour && m <= endMinute)
Expand Down Expand Up @@ -273,7 +268,8 @@ func (c *BusinessCalendar) WorkdayStart(date time.Time) time.Time {
}

if c.WorkdayStartFunc == nil {
return time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()).Add(c.workdayStart)
year, month, day := date.Date()
return time.Date(year, month, day, 0, 0, 0, 0, date.Location()).Add(c.workdayStart)
}
return c.WorkdayStartFunc(date)
}
Expand All @@ -286,7 +282,8 @@ func (c *BusinessCalendar) WorkdayEnd(date time.Time) time.Time {
}

if c.WorkdayEndFunc == nil {
return time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()).Add(c.workdayEnd)
year, month, day := date.Date()
return time.Date(year, month, day, 0, 0, 0, 0, date.Location()).Add(c.workdayEnd)
}
return c.WorkdayEndFunc(date)
}
Expand Down
44 changes: 28 additions & 16 deletions v2/cal_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,54 +79,65 @@ func IsWeekdayN(t time.Time, day time.Weekday, n int) bool {
return false
}

tYear, tMonth, tDay := t.Date()

if n > 0 {
return (t.Day()-1)/7 == (n - 1)
return (tDay-1)/7 == (n - 1)
}

want := WeekdayN(t.Year(), t.Month(), day, n)
return want.Year() == t.Year() && want.Month() == t.Month() && want.Day() == t.Day()
want := WeekdayN(tYear, tMonth, day, n)
wantYear, wantMonth, wantDay := want.Date()
return wantYear == tYear && wantMonth == tMonth && wantDay == tDay
}

// DayStart reports the start of the day in t (sets time fields zero).
func DayStart(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
year, month, day := t.Date()
return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
}

// DayEnd reports the end of the day in t (sets time fields to maximum).
func DayEnd(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 999999999, t.Location())
year, month, day := t.Date()
return time.Date(year, month, day, 23, 59, 59, 999999999, t.Location())
}

// MonthStart reports the starting day of the month in t. The time portion is
// unchanged.
func MonthStart(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), 1, t.Hour(), t.Minute(), t.Second(),
t.Nanosecond(), t.Location())
year, month, _ := t.Date()
hour, min, sec := t.Clock()
return time.Date(year, month, 1, hour, min, sec, t.Nanosecond(), t.Location())
}

// MonthEnd reports the ending day of the month in t. The time portion is
// unchanged.
func MonthEnd(t time.Time) time.Time {
return time.Date(t.Year(), t.Month()+1, 0, t.Hour(), t.Minute(),
t.Second(), t.Nanosecond(), t.Location())
year, month, _ := t.Date()
hour, min, sec := t.Clock()
return time.Date(year, month+1, 0, hour, min, sec, t.Nanosecond(), t.Location())
}

// ReplaceLocation returns a copy of the given time in the given location
// without changing the time of day.
func ReplaceLocation(t time.Time, loc *time.Location) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), loc)
year, month, day := t.Date()
hour, min, sec := t.Clock()
return time.Date(year, month, day, hour, min, sec, t.Nanosecond(), loc)
}

// JulianDayNumber reports the Julian Day Number for t. Note that Julian days
// start at 12:00 UTC.
func JulianDayNumber(t time.Time) int {
// algorithm from http://www.tondering.dk/claus/cal/julperiod.php#formula
utc := t.UTC()
a := (14 - int(utc.Month())) / 12
y := utc.Year() + 4800 - a
m := int(utc.Month()) + 12*a - 3
year, month, day := utc.Date()

a := (14 - int(month)) / 12
y := year + 4800 - a
m := int(month) + 12*a - 3

jdn := utc.Day() + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
jdn := day + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
if utc.Hour() < 12 {
jdn--
}
Expand All @@ -149,8 +160,9 @@ func JulianDate(t time.Time) float64 {
if utc.Hour() < 12 {
jdn++
}
return float64(jdn) + (float64(utc.Hour())-12.0)/24.0 +
float64(utc.Minute())/1440.0 + float64(utc.Second())/86400.0
hour, min, sec := utc.Clock()
return float64(jdn) + (float64(hour)-12.0)/24.0 +
float64(min)/1440.0 + float64(sec)/86400.0
}

// ModifiedJulianDate reports the modified Julian Date Number for t. Note
Expand Down

0 comments on commit a2ee0b3

Please sign in to comment.