173 lines
3.9 KiB
Go
173 lines
3.9 KiB
Go
|
|
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))
|
|||
|
|
}
|