break intervals out
This commit is contained in:
parent
9dc6019c17
commit
ef1784c806
6 changed files with 125 additions and 73 deletions
84
internal/common/intervals.go
Normal file
84
internal/common/intervals.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DaysInWeek = 7
|
||||
MonthsInQuarter = 3
|
||||
)
|
||||
|
||||
type TimeBounder interface {
|
||||
GetDailyBounds(date time.Time) (lowerBound, upperBound time.Time)
|
||||
GetWeeklyBounds(date time.Time) (lowerBound, upperBound time.Time)
|
||||
GetMonthlyBounds(date time.Time) (lowerBound, upperBound time.Time)
|
||||
GetQuarterlyBounds(date time.Time) (lowerBound, upperBound time.Time)
|
||||
GetYearlyBounds(date time.Time) (lowerBound, upperBound time.Time)
|
||||
}
|
||||
|
||||
type tbOpt func(*timeBounder)
|
||||
|
||||
func WithLocation(l *time.Location) tbOpt {
|
||||
return func(tb *timeBounder) {
|
||||
tb.loc = l
|
||||
}
|
||||
}
|
||||
|
||||
func NewTimeBounder(opts ...tbOpt) timeBounder {
|
||||
tb := timeBounder{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&tb)
|
||||
}
|
||||
|
||||
if tb.loc == nil {
|
||||
tb.loc = time.UTC
|
||||
}
|
||||
|
||||
return tb
|
||||
}
|
||||
|
||||
type timeBounder struct {
|
||||
loc *time.Location
|
||||
}
|
||||
|
||||
func (tb timeBounder) GetDailyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, tb.loc)
|
||||
upperBound = lowerBound.AddDate(0, 0, 1)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tb timeBounder) GetWeeklyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, tb.loc).AddDate(0, 0, -int(date.Weekday()-time.Monday))
|
||||
upperBound = lowerBound.AddDate(0, 0, DaysInWeek)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tb timeBounder) GetMonthlyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, tb.loc)
|
||||
upperBound = lowerBound.AddDate(0, 1, 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tb *timeBounder) GetQuarterlyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
year, _, _ := date.Date()
|
||||
|
||||
quarter := (int(date.Month()) - 1) / MonthsInQuarter
|
||||
firstMonthOfTheQuarter := time.Month(quarter*MonthsInQuarter + 1)
|
||||
|
||||
lowerBound = time.Date(year, firstMonthOfTheQuarter, 1, 0, 0, 0, 0, tb.loc)
|
||||
upperBound = lowerBound.AddDate(0, MonthsInQuarter, 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tb timeBounder) GetYearlyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), 1, 1, 0, 0, 0, 0, tb.loc)
|
||||
upperBound = lowerBound.AddDate(1, 0, 0)
|
||||
|
||||
return
|
||||
}
|
|
@ -3,66 +3,23 @@ package partman
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"dynatron.me/x/stillbox/internal/common"
|
||||
)
|
||||
|
||||
const (
|
||||
daysInWeek = 7
|
||||
monthsInQuarter = 3
|
||||
)
|
||||
|
||||
func getDailyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, time.UTC)
|
||||
upperBound = lowerBound.AddDate(0, 0, 1)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getWeeklyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, time.UTC).AddDate(0, 0, -int(date.Weekday()-time.Monday))
|
||||
upperBound = lowerBound.AddDate(0, 0, daysInWeek)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getMonthlyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, time.UTC)
|
||||
upperBound = lowerBound.AddDate(0, 1, 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getQuarterlyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
year, _, _ := date.Date()
|
||||
|
||||
quarter := (int(date.Month()) - 1) / monthsInQuarter
|
||||
firstMonthOfTheQuarter := time.Month(quarter*monthsInQuarter + 1)
|
||||
|
||||
lowerBound = time.Date(year, firstMonthOfTheQuarter, 1, 0, 0, 0, 0, time.UTC)
|
||||
upperBound = lowerBound.AddDate(0, monthsInQuarter, 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getYearlyBounds(date time.Time) (lowerBound, upperBound time.Time) {
|
||||
lowerBound = time.Date(date.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
upperBound = lowerBound.AddDate(1, 0, 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p Partition) Next(i int) Partition {
|
||||
var t time.Time
|
||||
switch p.Interval {
|
||||
case Daily:
|
||||
t = p.Time.AddDate(0, 0, i)
|
||||
case Weekly:
|
||||
t = p.Time.AddDate(0, 0, i*daysInWeek)
|
||||
t = p.Time.AddDate(0, 0, i*common.DaysInWeek)
|
||||
case Monthly:
|
||||
year, month, _ := p.Time.Date()
|
||||
|
||||
t = time.Date(year, month+time.Month(i), 1, 0, 0, 0, 0, p.Time.Location())
|
||||
case Quarterly:
|
||||
t = p.Time.AddDate(0, i*monthsInQuarter, 0)
|
||||
t = p.Time.AddDate(0, i*common.MonthsInQuarter, 0)
|
||||
case Yearly:
|
||||
year, _, _ := p.Time.Date()
|
||||
|
||||
|
@ -125,13 +82,13 @@ func (p Partition) Prev(i int) Partition {
|
|||
case Daily:
|
||||
t = p.Time.AddDate(0, 0, -i)
|
||||
case Weekly:
|
||||
t = p.Time.AddDate(0, 0, -i*daysInWeek)
|
||||
t = p.Time.AddDate(0, 0, -i*common.DaysInWeek)
|
||||
case Monthly:
|
||||
year, month, _ := p.Time.Date()
|
||||
|
||||
t = time.Date(year, month-time.Month(i), 1, 0, 0, 0, 0, p.Time.Location())
|
||||
case Quarterly:
|
||||
t = p.Time.AddDate(0, -i*monthsInQuarter, 0)
|
||||
t = p.Time.AddDate(0, -i*common.MonthsInQuarter, 0)
|
||||
case Yearly:
|
||||
year, _, _ := p.Time.Date()
|
||||
|
||||
|
@ -150,3 +107,21 @@ func (p Partition) Prev(i int) Partition {
|
|||
return pp
|
||||
|
||||
}
|
||||
|
||||
func (p Partition) Range() (time.Time, time.Time) {
|
||||
b := common.NewTimeBounder()
|
||||
switch p.Interval {
|
||||
case Daily:
|
||||
return b.GetDailyBounds(p.Time)
|
||||
case Weekly:
|
||||
return b.GetWeeklyBounds(p.Time)
|
||||
case Monthly:
|
||||
return b.GetMonthlyBounds(p.Time)
|
||||
case Quarterly:
|
||||
return b.GetQuarterlyBounds(p.Time)
|
||||
case Yearly:
|
||||
return b.GetYearlyBounds(p.Time)
|
||||
}
|
||||
|
||||
panic("unknown interval!")
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"dynatron.me/x/stillbox/internal/common"
|
||||
"dynatron.me/x/stillbox/internal/isoweek"
|
||||
"dynatron.me/x/stillbox/pkg/config"
|
||||
"dynatron.me/x/stillbox/pkg/database"
|
||||
|
@ -325,23 +326,6 @@ func (pm *partman) Check(ctx context.Context, now time.Time) error {
|
|||
}, pgx.TxOptions{})
|
||||
}
|
||||
|
||||
func (p Partition) Range() (time.Time, time.Time) {
|
||||
switch p.Interval {
|
||||
case Daily:
|
||||
return getDailyBounds(p.Time)
|
||||
case Weekly:
|
||||
return getWeeklyBounds(p.Time)
|
||||
case Monthly:
|
||||
return getMonthlyBounds(p.Time)
|
||||
case Quarterly:
|
||||
return getQuarterlyBounds(p.Time)
|
||||
case Yearly:
|
||||
return getYearlyBounds(p.Time)
|
||||
}
|
||||
|
||||
panic("unknown interval!")
|
||||
}
|
||||
|
||||
func (p Partition) PartitionName() string {
|
||||
return p.Name
|
||||
}
|
||||
|
@ -423,7 +407,7 @@ func (pm *partman) verifyPartName(pr database.PartitionResult) (p Partition, err
|
|||
if quarterNum > 4 {
|
||||
return p, PartitionError(pn, errors.New("invalid quarter"))
|
||||
}
|
||||
firstMonthOfTheQuarter := time.Month((quarterNum-1)*monthsInQuarter + 1)
|
||||
firstMonthOfTheQuarter := time.Month((quarterNum-1)*common.MonthsInQuarter + 1)
|
||||
parsed := time.Date(year, firstMonthOfTheQuarter, 1, 0, 0, 0, 0, time.UTC)
|
||||
if parsed != p.Time {
|
||||
return p, PartitionError(pn, ParsedIntvlErr{parsed: parsed, start: p.Time})
|
||||
|
|
|
@ -20,7 +20,7 @@ WHERE
|
|||
CASE WHEN $2::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date >= $2 ELSE TRUE END AND
|
||||
CASE WHEN $3::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date <= $3 ELSE TRUE END
|
||||
c.call_date < $3 ELSE TRUE END
|
||||
GROUP BY date
|
||||
ORDER BY date DESC
|
||||
`
|
||||
|
@ -61,7 +61,7 @@ WHERE
|
|||
CASE WHEN $2::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date >= $2 ELSE TRUE END AND
|
||||
CASE WHEN $3::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date <= $3 ELSE TRUE END
|
||||
c.call_date < $3 ELSE TRUE END
|
||||
GROUP BY 2, 3, 4
|
||||
ORDER BY 4 DESC
|
||||
`
|
||||
|
|
|
@ -5,10 +5,12 @@ import (
|
|||
"time"
|
||||
|
||||
"dynatron.me/x/stillbox/internal/cache"
|
||||
"dynatron.me/x/stillbox/internal/common"
|
||||
"dynatron.me/x/stillbox/internal/jsontypes"
|
||||
"dynatron.me/x/stillbox/pkg/calls"
|
||||
"dynatron.me/x/stillbox/pkg/calls/callstore"
|
||||
"dynatron.me/x/stillbox/pkg/services"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const DefaultExpiration = 5 * time.Minute
|
||||
|
@ -56,20 +58,27 @@ func (s *stats) GetCallStats(ctx context.Context, interval calls.StatsInterval)
|
|||
|
||||
var start time.Time
|
||||
now := time.Now()
|
||||
end := now
|
||||
bnd := common.NewTimeBounder(common.WithLocation(now.Location()))
|
||||
|
||||
switch interval {
|
||||
case calls.IntervalHour:
|
||||
start = now.Add(-24 * time.Hour) // one day
|
||||
case calls.IntervalDay:
|
||||
start = now.Add(-7 * 24 * time.Hour) // one week
|
||||
case calls.IntervalWeek:
|
||||
start = now.Add(-30 * 24 * time.Hour) // one month
|
||||
start, end = bnd.GetMonthlyBounds(now)
|
||||
start, _ = bnd.GetWeeklyBounds(start)
|
||||
_, end = bnd.GetWeeklyBounds(end)
|
||||
case calls.IntervalMonth:
|
||||
start = now.Add(-365 * 24 * time.Hour) // one year
|
||||
default:
|
||||
return nil, calls.ErrInvalidInterval
|
||||
}
|
||||
|
||||
st, err := s.cs.CallStats(ctx, interval, jsontypes.Time(start), jsontypes.Time(now))
|
||||
log.Debug().Str("start", start.String()).Str("end", end.String()).Msg("bound")
|
||||
|
||||
st, err := s.cs.CallStats(ctx, interval, jsontypes.Time(start), jsontypes.Time(end))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ WHERE
|
|||
CASE WHEN sqlc.narg('start')::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date >= @start ELSE TRUE END AND
|
||||
CASE WHEN sqlc.narg('end')::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date <= sqlc.narg('end') ELSE TRUE END
|
||||
c.call_date < sqlc.narg('end') ELSE TRUE END
|
||||
GROUP BY 2, 3, 4
|
||||
ORDER BY 4 DESC;
|
||||
|
||||
|
@ -22,6 +22,6 @@ WHERE
|
|||
CASE WHEN sqlc.narg('start')::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date >= @start ELSE TRUE END AND
|
||||
CASE WHEN sqlc.narg('end')::TIMESTAMPTZ IS NOT NULL THEN
|
||||
c.call_date <= sqlc.narg('end') ELSE TRUE END
|
||||
c.call_date < sqlc.narg('end') ELSE TRUE END
|
||||
GROUP BY date
|
||||
ORDER BY date DESC;
|
||||
|
|
Loading…
Add table
Reference in a new issue