package timeutil import ( "fmt" "strconv" "strings" "time" ) type Period struct { Start time.Time `json:"start"` End time.Time `json:"end"` } func (p Period) IsValid() bool { return p.End.After(p.Start) } func (p Period) Overlaps(other Period) bool { return p.Start.Before(other.End) && other.Start.Before(p.End) } func (p Period) Contains(t time.Time) bool { return !t.Before(p.Start) && t.Before(p.End) } func (p Period) Intersection(other Period) (Period, bool) { if !p.Overlaps(other) { return Period{}, false } start := p.Start if other.Start.After(start) { start = other.Start } end := p.End if other.End.Before(end) { end = other.End } return Period{Start: start, End: end}, true } func ParseClock(value string) (time.Duration, error) { parts := strings.Split(value, ":") if len(parts) != 2 { return 0, fmt.Errorf("expected HH:MM") } hour, err := strconv.Atoi(parts[0]) if err != nil { return 0, fmt.Errorf("invalid hour") } minute, err := strconv.Atoi(parts[1]) if err != nil { return 0, fmt.Errorf("invalid minute") } if hour < 0 || hour > 24 { return 0, fmt.Errorf("hour must be between 00 and 24") } if minute < 0 || minute > 59 { return 0, fmt.Errorf("minute must be between 00 and 59") } if hour == 24 && minute != 0 { return 0, fmt.Errorf("24 is only valid as 24:00") } return time.Duration(hour)*time.Hour + time.Duration(minute)*time.Minute, nil } func CivilDay(date time.Time, location *time.Location) Period { local := date.In(location) start := time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, location) return Period{Start: start, End: start.AddDate(0, 0, 1)} } func ClockWindow(date time.Time, location *time.Location, startClock time.Duration, endClock time.Duration) Period { day := CivilDay(date, location) start := day.Start.Add(startClock) end := day.Start.Add(endClock) if !end.After(start) { end = end.AddDate(0, 0, 1) } return Period{Start: start, End: end} }