82 lines
1.9 KiB
Go
82 lines
1.9 KiB
Go
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}
|
|
}
|