diff --git a/internal/timeperiod/timeperiod.go b/internal/timeperiod/timeperiod.go index fb2ad6e60..fc3e930ae 100644 --- a/internal/timeperiod/timeperiod.go +++ b/internal/timeperiod/timeperiod.go @@ -2,7 +2,6 @@ package timeperiod import ( "github.com/teambition/rrule-go" - "log" "time" ) @@ -46,6 +45,8 @@ func (p *TimePeriod) NextTransition(base time.Time) time.Time { return transition } +// Entry represents a single TimePeriod segment and defines its own start and end time. +// You should initialize it by calling its Init method before using any of its other methods. type Entry struct { Start, End time.Time @@ -57,6 +58,7 @@ type Entry struct { } // Init initializes the rrule instance from the configured rrule string +// Returns error when it fails to initiate an RRule instance from the configured rrule string (if any). func (e *Entry) Init() error { if e.rrule != nil || e.RecurrenceRule == "" { return nil @@ -82,10 +84,10 @@ func (e *Entry) Init() error { } // Contains returns whether a point in time t is covered by this entry. +// It panics when it's used before initializing the rrule instance while a rrule string is configured. func (e *Entry) Contains(t time.Time) bool { - err := e.Init() - if err != nil { - log.Printf("Can't initialize entry: %s", err) + if e.rrule == nil && e.RecurrenceRule != "" { + panic("time period entry not initialized") } if t.Before(e.Start) { @@ -108,11 +110,10 @@ func (e *Entry) Contains(t time.Time) bool { // NextTransition returns the next recurrence start or end of this entry relative to the given time inclusively. // This function returns also time.Time's zero value if there is no transition that starts/ends at/after the -// specified time. +// specified time. It panics when it's used before initializing the rrule instance while a rrule string is configured. func (e *Entry) NextTransition(t time.Time) time.Time { - err := e.Init() - if err != nil { - log.Printf("Can't initialize entry: %s", err) + if e.rrule == nil && e.RecurrenceRule != "" { + panic("time period entry not initialized") } if t.Before(e.Start) { diff --git a/internal/timeperiod/timeperiod_test.go b/internal/timeperiod/timeperiod_test.go index 709e5655c..0af69bc52 100644 --- a/internal/timeperiod/timeperiod_test.go +++ b/internal/timeperiod/timeperiod_test.go @@ -18,11 +18,11 @@ func TestEntry(t *testing.T) { start := berlinTime("2023-03-01 09:00:00") end := berlinTime("2023-03-01 11:00:00") until := berlinTime("2023-03-03 09:00:00") - e := &timeperiod.Entry{ + e := initEntry(&timeperiod.Entry{ Start: start, End: end, RecurrenceRule: fmt.Sprintf("FREQ=DAILY;UNTIL=%s", until.UTC().Format(rrule.DateTimeFormat)), - } + }) t.Run("TimeAtFirstRecurrenceStart", func(t *testing.T) { assert.True(t, e.Contains(start)) @@ -72,11 +72,11 @@ func TestEntry(t *testing.T) { t.Run("DST", func(t *testing.T) { start := berlinTime("2023-03-25 01:00:00") end := berlinTime("2023-03-25 02:30:00") - e := &timeperiod.Entry{ + e := initEntry(&timeperiod.Entry{ Start: start, End: end, RecurrenceRule: "FREQ=DAILY", - } + }) assert.True(t, e.Contains(start)) @@ -94,11 +94,11 @@ func TestEntry(t *testing.T) { start := berlinTime("2023-03-01 08:00:00") end := berlinTime("2023-03-01 12:30:00") - e := &timeperiod.Entry{ + e := initEntry(&timeperiod.Entry{ Start: start, End: end, RecurrenceRule: "FREQ=DAILY", - } + }) t.Run("TimeAtFirstRecurrenceStart", func(t *testing.T) { assert.Equal(t, end, e.NextTransition(start)) @@ -123,11 +123,11 @@ func TestEntry(t *testing.T) { t.Run("DST", func(t *testing.T) { start := berlinTime("2023-03-25 01:00:00") end := berlinTime("2023-03-25 02:30:00") - e := &timeperiod.Entry{ + e := initEntry(&timeperiod.Entry{ Start: start, End: end, RecurrenceRule: "FREQ=DAILY", - } + }) assert.Equal(t, end, e.NextTransition(start), "next transition should match the first recurrence end") @@ -149,13 +149,14 @@ func TestTimePeriodTransitions(t *testing.T) { t.Run("WithOneEntry", func(t *testing.T) { start := berlinTime("2023-03-27 09:00:00") end := berlinTime("2023-03-27 17:00:00") + p := &timeperiod.TimePeriod{ Name: "Transition Test", - Entries: []*timeperiod.Entry{{ + Entries: []*timeperiod.Entry{initEntry(&timeperiod.Entry{ Start: start, End: end, RecurrenceRule: "FREQ=DAILY", - }}, + })}, } assert.Equal(t, end, p.NextTransition(start), "next transition should match the interval end") @@ -169,16 +170,16 @@ func TestTimePeriodTransitions(t *testing.T) { p := &timeperiod.TimePeriod{ Name: "Transition Test", Entries: []*timeperiod.Entry{ - { + initEntry(&timeperiod.Entry{ Start: start, End: end, RecurrenceRule: "FREQ=HOURLY;BYHOUR=1,3,5,7,9,11,13,15", - }, - { + }), + initEntry(&timeperiod.Entry{ Start: berlinTime("2023-03-27 08:00:00"), End: berlinTime("2023-03-27 08:30:00"), RecurrenceRule: "FREQ=HOURLY;BYHOUR=0,2,4,6,8,10,12,14", - }, + }), }, } @@ -219,3 +220,12 @@ func berlinTime(value string) time.Time { return t } + +func initEntry(e *timeperiod.Entry) *timeperiod.Entry { + err := e.Init() + if err != nil { + panic(err) + } + + return e +}