github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/timeutil/schedule_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package timeutil_test 21 22 import ( 23 "strings" 24 "testing" 25 "time" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/timeutil" 30 ) 31 32 func Test(t *testing.T) { TestingT(t) } 33 34 type timeutilSuite struct{} 35 36 var _ = Suite(&timeutilSuite{}) 37 38 func (ts *timeutilSuite) TestClock(c *C) { 39 td := timeutil.Clock{Hour: 23, Minute: 59} 40 c.Check(td.Add(time.Minute), Equals, timeutil.Clock{Hour: 0, Minute: 0}) 41 42 td = timeutil.Clock{Hour: 5, Minute: 34} 43 c.Check(td.Add(time.Minute), Equals, timeutil.Clock{Hour: 5, Minute: 35}) 44 45 td = timeutil.Clock{Hour: 10, Minute: 1} 46 c.Check(td.Sub(timeutil.Clock{Hour: 10, Minute: 0}), Equals, time.Minute) 47 48 td = timeutil.Clock{Hour: 23, Minute: 0} 49 c.Check(td.Add(time.Hour), Equals, timeutil.Clock{Hour: 0, Minute: 0}) 50 c.Check(td.Add(2*time.Hour), Equals, timeutil.Clock{Hour: 1, Minute: 0}) 51 c.Check(td.Sub(timeutil.Clock{Hour: 1, Minute: 0}), Equals, 22*time.Hour) 52 c.Check(td.Sub(timeutil.Clock{Hour: 0, Minute: 0}), Equals, 23*time.Hour) 53 54 td = timeutil.Clock{Hour: 1, Minute: 0} 55 c.Check(td.Sub(timeutil.Clock{Hour: 23, Minute: 0}), Equals, -2*time.Hour) 56 c.Check(td.Sub(timeutil.Clock{Hour: 1, Minute: 0}), Equals, time.Duration(0)) 57 58 td = timeutil.Clock{Hour: 0, Minute: 0} 59 c.Check(td.Sub(timeutil.Clock{Hour: 23, Minute: 0}), Equals, -1*time.Hour) 60 c.Check(td.Sub(timeutil.Clock{Hour: 1, Minute: 0}), Equals, -23*time.Hour) 61 } 62 63 func (ts *timeutilSuite) TestParseClock(c *C) { 64 for _, t := range []struct { 65 timeStr string 66 hour, minute int 67 errStr string 68 }{ 69 {"8:59", 8, 59, ""}, 70 {"08:59", 8, 59, ""}, 71 {"12:00", 12, 0, ""}, 72 {"xx", 0, 0, `cannot parse "xx"`}, 73 {"11:61", 0, 0, `cannot parse "11:61"`}, 74 {"25:00", 0, 0, `cannot parse "25:00"`}, 75 } { 76 ti, err := timeutil.ParseClock(t.timeStr) 77 if t.errStr != "" { 78 c.Check(err, ErrorMatches, t.errStr) 79 } else { 80 c.Check(err, IsNil) 81 c.Check(ti.Hour, Equals, t.hour) 82 c.Check(ti.Minute, Equals, t.minute) 83 } 84 } 85 } 86 87 func (ts *timeutilSuite) TestScheduleString(c *C) { 88 for _, t := range []struct { 89 sched timeutil.Schedule 90 str string 91 }{ 92 { 93 timeutil.Schedule{ 94 ClockSpans: []timeutil.ClockSpan{ 95 {Start: timeutil.Clock{Hour: 13, Minute: 41}, End: timeutil.Clock{Hour: 14, Minute: 59}}}, 96 }, 97 "13:41-14:59", 98 }, { 99 timeutil.Schedule{ 100 ClockSpans: []timeutil.ClockSpan{ 101 {Start: timeutil.Clock{Hour: 13, Minute: 41}, End: timeutil.Clock{Hour: 14, Minute: 59}}, 102 }, 103 WeekSpans: []timeutil.WeekSpan{ 104 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 105 }, 106 "mon,13:41-14:59", 107 }, { 108 timeutil.Schedule{ 109 ClockSpans: []timeutil.ClockSpan{ 110 {Start: timeutil.Clock{Hour: 13, Minute: 41}, End: timeutil.Clock{Hour: 14, Minute: 59}, Spread: true}}, 111 }, 112 "13:41~14:59", 113 }, { 114 timeutil.Schedule{ 115 ClockSpans: []timeutil.ClockSpan{ 116 {Start: timeutil.Clock{Hour: 6}, End: timeutil.Clock{Hour: 6}}}, 117 WeekSpans: []timeutil.WeekSpan{ 118 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Friday}}}, 119 }, 120 "mon-fri,06:00", 121 }, { 122 timeutil.Schedule{ 123 ClockSpans: []timeutil.ClockSpan{ 124 {Start: timeutil.Clock{Hour: 6}, End: timeutil.Clock{Hour: 6}}, 125 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 14}, Spread: true, Split: 2}}, 126 WeekSpans: []timeutil.WeekSpan{ 127 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Friday}}, 128 {Start: timeutil.Week{Weekday: time.Saturday}, End: timeutil.Week{Weekday: time.Saturday}}}, 129 }, 130 "mon-fri,sat,06:00,09:00~14:00/2", 131 }, { 132 timeutil.Schedule{ 133 ClockSpans: []timeutil.ClockSpan{ 134 {Start: timeutil.Clock{Hour: 6}, End: timeutil.Clock{Hour: 6}}}, 135 WeekSpans: []timeutil.WeekSpan{ 136 {Start: timeutil.Week{Weekday: time.Monday, Pos: 1}, End: timeutil.Week{Weekday: time.Friday, Pos: 1}}}, 137 }, 138 "mon1-fri1,06:00", 139 }, { 140 timeutil.Schedule{ 141 ClockSpans: []timeutil.ClockSpan{ 142 {Start: timeutil.Clock{Hour: 6}, End: timeutil.Clock{Hour: 6}}}, 143 WeekSpans: []timeutil.WeekSpan{ 144 {Start: timeutil.Week{Weekday: time.Monday, Pos: 5}, 145 End: timeutil.Week{Weekday: time.Monday, Pos: 5}}}, 146 }, 147 "mon5,06:00", 148 }, { 149 timeutil.Schedule{ 150 WeekSpans: []timeutil.WeekSpan{ 151 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 152 }, 153 "mon", 154 }, { 155 timeutil.Schedule{ 156 ClockSpans: []timeutil.ClockSpan{ 157 {Start: timeutil.Clock{Hour: 6}, End: timeutil.Clock{Hour: 9}, Spread: true, Split: 2}}, 158 }, 159 "06:00~09:00/2", 160 }, 161 } { 162 c.Check(t.sched.String(), Equals, t.str) 163 } 164 } 165 166 func (ts *timeutilSuite) TestParseLegacySchedule(c *C) { 167 for _, t := range []struct { 168 in string 169 expected []*timeutil.Schedule 170 errStr string 171 }{ 172 // invalid 173 {"", nil, `cannot parse "": not a valid interval`}, 174 {"invalid-11:00", nil, `cannot parse "invalid": not a valid time`}, 175 {"9:00-11:00/invalid", nil, `cannot parse "invalid": not a valid interval`}, 176 {"09:00-25:00", nil, `cannot parse "25:00": not a valid time`}, 177 {"09:00-24:30", nil, `cannot parse "24:30": not a valid time`}, 178 179 // valid 180 {"9:00-11:00", []*timeutil.Schedule{ 181 {ClockSpans: []timeutil.ClockSpan{ 182 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}, Spread: true}}}, 183 }, ""}, 184 {"9:00-11:00/20:00-22:00", []*timeutil.Schedule{ 185 {ClockSpans: []timeutil.ClockSpan{ 186 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}, Spread: true}}}, 187 {ClockSpans: []timeutil.ClockSpan{ 188 {Start: timeutil.Clock{Hour: 20}, End: timeutil.Clock{Hour: 22}, Spread: true}}}, 189 }, ""}, 190 } { 191 c.Logf("trying: %v", t) 192 schedule, err := timeutil.ParseLegacySchedule(t.in) 193 if t.errStr != "" { 194 c.Check(err, ErrorMatches, t.errStr, Commentf("%q returned unexpected error: %s", t.in, err)) 195 } else { 196 c.Check(err, IsNil, Commentf("%q returned error: %s", t.in, err)) 197 c.Check(schedule, DeepEquals, t.expected, Commentf("%q failed", t.in)) 198 } 199 200 } 201 } 202 203 func parse(c *C, s string) (time.Duration, time.Duration) { 204 l := strings.Split(s, "-") 205 c.Assert(l, HasLen, 2) 206 a, err := time.ParseDuration(l[0]) 207 c.Assert(err, IsNil) 208 b, err := time.ParseDuration(l[1]) 209 c.Assert(err, IsNil) 210 return a, b 211 } 212 213 const ( 214 maxDuration = 60 * 24 * time.Hour 215 ) 216 217 func (ts *timeutilSuite) TestLegacyScheduleNext(c *C) { 218 const shortForm = "2006-01-02 15:04" 219 220 for _, t := range []struct { 221 schedule string 222 last string 223 now string 224 next string 225 }{ 226 { 227 // daily schedule, missed one window 228 // -> run next daily window 229 schedule: "9:00-11:00/21:00-23:00", 230 last: "2017-02-05 22:00", 231 now: "2017-02-06 20:00", 232 next: "1h-3h", 233 }, 234 { 235 // daily schedule, used one window 236 // -> run next daily window 237 schedule: "9:00-11:00/21:00-23:00", 238 last: "2017-02-06 10:00", 239 now: "2017-02-06 20:00", 240 next: "1h-3h", 241 }, 242 { 243 // daily schedule, missed all todays windows 244 // run tomorrow 245 schedule: "9:00-11:00/21:00-22:00", 246 last: "2017-02-04 21:30", 247 now: "2017-02-06 23:00", 248 next: "10h-12h", 249 }, 250 { 251 // single daily schedule, already updated today 252 schedule: "9:00-11:00", 253 last: "2017-02-06 09:30", 254 now: "2017-02-06 10:00", 255 next: "23h-25h", 256 }, 257 { 258 // single daily schedule, already updated today 259 // (at exactly the edge) 260 schedule: "9:00-11:00", 261 last: "2017-02-06 09:00", 262 now: "2017-02-06 09:00", 263 next: "24h-26h", 264 }, 265 { 266 // single daily schedule, last update a day ago 267 // now is within the update window so randomize 268 // (run within remaining time delta) 269 schedule: "9:00-11:00", 270 last: "2017-02-05 09:30", 271 now: "2017-02-06 10:00", 272 next: "0-55m", 273 }, 274 { 275 // multi daily schedule, already updated today 276 schedule: "9:00-11:00/21:00-22:00", 277 last: "2017-02-06 21:30", 278 now: "2017-02-06 23:00", 279 next: "10h-12h", 280 }, 281 { 282 // daily schedule, very small window 283 schedule: "9:00-9:03", 284 last: "2017-02-05 09:02", 285 now: "2017-02-06 08:58", 286 next: "2m-5m", 287 }, 288 { 289 // daily schedule, zero window 290 schedule: "9:00-9:00", 291 last: "2017-02-05 09:02", 292 now: "2017-02-06 08:58", 293 next: "2m-2m", 294 }, 295 } { 296 last, err := time.ParseInLocation(shortForm, t.last, time.Local) 297 c.Assert(err, IsNil) 298 299 fakeNow, err := time.ParseInLocation(shortForm, t.now, time.Local) 300 c.Assert(err, IsNil) 301 restorer := timeutil.MockTimeNow(func() time.Time { 302 return fakeNow 303 }) 304 defer restorer() 305 306 sched, err := timeutil.ParseLegacySchedule(t.schedule) 307 c.Assert(err, IsNil) 308 minDist, maxDist := parse(c, t.next) 309 310 next := timeutil.Next(sched, last, maxDuration) 311 c.Check(next >= minDist && next <= maxDist, Equals, true, Commentf("invalid distance for schedule %q with last refresh %q, now %q, expected %v, got %v", t.schedule, t.last, t.now, t.next, next)) 312 } 313 314 } 315 316 func (ts *timeutilSuite) TestParseSchedule(c *C) { 317 for _, t := range []struct { 318 in string 319 expected []*timeutil.Schedule 320 errStr string 321 }{ 322 // invalid 323 {"", nil, `cannot parse "": not a valid fragment`}, 324 {"invalid-11:00", nil, `cannot parse "invalid-11:00": not a valid time`}, 325 {"9:00-11:00/invalid", nil, `cannot parse "9:00-11:00/invalid": not a valid interval`}, 326 {"9:00-11:00/0", nil, `cannot parse "9:00-11:00/0": not a valid interval`}, 327 {"09:00-25:00", nil, `cannot parse "09:00-25:00": not a valid time`}, 328 {"09:00-24:30", nil, `cannot parse "09:00-24:30": not a valid time`}, 329 {"mon-01:00", nil, `cannot parse "mon-01:00": not a valid time`}, 330 {"9:00-mon@11:00", nil, `cannot parse "9:00-mon@11:00": not a valid time`}, 331 {"9:00,mon", nil, `cannot parse "mon": invalid schedule fragment`}, 332 {"mon~wed", nil, `cannot parse "mon~wed": "mon~wed" is not a valid weekday`}, 333 {"mon--wed", nil, `cannot parse "mon--wed": invalid week span`}, 334 {"mon-wed/2,,9:00", nil, `cannot parse "mon-wed/2": "wed/2" is not a valid weekday`}, 335 {"mon..wed", nil, `cannot parse "mon..wed": "mon..wed" is not a valid weekday`}, 336 {"mon9,9:00", nil, `cannot parse "mon9": "mon9" is not a valid weekday`}, 337 {"mon0,9:00", nil, `cannot parse "mon0": "mon0" is not a valid weekday`}, 338 {"mon5-mon1,9:00", nil, `cannot parse "mon5-mon1": unsupported schedule`}, 339 {"mon%,9:00", nil, `cannot parse "mon%": "mon%" is not a valid weekday`}, 340 {"foo2,9:00", nil, `cannot parse "foo2": "foo2" is not a valid weekday`}, 341 {"9:00---11:00", nil, `cannot parse "9:00---11:00": not a valid time`}, 342 {"9:00-11:00/3/3/3", nil, `cannot parse "9:00-11:00/3/3/3": not a valid interval`}, 343 {"9:00-11:00///3", nil, `cannot parse "9:00-11:00///3": not a valid interval`}, 344 {"9:00-9:00-10:00/3", nil, `cannot parse "9:00-9:00-10:00/3": not a valid time`}, 345 {"9:00,,,9:00-10:00/3", nil, `cannot parse ",9:00-10:00/3": not a valid fragment`}, 346 {",,,", nil, `cannot parse "": not a valid fragment`}, 347 {",,", nil, `cannot parse "": not a valid fragment`}, 348 {":", nil, `cannot parse ":": not a valid time`}, 349 {"-", nil, `cannot parse "-": "" is not a valid weekday`}, 350 {"-/4", nil, `cannot parse "-/4": "" is not a valid weekday`}, 351 {"~/4", nil, `cannot parse "~/4": "~/4" is not a valid weekday`}, 352 // valid 353 { 354 in: "9:00-11:00", 355 expected: []*timeutil.Schedule{{ 356 ClockSpans: []timeutil.ClockSpan{ 357 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}}}}}, 358 }, { 359 in: "9:00-11:00/2", 360 expected: []*timeutil.Schedule{{ 361 ClockSpans: []timeutil.ClockSpan{ 362 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}, Split: 2}}}}, 363 }, { 364 in: "mon,9:00-11:00", 365 expected: []*timeutil.Schedule{{ 366 ClockSpans: []timeutil.ClockSpan{ 367 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}}}, 368 WeekSpans: []timeutil.WeekSpan{ 369 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 370 }}, 371 }, { 372 in: "fri,mon,9:00-11:00", 373 expected: []*timeutil.Schedule{{ 374 ClockSpans: []timeutil.ClockSpan{ 375 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}}}, 376 WeekSpans: []timeutil.WeekSpan{ 377 {Start: timeutil.Week{Weekday: time.Friday}, End: timeutil.Week{Weekday: time.Friday}}, 378 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 379 }}, 380 }, { 381 in: "9:00-11:00,,20:00-22:00", 382 expected: []*timeutil.Schedule{{ 383 ClockSpans: []timeutil.ClockSpan{ 384 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}}}, 385 }, { 386 ClockSpans: []timeutil.ClockSpan{ 387 {Start: timeutil.Clock{Hour: 20}, End: timeutil.Clock{Hour: 22}}}}, 388 }, 389 }, { 390 in: "mon,9:00-11:00,,wed,22:00-23:00", 391 expected: []*timeutil.Schedule{{ 392 ClockSpans: []timeutil.ClockSpan{ 393 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}}}, 394 WeekSpans: []timeutil.WeekSpan{ 395 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 396 }, { 397 ClockSpans: []timeutil.ClockSpan{ 398 {Start: timeutil.Clock{Hour: 22}, End: timeutil.Clock{Hour: 23}}}, 399 WeekSpans: []timeutil.WeekSpan{ 400 {Start: timeutil.Week{Weekday: time.Wednesday}, End: timeutil.Week{Weekday: time.Wednesday}}}, 401 }}, 402 }, { 403 in: "mon,9:00,10:00,14:00,15:00", 404 expected: []*timeutil.Schedule{{ 405 ClockSpans: []timeutil.ClockSpan{ 406 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 9}}, 407 {Start: timeutil.Clock{Hour: 10}, End: timeutil.Clock{Hour: 10}}, 408 {Start: timeutil.Clock{Hour: 14}, End: timeutil.Clock{Hour: 14}}, 409 {Start: timeutil.Clock{Hour: 15}, End: timeutil.Clock{Hour: 15}}}, 410 WeekSpans: []timeutil.WeekSpan{ 411 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 412 }}, 413 }, { 414 in: "mon,wed", 415 expected: []*timeutil.Schedule{{ 416 WeekSpans: []timeutil.WeekSpan{ 417 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}, 418 {Start: timeutil.Week{Weekday: time.Wednesday}, End: timeutil.Week{Weekday: time.Wednesday}}}, 419 }}, 420 }, { 421 // same as above 422 in: "mon,,wed", 423 expected: []*timeutil.Schedule{{ 424 WeekSpans: []timeutil.WeekSpan{ 425 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday}}}, 426 }, { 427 WeekSpans: []timeutil.WeekSpan{ 428 {Start: timeutil.Week{Weekday: time.Wednesday}, End: timeutil.Week{Weekday: time.Wednesday}}}}, 429 }, 430 }, { 431 // but not the same as this one 432 in: "mon-wed", 433 expected: []*timeutil.Schedule{{ 434 WeekSpans: []timeutil.WeekSpan{ 435 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Wednesday}}}, 436 }}, 437 }, { 438 in: "mon-wed,fri,9:00-11:00/2", 439 expected: []*timeutil.Schedule{{ 440 ClockSpans: []timeutil.ClockSpan{ 441 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}, Split: 2}, 442 }, 443 WeekSpans: []timeutil.WeekSpan{ 444 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Wednesday}}, 445 {Start: timeutil.Week{Weekday: time.Friday}, End: timeutil.Week{Weekday: time.Friday}}, 446 }, 447 }}, 448 }, { 449 in: "9:00~11:00", 450 expected: []*timeutil.Schedule{{ 451 ClockSpans: []timeutil.ClockSpan{ 452 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 11}, Spread: true}}, 453 }}, 454 }, { 455 in: "9:00", 456 expected: []*timeutil.Schedule{{ 457 ClockSpans: []timeutil.ClockSpan{ 458 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 9}}}, 459 }}, 460 }, { 461 in: "mon1,9:00", 462 expected: []*timeutil.Schedule{{ 463 ClockSpans: []timeutil.ClockSpan{ 464 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 9}}}, 465 WeekSpans: []timeutil.WeekSpan{ 466 {Start: timeutil.Week{Weekday: time.Monday, Pos: 1}, End: timeutil.Week{Weekday: time.Monday, Pos: 1}}}, 467 }}, 468 }, { 469 in: "00:00-24:00", 470 expected: []*timeutil.Schedule{{ 471 ClockSpans: []timeutil.ClockSpan{ 472 {Start: timeutil.Clock{Hour: 0}, End: timeutil.Clock{Hour: 24}}}, 473 }}, 474 }, { 475 in: "23:00-01:00", 476 expected: []*timeutil.Schedule{{ 477 ClockSpans: []timeutil.ClockSpan{ 478 {Start: timeutil.Clock{Hour: 23}, End: timeutil.Clock{Hour: 1}}, 479 }, 480 }}, 481 }, { 482 in: "fri-mon", 483 expected: []*timeutil.Schedule{{ 484 WeekSpans: []timeutil.WeekSpan{ 485 {Start: timeutil.Week{Weekday: time.Friday}, End: timeutil.Week{Weekday: time.Monday}}}, 486 }}, 487 }, { 488 in: "mon-mon2,9:00", 489 expected: []*timeutil.Schedule{{ 490 ClockSpans: []timeutil.ClockSpan{ 491 {Start: timeutil.Clock{Hour: 9}, End: timeutil.Clock{Hour: 9}}}, 492 WeekSpans: []timeutil.WeekSpan{ 493 {Start: timeutil.Week{Weekday: time.Monday}, End: timeutil.Week{Weekday: time.Monday, Pos: 2}}}, 494 }}, 495 }, 496 } { 497 c.Logf("trying %+v", t) 498 schedule, err := timeutil.ParseSchedule(t.in) 499 if t.errStr != "" { 500 c.Check(err, ErrorMatches, t.errStr, Commentf("%q returned unexpected error: %s", t.in, err)) 501 } else { 502 c.Check(err, IsNil, Commentf("%q returned error: %s", t.in, err)) 503 c.Check(schedule, DeepEquals, t.expected, Commentf("%q failed", t.in)) 504 } 505 } 506 } 507 508 func (ts *timeutilSuite) TestScheduleNext(c *C) { 509 const shortForm = "2006-01-02 15:04" 510 511 for _, t := range []struct { 512 schedule string 513 last string 514 now string 515 next string 516 randomized bool 517 }{ 518 { 519 schedule: "mon,10:00,,fri,15:00", 520 // sun 22:00 521 last: "2017-02-05 22:00", 522 // mon 9:00 523 now: "2017-02-06 9:00", 524 next: "1h-1h", 525 }, { 526 // first monday of the month, at 10:00 527 schedule: "mon1,10:00", 528 // Sun 22:00 529 last: "2017-02-05 22:00", 530 // Mon 9:00 531 now: "2017-02-06 9:00", 532 next: "1h-1h", 533 }, { 534 // first Monday of the month, at 10:00 535 schedule: "mon1,10:00", 536 // first Monday of the month, 10:00 537 last: "2017-02-06 10:00", 538 // first Monday of the month, 11:00, right after 539 // 'previous first Monday' run 540 now: "2017-02-06 11:00", 541 // expecting March, 6th, 10:00, 27 days and 23 hours 542 // from now 543 next: "671h-671h", 544 }, { 545 // second Monday of the month, at 10:00 546 schedule: "mon2,10:00", 547 // first Monday of the month, 10:00 548 last: "2017-02-06 10:00", 549 // first Monday of the month, 11:00, right after 550 // 'previous first Monday' run 551 now: "2017-02-06 11:00", 552 // expecting February, 13, 10:00, 6 days and 23 hours 553 // from now 554 next: "167h-167h", 555 }, { 556 // last Monday of the month, at 10:00 557 schedule: "mon5,10:00", 558 // first Monday of the month, 10:00 559 last: "2017-02-06 10:00", 560 // first Monday of the month, 11:00, right after 561 // 'previous first Monday' run 562 now: "2017-02-06 11:00", 563 // expecting February, 27th, 10:00, 20 days and 23 hours 564 // from now 565 next: "503h-503h", 566 }, { 567 // (deprecated syntax, interpreted as mon1-tue) 568 // from the first Monday of the month to the second Tuesday of 569 // the month, at 10:00 570 schedule: "mon1-tue2,10:00", 571 // Monday, 10:00 572 last: "2017-02-06 10:00", 573 // Tuesday, the day after the first Monday of the month 574 now: "2017-02-07 11:00", 575 // expecting to run on 03.06.2017 576 next: "647h-647h", 577 }, { 578 // from the first Monday of the month to the following Tuesday of 579 // the month, at 10:00 580 schedule: "mon1-tue,10:00", 581 last: "2017-02-01 10:00", 582 // Sunday, 10:00 583 now: "2017-02-05 10:00", 584 // expecting to run the next day at 10:00 585 next: "24h-24h", 586 }, { 587 // from the first Monday of the month to the following Tuesday of 588 // the month, at 10:00 589 schedule: "mon1-tue,10:00", 590 // Tuesday, 10:00 591 last: "2017-02-14 22:00", 592 // Thursday, 10:00 593 now: "2017-02-16 10:00", 594 // expecting to run in 18 days 595 next: "432h-432h", 596 }, { 597 // from the first Monday of the month to the following Tuesday of 598 // the month, at 10:00 599 schedule: "mon1-tue,10:00", 600 // Sunday, 22:00 601 last: "2017-02-05 22:00", 602 // first Monday of the month 603 now: "2017-02-06 11:00", 604 // expecting to run the next day at 10:00 605 next: "23h-23h", 606 }, { 607 // from the first Monday of the month to the following Tuesday of 608 // the month, at 10:00 609 schedule: "mon1-tue,10:00-12:00", 610 // Sunday, 22:00 611 last: "2017-02-05 22:00", 612 // first Monday of the month, within the update window 613 now: "2017-02-06 11:00", 614 // expecting to run now 615 next: "0h-0h", 616 }, { 617 // from the first Monday of the month to the following Tuesday of 618 // the month, at 10:00 619 schedule: "mon1-tue,10:00~12:00", 620 // Sunday, 22:00 621 last: "2017-02-05 22:00", 622 // first Monday of the month, within the update window 623 now: "2017-02-06 11:00", 624 // expecting to run now 625 next: "0h-1h", 626 // since we're in update window we'll run now regardless 627 // of 'spreading' 628 randomized: false, 629 }, { 630 schedule: "mon,10:00~12:00,,fri,15:00", 631 last: "2017-02-05 22:00", 632 now: "2017-02-06 9:00", 633 next: "1h-3h", 634 randomized: true, 635 }, { 636 schedule: "mon,10:00-12:00,,fri,15:00", 637 last: "2017-02-06 12:00", 638 // tue 12:00 639 now: "2017-02-07 12:00", 640 // 3 days and 3 hours from now 641 next: "75h-75h", 642 }, { 643 // randomized between 10:00 and 12:00 644 schedule: "mon,10:00~12:00", 645 // sun 22:00 646 last: "2017-02-05 22:00", 647 // mon 9:00 648 now: "2017-02-06 9:00", 649 next: "1h-3h", 650 randomized: true, 651 }, { 652 // Friday to Monday, 10am 653 schedule: "fri-mon,10:00", 654 // sun 22:00 655 last: "2017-02-05 22:00", 656 // mon 9:00 657 now: "2017-02-06 9:00", 658 next: "1h-1h", 659 }, { 660 // Friday to Monday, 10am 661 schedule: "fri-mon,10:00", 662 // mon 10:00 663 last: "2017-02-06 10:00", 664 // mon 10:00 665 now: "2017-02-06 10:00", 666 // 4 days from now 667 next: "96h-96h", 668 }, { 669 // Wednesday to Friday, 10am 670 schedule: "wed-fri,10:00", 671 // mon 10:00 672 last: "2017-02-06 10:00", 673 // mon 10:00 674 now: "2017-02-06 10:00", 675 // 2 days from now 676 next: "48h-48h", 677 }, { 678 // randomized, once a day 679 schedule: "0:00~24:00", 680 // sun 22:00 681 last: "2017-02-05 22:00", 682 // mon 9:00 683 now: "2017-02-05 23:00", 684 next: "1h-25h", 685 randomized: true, 686 }, { 687 // randomized, once a day 688 schedule: "0:00~24:00", 689 // mon 10:00 690 last: "2017-02-06 10:00", 691 // mon 11:00 692 now: "2017-02-06 11:00", 693 // sometime the next day 694 next: "13h-37h", 695 randomized: true, 696 }, { 697 // during the night, 23:00-1:00 698 schedule: "23:00~1:00", 699 // mon 10:00 700 last: "2017-02-06 10:00", 701 // mon 11:00 702 now: "2017-02-06 22:00", 703 // sometime over the night 704 next: "1h-3h", 705 randomized: true, 706 }, { 707 // during the night, 23:00-1:00 708 schedule: "23:00~1:00", 709 // Mon 23:00 710 last: "2017-02-06 23:00", 711 // Tue 0:00 712 now: "2017-02-07 00:00", 713 // sometime over the night 714 next: "23h-25h", 715 randomized: true, 716 }, { 717 // twice between 9am and 11am 718 schedule: "9:00-11:00/2", 719 // last attempt at the beginning of window 720 last: "2017-02-06 9:00", 721 // sometime between 10am and 11am 722 now: "2017-02-06 9:30", 723 next: "30m-90m", 724 }, { 725 // 2 ranges 726 schedule: "9:00-10:00,10:00-11:00", 727 // last attempt at the beginning of window 728 last: "2017-02-06 9:01", 729 // next one at 10am 730 now: "2017-02-06 9:30", 731 next: "30m-30m", 732 }, { 733 // twice, at 9am and at 2pm 734 schedule: "9:00,14:00", 735 // last right after scheduled time window 736 last: "2017-02-06 9:01", 737 // next one at 2pm 738 now: "2017-02-06 9:30", 739 next: "270m-270m", 740 }, { 741 // 2 ranges, reversed order in spec 742 schedule: "10:00~11:00,9:00-10:00", 743 // last attempt at the beginning of window 744 last: "2017-02-06 9:01", 745 // sometime between 10am and 11am 746 now: "2017-02-06 9:30", 747 next: "30m-90m", 748 randomized: true, 749 }, { 750 // first Wednesday at 13:00 751 schedule: "wed1,13:00", 752 now: "2018-07-30 9:00", 753 // yesterday 754 last: "2018-07-29 13:00", 755 // next one on 2018-08-01 13:00 756 next: "52h-52h", 757 }, { 758 // October 2019 759 // Su Mo Tu We Th Fr Sa 760 // 29 30| 1 2 3 4 5 761 // 6 7 8 9 10 11 12 762 // 13 14 15 16 17 18 19 763 // 20 21 22 23 24 25 26 764 // 27 28 29 30 31 765 766 // first Monday to the following Wednesday of the month, in Oct 767 // 2019, matches 07.10-09.10 768 schedule: "mon1-wed,9:00-13:00", 769 now: "2019-09-30 9:00", 770 // yesterday 771 last: "2019-09-30 9:00", 772 // next one on 2019-10-07 9:00 773 next: "168h-168h", 774 }, { 775 // first Monday to the following Wednesday of the month, in Oct 776 // 2019, matches 30.09-04.10 777 schedule: "mon-fri1,9:00-13:00", 778 now: "2019-09-29 9:00", 779 last: "2019-09-29 9:00", 780 // next one on 2019-09-30 9:00 781 next: "24h-24h", 782 }, { 783 // most trivial case 784 schedule: "21:00-22:00", 785 now: "2019-09-29 8:00", 786 last: "2019-09-28 21:05", 787 // next one on 2019-09-29 at 21:00 788 next: "13h-13h", 789 }, 790 } { 791 c.Logf("trying %+v", t) 792 793 last, err := time.ParseInLocation(shortForm, t.last, time.Local) 794 c.Assert(err, IsNil) 795 796 fakeNow, err := time.ParseInLocation(shortForm, t.now, time.Local) 797 c.Assert(err, IsNil) 798 restorer := timeutil.MockTimeNow(func() time.Time { 799 return fakeNow 800 }) 801 defer restorer() 802 803 sched, err := timeutil.ParseSchedule(t.schedule) 804 c.Assert(err, IsNil) 805 806 // keep track of previous result for tests where event time is 807 // randomized 808 previous := time.Duration(0) 809 calls := 2 810 811 for i := 0; i < calls; i++ { 812 next := timeutil.Next(sched, last, maxDuration) 813 if t.randomized { 814 c.Check(next, Not(Equals), previous) 815 } else if previous != 0 { 816 // not randomized and not the first run 817 c.Check(next, Equals, previous) 818 } 819 820 c.Logf("next: %v", next) 821 minDist, maxDist := parse(c, t.next) 822 823 c.Check(next >= minDist && next <= maxDist, 824 Equals, true, 825 Commentf("invalid distance for schedule %q with last refresh %q, now %q, expected %v, got %v, date %s", 826 t.schedule, t.last, t.now, t.next, next, fakeNow.Add(next))) 827 previous = next 828 } 829 } 830 } 831 832 func (ts *timeutilSuite) TestScheduleIncludes(c *C) { 833 const shortForm = "2006-01-02 15:04:05" 834 835 for _, t := range []struct { 836 schedule string 837 now string 838 expecting bool 839 }{ 840 { 841 schedule: "mon,10:00,,fri,15:00", 842 // mon 9:00 843 now: "2017-02-06 9:00:00", 844 expecting: false, 845 }, { 846 // first monday of the month, at 10:00 847 schedule: "mon1,10:00", 848 // Mon 10:00:00 849 now: "2017-02-06 10:00:00", 850 expecting: true, 851 }, { 852 // first monday of the month, at 10:00 853 schedule: "mon1,10:00", 854 // Mon 10:00:45 855 now: "2017-02-06 10:00:45", 856 expecting: true, 857 }, { 858 // first monday of the month, at 10:00 859 schedule: "mon1,10:00", 860 // Mon 10:01 861 now: "2017-02-06 10:01:00", 862 expecting: false, 863 }, { 864 // last Monday of the month, at 10:00 865 schedule: "mon5,10:00-11:00", 866 // first Monday of the month, 11:00, right after 867 // 'previous first Monday' run 868 now: "2017-02-27 10:59:20", 869 expecting: true, 870 }, { 871 // (deprecated syntax) 872 // from first Monday of the month to the second Tuesday of 873 // the month, at 10:00 to 12:00 874 schedule: "mon1-tue2,10:00-12:00", 875 // Thursday, 11:10 876 now: "2017-02-09 11:10:00", 877 expecting: false, 878 }, { 879 // from first Monday of the month to the following Tuesday of 880 // the month, at 10:00 to 12:00 881 schedule: "mon1-tue,10:00~12:00", 882 // Thursday, 11:10 883 now: "2017-02-02 11:10:00", 884 expecting: false, 885 }, { 886 // from first Monday of the month to the following Tuesday of 887 // the month, at 10:00 to 12:00 888 schedule: "mon1-tue,10:00~12:00", 889 // Monday, 11:10 890 now: "2017-02-06 11:10:00", 891 expecting: true, 892 }, { 893 // from first Monday of the month to the following Tuesday of 894 // the month, at 10:00 to 12:00 895 schedule: "mon1-tue,10:00~12:00", 896 // Thursday, 11:10 897 now: "2017-02-16 11:10:00", 898 expecting: false, 899 }, { 900 // from first Monday of the month to the following Tuesday of 901 // the month, at 10:00 to 12:00 902 schedule: "mon1-tue,10:00~12:00", 903 // Thursday, 11:10 904 now: "2017-03-06 11:10:00", 905 expecting: true, 906 }, { 907 // from first Monday of the month to the following Tuesday of 908 // the month, at 10:00 to 12:00 909 schedule: "mon1-tue,10:00~12:00", 910 // Thursday, 11:10 911 now: "2017-02-09 11:10:00", 912 expecting: false, 913 }, { 914 // from first Tuesday of the month to the following Monday of 915 // the month, at 10:00 to 12:00 916 schedule: "tue1-mon,10:00~12:00", 917 // Thursday, 11:10 918 now: "2017-02-09 11:10:00", 919 expecting: true, 920 }, { 921 // (deprecated syntax) 922 // from 4th Monday of the month to the following Wednesday of 923 // the month, at 10:00 to 12:00 924 schedule: "mon4-wed5,10:00~12:00", 925 // Schedule ends up being Feb 27 - Mar 01 2017 926 now: "2017-03-02 11:10:00", 927 expecting: false, 928 }, { 929 // from last Monday of the month to the following Wednesday of 930 // the month, at 10:00 to 12:00 931 schedule: "mon5-wed,10:00~12:00", 932 // Schedule ends up being Feb 27 - Mar 01 2017 933 now: "2017-03-01 11:10:00", 934 expecting: true, 935 }, { 936 // from last Monday of the month to the following Wednesday of 937 // the month, at 10:00 to 12:00 938 schedule: "mon5-wed,10:00~12:00", 939 // Schedule ends up being Feb 27 - Mar 01 2017 940 now: "2017-03-02 11:10:00", 941 expecting: false, 942 }, { 943 // (deprecated syntax) 944 // from last Monday of the month to the following Tuesday of 945 // the month, at 10:00 946 schedule: "mon1-tue2,10:00~12:00", 947 // Sunday, 11:10 948 now: "2017-02-05 11:10:00", 949 expecting: false, 950 }, { 951 // twice between 9am and 11am 952 schedule: "9:00-11:00/2", 953 now: "2017-02-06 10:30:00", 954 expecting: true, 955 }, { 956 schedule: "9:00-10:00,10:00-11:00", 957 now: "2017-02-06 10:30:00", 958 expecting: true, 959 }, { 960 // every day, 23:59 961 schedule: "23:59", 962 now: "2017-02-06 23:59:59", 963 expecting: true, 964 }, { 965 // 2 ranges, reversed order in spec 966 schedule: "10:00~11:00,9:00-10:00", 967 // sometime between 10am and 11am 968 now: "2017-02-06 9:30:00", 969 expecting: true, 970 }, { 971 schedule: "mon1-wed,9:00-10:00", 972 // Tue, 9:30 973 now: "2019-10-08 9:30:00", 974 expecting: true, 975 }, { 976 schedule: "tue1,9:00-10:00", 977 // Tue, 9:30 978 now: "2019-10-01 9:30:00", 979 expecting: true, 980 }, 981 } { 982 c.Logf("trying %+v", t) 983 984 now, err := time.ParseInLocation(shortForm, t.now, time.Local) 985 c.Assert(err, IsNil) 986 987 sched, err := timeutil.ParseSchedule(t.schedule) 988 c.Assert(err, IsNil) 989 990 c.Check(timeutil.Includes(sched, now), Equals, t.expecting, 991 Commentf("unexpected result for schedule %v and time %v", t.schedule, now)) 992 } 993 } 994 995 func (ts *timeutilSuite) TestClockSpans(c *C) { 996 const shortForm = "2006-01-02 15:04:05" 997 998 for _, t := range []struct { 999 clockspan string 1000 flattenend []string 1001 }{ 1002 { 1003 clockspan: "23:00-01:00/2", 1004 flattenend: []string{"23:00-00:00", "00:00-01:00"}, 1005 }, { 1006 clockspan: "23:00-01:00/4", 1007 flattenend: []string{"23:00-23:30", "23:30-00:00", "00:00-00:30", "00:30-01:00"}, 1008 }, 1009 } { 1010 c.Logf("trying %+v", t) 1011 spans, err := timeutil.ParseClockSpan(t.clockspan) 1012 c.Assert(err, IsNil) 1013 1014 spanStrings := make([]string, len(t.flattenend)) 1015 flattened := spans.ClockSpans() 1016 c.Assert(flattened, HasLen, len(t.flattenend)) 1017 for i := range flattened { 1018 spanStrings[i] = flattened[i].String() 1019 } 1020 1021 c.Assert(spanStrings, DeepEquals, t.flattenend) 1022 } 1023 } 1024 1025 func (ts *timeutilSuite) TestWeekSpans(c *C) { 1026 const shortForm = "2006-01-02" 1027 1028 // July 2018 August 2018 1029 // Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1030 // 1 2 3 4 5 6 7 1 2 3 4 1031 // 8 9 10 11 12 13 14 5 6 7 8 9 10 11 1032 // 15 16 17 18 19 20 21 12 13 14 15 16 17 18 1033 // 22 23 24 25 26 27 28 19 20 21 22 23 24 25 1034 // 29 30 31 26 27 28 29 30 31 1035 1036 for _, t := range []struct { 1037 week string 1038 when string 1039 match bool 1040 }{ 1041 { 1042 // first Wednesday 1043 week: "wed1", 1044 when: "2018-08-01", 1045 match: true, 1046 }, { 1047 // first Wednesday 1048 week: "wed1", 1049 // actually 2nd Wednesday 1050 when: "2018-08-08", 1051 match: false, 1052 }, { 1053 // second Wednesday 1054 week: "wed2", 1055 when: "2018-08-08", 1056 match: true, 1057 }, { 1058 // first Tuesday 1059 week: "tue1", 1060 when: "2018-08-07", 1061 match: true, 1062 }, { 1063 // first Sunday 1064 week: "sun1", 1065 when: "2018-07-01", 1066 match: true, 1067 }, { 1068 // last Tuesday 1069 week: "tue5", 1070 when: "2018-07-31", 1071 match: true, 1072 }, { 1073 // last Tuesday 1074 week: "tue5", 1075 when: "2018-07-24", 1076 match: false, 1077 }, { 1078 // last Thursday 1079 week: "thu5", 1080 when: "2018-07-26", 1081 match: true, 1082 }, { 1083 // using deprecated syntax 1084 // first Monday (06.08) to first Friday (03.08), see August calendar above 1085 // includes: 01.08-03.08 and 06.08-07.08 1086 week: "mon1-fri1", 1087 // Wednesday 1088 when: "2018-08-01", 1089 match: false, 1090 }, { 1091 // using deprecated syntax 1092 // first Monday (06.08) to first Friday (03.08), see August calendar above 1093 week: "mon1-fri", 1094 // Tuesday 1095 when: "2018-08-07", 1096 match: true, 1097 }, { 1098 // first Monday (06.08) to first Friday (03.08), see August calendar above 1099 week: "mon1-fri", 1100 // Thursday 1101 when: "2018-08-08", 1102 match: true, 1103 }, { 1104 // second Monday (13.08) to second Friday (10.08), see August calendar above 1105 // includes: 13.08-14.08 and 08.08-10.08 1106 week: "mon2-fri", 1107 // Thursday 1108 when: "2018-08-13", 1109 match: true, 1110 }, { 1111 // second Monday (13.08) to second Friday (10.08), see August calendar above 1112 week: "mon2-fri", 1113 // Thursday 1114 when: "2018-08-13", 1115 match: true, 1116 }, { 1117 // first Friday (03.08) to the following Monday (06.08), see August calendar above 1118 // includes: 03.08-06.08 1119 week: "fri1-mon", 1120 // Saturday 1121 when: "2018-08-04", 1122 match: true, 1123 }, { 1124 // first Friday (06.07) to the following Monday (09.07), see July calendar above 1125 // includes: 03.07-09.07 1126 week: "fri1-mon", 1127 // Sunday 1128 when: "2018-07-08", 1129 match: true, 1130 }, { 1131 // first Friday (03.08) to the preceding Monday (30.07), see July. August calendar above 1132 // includes: 30.07-03.08 1133 week: "mon-fri1", 1134 // Saturday 1135 when: "2018-08-01", 1136 match: true, 1137 }, { 1138 // first Friday (03.08) to the preceding Monday (30.07), see July. August calendar above 1139 // includes: 30.07-03.08 1140 week: "mon-fri1", 1141 // Saturday 1142 when: "2018-07-30", 1143 match: true, 1144 }, { 1145 // 4th Friday (27.08) to the following Monday (02.08), see July. August calendar above 1146 // includes: 27.07-02.08 1147 week: "fri4-thu", 1148 // Saturday 1149 when: "2018-08-01", 1150 match: true, 1151 }, { 1152 // using deprecated syntax 1153 // first Friday (06.07) to the following Monday (09.07), see July calendar above 1154 // includes: 03.07-09.07 1155 week: "fri1-mon1", 1156 // Sunday 1157 when: "2018-07-08", 1158 match: true, 1159 }, { 1160 // first Friday (06.07) to the following Monday (09.07), see July calendar above 1161 // includes: 06.07-09.07 1162 week: "fri1-mon", 1163 // Sunday 1164 when: "2018-07-15", 1165 match: false, 1166 }, { 1167 // last Monday (30.07) to the following Friday (03.07), see July calendar above 1168 // includes: 03.07-03.08 1169 week: "mon5-fri", 1170 // Sunday 1171 when: "2018-07-31", 1172 match: true, 1173 }, { 1174 // last Friday (27.07) to the preceding Monday (23.07), see July calendar above 1175 // includes: 23.07-27.07 1176 week: "mon-fri5", 1177 // Sunday 1178 when: "2018-07-28", 1179 match: false, 1180 }, { 1181 // last Friday (27.07) to the preceding Monday (23.07), see July calendar above 1182 // includes: 23.07-27.07 1183 week: "mon-fri5", 1184 // Sunday 1185 when: "2018-07-25", 1186 match: true, 1187 }, { 1188 // first Monday (2.07) to the following Monday (9.07), see July calendar above 1189 // includes: 2.07-9.07 1190 week: "mon1-mon", 1191 // Tuesday 1192 when: "2018-07-03", 1193 match: true, 1194 }, { 1195 week: "mon1-mon", 1196 // Monday (the farther edge of the span) 1197 when: "2018-07-09", 1198 match: true, 1199 }, { 1200 week: "mon1-mon", 1201 // Tuesday 1202 when: "2018-07-10", 1203 match: false, 1204 }, 1205 } { 1206 c.Logf("trying %+v", t) 1207 ws, err := timeutil.ParseWeekSpan(t.week) 1208 c.Assert(err, IsNil) 1209 1210 when, err := time.ParseInLocation(shortForm, t.when, time.Local) 1211 c.Assert(err, IsNil) 1212 c.Logf("when: %v %s", when, when.Weekday()) 1213 1214 c.Check(ws.Match(when), Equals, t.match) 1215 } 1216 } 1217 1218 func (ts *timeutilSuite) TestTimeZero(c *C) { 1219 // test with a zero time stamp to make sure that code does not do 1220 // anything silly 1221 1222 // zero time is: time is: 0001-01-01 00:00:00 +0000 UTC and ... Monday 1223 zero := time.Time{} 1224 c.Logf("time is: %v weekday: %v", zero, zero.Weekday()) 1225 1226 for _, schedule := range []string{ 1227 "mon-tue,0:00-12:00", 1228 "mon1-tue,0:00-12:00", 1229 "mon-tue1,0:00-12:00", 1230 } { 1231 c.Logf("trying: %v", schedule) 1232 sch, err := timeutil.ParseSchedule(schedule) 1233 c.Assert(err, IsNil) 1234 1235 c.Check(timeutil.Includes(sch, zero), Equals, true) 1236 c.Check(timeutil.Includes(sch, zero.Add(5*time.Hour)), Equals, true) 1237 // wednesday 1238 c.Check(timeutil.Includes(sch, zero.Add(2*24*time.Hour)), Equals, false) 1239 } 1240 } 1241 1242 func (ts *timeutilSuite) TestMonthNext(c *C) { 1243 const shortForm = "2006-01-02" 1244 for _, t := range []struct { 1245 when, next string 1246 }{ 1247 {"2018-07-01", "2018-08-01"}, 1248 {"2018-07-31", "2018-08-01"}, 1249 {"2018-07-20", "2018-08-01"}, 1250 {"2018-02-01", "2018-03-01"}, 1251 {"2018-02-28", "2018-03-01"}, 1252 {"2018-01-31", "2018-02-01"}, 1253 // in 2020 Feb is 29 days 1254 {"2020-01-31", "2020-02-01"}, 1255 {"2020-02-01", "2020-03-01"}, 1256 {"2020-02-14", "2020-03-01"}, 1257 } { 1258 when, err := time.ParseInLocation(shortForm, t.when, time.Local) 1259 c.Assert(err, IsNil) 1260 c.Logf("when: %v expecting: %v", when, t.next) 1261 1262 next := timeutil.MonthNext(when) 1263 c.Check(next.Format(shortForm), Equals, t.next) 1264 } 1265 1266 }