Sybren A. Stüvel
eb9f46dc9b
The sleep scheduler now always works in the local time zone. This fixes the sleep scheduler part of #104219.
105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
package sleep_scheduler
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"projects.blender.org/studio/flamenco/internal/manager/persistence"
|
|
"projects.blender.org/studio/flamenco/pkg/api"
|
|
)
|
|
|
|
// scheduledWorkerStatus returns the expected worker status at the given date/time.
|
|
func scheduledWorkerStatus(now time.Time, sched *persistence.SleepSchedule) api.WorkerStatus {
|
|
if sched == nil {
|
|
// If there is no schedule at all, the worker should be awake.
|
|
return api.WorkerStatusAwake
|
|
}
|
|
|
|
tod := persistence.MakeTimeOfDay(now)
|
|
|
|
if !sched.IsActive {
|
|
return api.WorkerStatusAwake
|
|
}
|
|
|
|
if sched.DaysOfWeek != "" {
|
|
weekdayName := strings.ToLower(now.Weekday().String()[:2])
|
|
if !strings.Contains(sched.DaysOfWeek, weekdayName) {
|
|
// There are days configured, and today is not a sleeping day.
|
|
return api.WorkerStatusAwake
|
|
}
|
|
}
|
|
|
|
beforeStart := sched.StartTime.HasValue() && tod.IsBefore(sched.StartTime)
|
|
afterEnd := sched.EndTime.HasValue() && !tod.IsBefore(sched.EndTime)
|
|
|
|
if beforeStart || afterEnd {
|
|
// Outside sleeping time.
|
|
return api.WorkerStatusAwake
|
|
}
|
|
|
|
return api.WorkerStatusAsleep
|
|
}
|
|
|
|
func cleanupDaysOfWeek(daysOfWeek string) string {
|
|
trimmed := strings.TrimSpace(daysOfWeek)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
|
|
daynames := strings.Fields(trimmed)
|
|
for idx, name := range daynames {
|
|
daynames[idx] = strings.ToLower(strings.TrimSpace(name))[:2]
|
|
}
|
|
return strings.Join(daynames, " ")
|
|
}
|
|
|
|
// Return a timestamp when the next scheck for this schedule is due.
|
|
func calculateNextCheck(now time.Time, schedule *persistence.SleepSchedule) time.Time {
|
|
// calcNext returns the given time of day on "today" if that hasn't passed
|
|
// yet, otherwise on "tomorrow".
|
|
calcNext := func(tod persistence.TimeOfDay) time.Time {
|
|
nextCheck := tod.OnDate(now).In(time.Local)
|
|
if nextCheck.Before(now) {
|
|
nextCheck = nextCheck.AddDate(0, 0, 1)
|
|
}
|
|
return nextCheck
|
|
}
|
|
|
|
nextChecks := []time.Time{
|
|
// Always check at the end of the day.
|
|
endOfDay(now),
|
|
}
|
|
|
|
// No start time means "start of the day", which is already covered by
|
|
// yesterday's "end of the day" check.
|
|
if schedule.StartTime.HasValue() {
|
|
nextChecks = append(nextChecks, calcNext(schedule.StartTime))
|
|
}
|
|
// No end time means "end of the day", which is already covered by today's
|
|
// "end of the day" check.
|
|
if schedule.EndTime.HasValue() {
|
|
nextChecks = append(nextChecks, calcNext(schedule.EndTime))
|
|
}
|
|
|
|
next := earliestTime(nextChecks)
|
|
return next
|
|
}
|
|
|
|
func earliestTime(timestamps []time.Time) time.Time {
|
|
earliest := timestamps[0]
|
|
for _, timestamp := range timestamps[1:] {
|
|
if timestamp.Before(earliest) {
|
|
earliest = timestamp
|
|
}
|
|
}
|
|
return earliest
|
|
}
|
|
|
|
// endOfDay returns the next midnight at UTC.
|
|
func endOfDay(now time.Time) time.Time {
|
|
startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
|
return startOfDay.AddDate(0, 0, 1)
|
|
}
|