Files
qb/mqe/period_calculator.go
2026-06-19 07:25:46 +03:00

173 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mqe
import (
"fmt"
"time"
"gordenko.dev/dima/qb/timeutil"
)
type PeriodCalculatorOptions struct {
GroupBy GroupBy
LastDayOfMonth int
FirstHourOfDay int
Since time.Time
Until time.Time
}
func NewPeriodCalculator(opt PeriodCalculatorOptions) PeriodCalculator {
switch opt.GroupBy {
case ByHour, ByDay, ByMonth:
// pass
default:
panic(fmt.Sprintf("bug: unknown groupBy %q", opt.GroupBy))
}
s := PeriodCalculator{
groupBy: opt.GroupBy,
lastDayOfMonth: opt.LastDayOfMonth,
firstHourOfDay: opt.FirstHourOfDay,
}
s.sincePeriod = s.TimeToPeriod(opt.Since)
s.untilPeriod = s.TimeToPeriod(opt.Until)
return s
}
type PeriodCalculator struct {
groupBy GroupBy
lastDayOfMonth int
firstHourOfDay int
sincePeriod time.Time
untilPeriod time.Time
}
func (s PeriodCalculator) IsExtendedSincePeriod(period time.Time) bool {
return period.Before(s.sincePeriod)
}
func (s PeriodCalculator) IsExtendedUntilPeriod(period time.Time) bool {
return period.After(s.untilPeriod)
}
func (s PeriodCalculator) NextPeriod(period time.Time) time.Time {
switch s.groupBy {
case ByHour:
return period.Add(time.Hour)
case ByDay:
return period.AddDate(0, 0, 1)
case ByMonth:
return period.AddDate(0, 1, 0)
default:
panic(fmt.Sprintf("bug: unknown groupBy %q", s.groupBy))
}
}
func (s PeriodCalculator) IsPeriodCorrectEnds(period time.Time, lastMeasureTime int64) bool {
// по умолчанию
tm := timeutil.LastSecondInPeriod(period, string(s.groupBy))
switch s.groupBy {
case ByDay:
if s.firstHourOfDay > 0 {
// Было 23:59, стало - 01:59
tm = tm.Add(time.Duration(s.firstHourOfDay) * time.Hour)
}
case ByMonth:
if s.lastDayOfMonth == 0 {
if s.firstHourOfDay > 0 {
// Было 23:59, стало - 01:59
tm = tm.Add(time.Duration(s.firstHourOfDay) * time.Hour)
}
} else {
if s.firstHourOfDay > 0 {
// last day 28, first hour 2 превращается в day 29 01:59:59
tm = time.Date(
tm.Year(),
tm.Month(),
s.lastDayOfMonth+1,
s.firstHourOfDay-1,
59,
59,
0,
tm.Location(),
)
} else {
// last day 23:59:59
tm = time.Date(
tm.Year(),
tm.Month(),
s.lastDayOfMonth,
23,
59,
59,
0,
tm.Location(),
)
}
}
}
timestamp := tm.Add(-179 * time.Second).Unix()
if lastMeasureTime >= timestamp && lastMeasureTime <= tm.Unix() {
return true
}
//fmt.Printf("Ends: lastMeasureTime=%s, timetsamp=%s\n", time.Unix(lastMeasureTime, 0), time.Unix(timestamp, 0))
return false
}
func (s PeriodCalculator) TimeToPeriod(tm time.Time) (period time.Time) {
switch s.groupBy {
case ByDay:
if s.firstHourOfDay > 0 {
if tm.Hour() < s.firstHourOfDay {
tm = tm.AddDate(0, 0, -1)
}
}
case ByMonth:
if s.lastDayOfMonth > 0 {
if s.lastDayOfMonth > 28 {
// На всякий случай добавляем защиту от дурака, ибо модификация даты
// в запросе (+1 месяц) для 29, 30 и 31 чисел сделает запрос неверным.
panic(fmt.Sprintf("LastDayOfMonth: %d, max allowed value is 28", s.lastDayOfMonth))
}
if s.firstHourOfDay > 0 {
if tm.Hour() >= s.firstHourOfDay {
if tm.Day() > s.lastDayOfMonth {
// add 1 month
tm = tm.AddDate(0, 1, 0)
}
} else {
// firstHourOfDay показывает что текущий день еще не наступил,
// поэтому day - 1
if (tm.Day() - 1) <= s.lastDayOfMonth {
tm = tm.AddDate(0, 0, -1)
} else {
// - 1 day, + 1 month
tm = tm.AddDate(0, 0, -1).AddDate(0, 1, 0)
}
}
} else {
if tm.Day() > s.lastDayOfMonth {
tm = tm.AddDate(0, 1, 0)
}
}
} else {
if s.firstHourOfDay > 0 {
if tm.Hour() < s.firstHourOfDay {
tm = tm.AddDate(0, 0, -1)
}
}
}
}
return timeutil.FirstSecondInPeriod(tm, string(s.groupBy))
}