package timeutil import ( "testing" "time" ) func TestPeriodOverlapUsesHalfOpenIntervals(t *testing.T) { start := time.Date(2026, 5, 29, 6, 0, 0, 0, time.UTC) left := Period{Start: start, End: start.Add(time.Hour)} touching := Period{Start: start.Add(time.Hour), End: start.Add(2 * time.Hour)} overlapping := Period{Start: start.Add(30 * time.Minute), End: start.Add(90 * time.Minute)} if left.Overlaps(touching) { t.Fatal("touching half-open periods overlap, want false") } if !left.Overlaps(overlapping) { t.Fatal("overlapping periods do not overlap, want true") } } func TestClockWindowHandlesOvernight(t *testing.T) { location := time.FixedZone("Test", -5*60*60) date := time.Date(2026, 5, 29, 12, 0, 0, 0, location) start, _ := ParseClock("22:00") end, _ := ParseClock("06:00") window := ClockWindow(date, location, start, end) if got := window.Start.Format("2006-01-02T15:04"); got != "2026-05-29T22:00" { t.Fatalf("Start = %s, want 2026-05-29T22:00", got) } if got := window.End.Format("2006-01-02T15:04"); got != "2026-05-30T06:00" { t.Fatalf("End = %s, want 2026-05-30T06:00", got) } } func TestClockWindowUsesCivilTimeAcrossDSTTransitions(t *testing.T) { location, err := LoadLocation("America/Chicago") if err != nil { t.Fatalf("LoadLocation() error = %v", err) } tests := []struct { name string date time.Time start string end string wantStart string wantEnd string }{ { name: "spring-forward daytime", date: time.Date(2026, time.March, 8, 12, 0, 0, 0, location), start: "00:00", end: "06:00", wantStart: "2026-03-08T00:00:00-06:00", wantEnd: "2026-03-08T06:00:00-05:00", }, { name: "fall-back daytime", date: time.Date(2026, time.November, 1, 12, 0, 0, 0, location), start: "00:00", end: "06:00", wantStart: "2026-11-01T00:00:00-05:00", wantEnd: "2026-11-01T06:00:00-06:00", }, { name: "spring-forward overnight", date: time.Date(2026, time.March, 8, 12, 0, 0, 0, location), start: "22:00", end: "06:00", wantStart: "2026-03-08T22:00:00-05:00", wantEnd: "2026-03-09T06:00:00-05:00", }, { name: "spring-forward end of day", date: time.Date(2026, time.March, 8, 12, 0, 0, 0, location), start: "18:00", end: "24:00", wantStart: "2026-03-08T18:00:00-05:00", wantEnd: "2026-03-09T00:00:00-05:00", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { start, err := ParseClock(tt.start) if err != nil { t.Fatalf("ParseClock(%q) error = %v", tt.start, err) } end, err := ParseClock(tt.end) if err != nil { t.Fatalf("ParseClock(%q) error = %v", tt.end, err) } window := ClockWindow(tt.date, location, start, end) if got := window.Start.Format(time.RFC3339); got != tt.wantStart { t.Fatalf("Start = %s, want %s", got, tt.wantStart) } if got := window.End.Format(time.RFC3339); got != tt.wantEnd { t.Fatalf("End = %s, want %s", got, tt.wantEnd) } }) } }