Created
January 15, 2025 18:02
-
-
Save hamza-cskn/a64db49bbfa4e5196ba82cccabc89352 to your computer and use it in GitHub Desktop.
a util file to help periodical time calculations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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