Sybren A. Stüvel d7b164133a Sleep Scheduler implementation for the Manager
The Manager now has a sleep scheduler for Workers. The API and background
service work, but there is no web interface yet.

Manifest Task: T99397
2022-07-17 17:27:32 +02:00

110 lines
2.9 KiB
Go

package persistence
// SPDX-License-Identifier: GPL-3.0-or-later
import (
"database/sql/driver"
"fmt"
"time"
)
const (
timeOfDayStringFormat = "%02d:%02d"
// Assigned to the Hour and Minute fields to indicate "no value".
timeOfDayNoValue = -1
)
// TimeOfDay represents a time of day, and can be converted to/from a string.
// Its date and timezone components are ignored, and the time is supposed to be
// interpreted as local time on any date (f.e. a scheduled sleep time of some
// Worker on a certain day-of-week & local timezone).
//
// TimeOfDay structs can also represent "no value", which will be marshaled as
// an empty string.
type TimeOfDay struct {
Hour int
Minute int
}
// MakeTimeOfDay converts a time.Time into a TimeOfDay.
func MakeTimeOfDay(someTime time.Time) TimeOfDay {
return TimeOfDay{someTime.Hour(), someTime.Minute()}
}
// EmptyTimeOfDay returns a TimeOfDay struct with no value.
// See `TimeOfDay.HasValue()`.
func EmptyTimeOfDay() TimeOfDay {
return TimeOfDay{Hour: timeOfDayNoValue, Minute: timeOfDayNoValue}
}
// Value converts a TimeOfDay to a value usable by SQL databases.
func (ot TimeOfDay) Value() (driver.Value, error) {
var asString = ot.String()
return asString, nil
}
// Scan updates this TimeOfDay from the value stored in a database.
func (ot *TimeOfDay) Scan(value interface{}) error {
b, ok := value.(string)
if !ok {
return fmt.Errorf("expected string, received %T", value)
}
return ot.setString(string(b))
}
// Equals returns True iff both times represent the same time of day.
func (ot TimeOfDay) Equals(other TimeOfDay) bool {
return ot.Hour == other.Hour && ot.Minute == other.Minute
}
// IsBefore returns True iff ot is before other.
// Ignores everything except hour and minute fields.
func (ot TimeOfDay) IsBefore(other TimeOfDay) bool {
if ot.Hour != other.Hour {
return ot.Hour < other.Hour
}
return ot.Minute < other.Minute
}
// IsAfter returns True iff ot is after other.
// Ignores everything except hour and minute fields.
func (ot TimeOfDay) IsAfter(other TimeOfDay) bool {
if ot.Hour != other.Hour {
return ot.Hour > other.Hour
}
return ot.Minute > other.Minute
}
// OnDate returns the time of day in the local timezone on the given date.
func (ot TimeOfDay) OnDate(date time.Time) time.Time {
year, month, day := date.Date()
return time.Date(year, month, day, ot.Hour, ot.Minute, 0, 0, time.Local)
}
func (ot TimeOfDay) String() string {
if !ot.HasValue() {
return ""
}
return fmt.Sprintf(timeOfDayStringFormat, ot.Hour, ot.Minute)
}
func (ot TimeOfDay) HasValue() bool {
return ot.Hour != timeOfDayNoValue && ot.Minute != timeOfDayNoValue
}
func (ot *TimeOfDay) setString(value string) error {
scanned := TimeOfDay{}
if value == "" {
*ot = TimeOfDay{timeOfDayNoValue, timeOfDayNoValue}
return nil
}
_, err := fmt.Sscanf(value, timeOfDayStringFormat, &scanned.Hour, &scanned.Minute)
if err != nil {
return err
}
*ot = scanned
return nil
}