@@ -117,30 +117,55 @@ func TestNextScheduleTime_SpecificSchedule(t *testing.T) {
117117 assert .True (t , ts .Time .After (time .Now ()))
118118}
119119
120- func TestNextScheduleTime_UsesDynamicDTStartDefaults (t * testing.T ) {
120+ func TestNextScheduleTime_MidnightAnchoredDefaults (t * testing.T ) {
121121 tests := []struct {
122122 name string
123123 schedule string
124124 now time.Time
125125 expected time.Time
126126 }{
127127 {
128- name : "daily defaults to current time of day " ,
128+ name : "daily defaults to midnight " ,
129129 schedule : "FREQ=DAILY" ,
130130 now : time .Date (2026 , 1 , 2 , 3 , 4 , 5 , 0 , time .UTC ),
131- expected : time .Date (2026 , 1 , 3 , 3 , 4 , 5 , 0 , time .UTC ),
131+ expected : time .Date (2026 , 1 , 3 , 0 , 0 , 0 , 0 , time .UTC ),
132132 },
133133 {
134- name : "weekly defaults to current weekday and time " ,
134+ name : "weekly defaults to anchor weekday at midnight " ,
135135 schedule : "FREQ=WEEKLY" ,
136136 now : time .Date (2026 , 1 , 7 , 10 , 11 , 12 , 0 , time .UTC ),
137- expected : time .Date (2026 , 1 , 14 , 10 , 11 , 12 , 0 , time .UTC ),
137+ expected : time .Date (2026 , 1 , 14 , 0 , 0 , 0 , 0 , time .UTC ),
138138 },
139139 {
140- name : "monthly defaults to current day of month and time " ,
140+ name : "monthly defaults to anchor day at midnight " ,
141141 schedule : "FREQ=MONTHLY" ,
142142 now : time .Date (2026 , 1 , 15 , 8 , 30 , 0 , 0 , time .UTC ),
143- expected : time .Date (2026 , 2 , 15 , 8 , 30 , 0 , 0 , time .UTC ),
143+ expected : time .Date (2026 , 2 , 15 , 0 , 0 , 0 , 0 , time .UTC ),
144+ },
145+ {
146+ name : "minutely interval has a midnight-aligned phase" ,
147+ schedule : "FREQ=MINUTELY;INTERVAL=15" ,
148+ now : time .Date (2026 , 1 , 2 , 10 , 7 , 44 , 0 , time .UTC ),
149+ expected : time .Date (2026 , 1 , 2 , 10 , 15 , 0 , 0 , time .UTC ),
150+ },
151+ {
152+ name : "hourly defaults minute and second to zero" ,
153+ schedule : "FREQ=HOURLY" ,
154+ now : time .Date (2026 , 1 , 2 , 10 , 7 , 44 , 0 , time .UTC ),
155+ expected : time .Date (2026 , 1 , 2 , 11 , 0 , 0 , 0 , time .UTC ),
156+ },
157+ {
158+ name : "explicit minute overrides the anchor default" ,
159+ schedule : "FREQ=HOURLY;BYMINUTE=30" ,
160+ now : time .Date (2026 , 1 , 2 , 10 , 7 , 44 , 0 , time .UTC ),
161+ expected : time .Date (2026 , 1 , 2 , 10 , 30 , 0 , 0 , time .UTC ),
162+ },
163+ {
164+ name : "calendar rule inherits a zero second" ,
165+ schedule : "FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;BYMINUTE=0" ,
166+ // Monday 08:59:45 — next run is 09:00:00 sharp, not 09:00:45.
167+ now : time .Date (2026 , 1 , 12 , 8 , 59 , 45 , 0 , time .UTC ),
168+ expected : time .Date (2026 , 1 , 12 , 9 , 0 , 0 , 0 , time .UTC ),
144169 },
145170 }
146171
0 commit comments