67 lines
1.9 KiB
Go
67 lines
1.9 KiB
Go
package app
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
|
)
|
|
|
|
func eligibleDailyDates(hourly *weatherdata.ForecastRun, now time.Time, location *time.Location) []time.Time {
|
|
if hourly == nil || location == nil || hourly.Product != "hourly" || len(hourly.Periods) == 0 {
|
|
return nil
|
|
}
|
|
|
|
hourlyStarts := make(map[time.Time]struct{}, len(hourly.Periods))
|
|
var maxLocalDate time.Time
|
|
for _, period := range hourly.Periods {
|
|
if !isHourlyPeriod(period) {
|
|
continue
|
|
}
|
|
start := period.StartTime
|
|
hourlyStarts[instantKey(start)] = struct{}{}
|
|
localDate := localDateStart(start, location)
|
|
if maxLocalDate.IsZero() || localDate.After(maxLocalDate) {
|
|
maxLocalDate = localDate
|
|
}
|
|
}
|
|
if len(hourlyStarts) == 0 || maxLocalDate.IsZero() {
|
|
return nil
|
|
}
|
|
|
|
startDate := localDateStart(now.In(location).AddDate(0, 0, 2), location)
|
|
var dates []time.Time
|
|
for candidate := startDate; !candidate.After(maxLocalDate); candidate = candidate.AddDate(0, 0, 1) {
|
|
if hasFullHourlyCoverage(candidate, location, hourlyStarts) {
|
|
dates = append(dates, candidate)
|
|
}
|
|
}
|
|
return dates
|
|
}
|
|
|
|
func isHourlyPeriod(period weatherdata.ForecastPeriod) bool {
|
|
if period.StartTime.IsZero() || period.EndTime.IsZero() {
|
|
return false
|
|
}
|
|
return period.EndTime.Equal(period.StartTime.Add(time.Hour))
|
|
}
|
|
|
|
func hasFullHourlyCoverage(date time.Time, location *time.Location, hourlyStarts map[time.Time]struct{}) bool {
|
|
day := timeutil.CivilDay(date, location)
|
|
for required := day.Start; required.Before(day.End); required = required.Add(time.Hour) {
|
|
if _, ok := hourlyStarts[instantKey(required)]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func instantKey(value time.Time) time.Time {
|
|
return value.UTC()
|
|
}
|
|
|
|
func localDateStart(value time.Time, location *time.Location) time.Time {
|
|
local := value.In(location)
|
|
return time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, location)
|
|
}
|