Skip to content

Instantly share code, notes, and snippets.

@hamza-cskn
Created January 15, 2025 18:02
Show Gist options
  • Save hamza-cskn/a64db49bbfa4e5196ba82cccabc89352 to your computer and use it in GitHub Desktop.
Save hamza-cskn/a64db49bbfa4e5196ba82cccabc89352 to your computer and use it in GitHub Desktop.
a util file to help periodical time calculations.
package utils
import (
"time"
)
// Period type to represent a point in modularized time.
/**
* e.g: Every monday at 10:00.
* duration: period duration is one week.
* margin: if margin is 10 hours, then this period is every monday.
*
* e.g: Every 18.00 in the evening.
* duration: period duration is one day.
* margin: if margin is 18 hours, then this period is every day at 18.00.
*/
type Period struct {
duration time.Duration
margin time.Duration
}
// Same as Period but represents a time part instead of point.
type RangedPeriod struct {
duration time.Duration
startMargin time.Duration
endMargin time.Duration
}
type PeriodicPlan struct {
periods []RangedPeriod
}
func (plan *PeriodicPlan) RegisterPeriod(period RangedPeriod) {
plan.periods = append(plan.periods, period)
}
func (plan *PeriodicPlan) IsIn(time time.Time) bool {
for _, rangedPeriod := range plan.periods {
if rangedPeriod.IsIn(time) {
return true
}
}
return false
}
/* converts ranged period to point-like start period, end period */
func (rangedPeriod *RangedPeriod) getStartEndPeriods() (Period, Period) {
return Period{rangedPeriod.duration, rangedPeriod.startMargin}, Period{rangedPeriod.duration, rangedPeriod.endMargin}
}
func (rangedPeriod *RangedPeriod) IsIn(time time.Time) bool {
startPeriod, endPeriod := rangedPeriod.getStartEndPeriods()
start := startPeriod.NextPointBefore(time)
end := endPeriod.NextPointAfter(time)
if end.UnixNano()-start.UnixNano() > rangedPeriod.duration.Nanoseconds() {
return false
}
return time.After(start) && time.Before(end)
}
func (period *Period) NextPointBefore(t time.Time) time.Time {
return period.NextPointAfter(t.Add(-period.duration))
}
func (period *Period) NextPointAfter(t time.Time) time.Time {
nowNano := t.UnixNano()
periodNano := period.duration.Nanoseconds()
nextNano := nowNano - (nowNano % periodNano) // align
nextNano += period.margin.Nanoseconds() // add margin
if nowNano > nextNano {
nextNano += period.duration.Nanoseconds() // if margin was not enough to be next point, next period will.
}
newTime := time.Unix(0, nextNano)
zone, _ := t.Zone()
location, _ := time.LoadLocation(zone)
result := newTime.In(location)
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment