interval.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package util
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "time"
  7. "github.com/opencost/opencost/core/pkg/util/timeutil"
  8. )
  9. var intervalRegex = regexp.MustCompile(`^(\d+)(m|h|d|w)$`)
  10. // Interval is a time period defined by a string with a integer followed by a letter (ex: 5d = 5 days)
  11. type Interval interface {
  12. // Add adds the interval multiplied by the given int to the given time. (A 10m interval called with 3 would add 30
  13. // minutes to the given time)
  14. Add(time.Time, int) time.Time
  15. // Truncate returns the start of the interval that the given time is a part off
  16. Truncate(time time.Time) time.Time
  17. }
  18. func NewInterval(def string) (Interval, error) {
  19. match := intervalRegex.FindStringSubmatch(def)
  20. if match == nil {
  21. return nil, fmt.Errorf("failed to parse interval '%s'", def)
  22. }
  23. num, err := strconv.ParseInt(match[1], 10, 64)
  24. // This should not happen
  25. if err != nil {
  26. panic(fmt.Sprintf("NewInterval: regex failure on int '%s'", def))
  27. }
  28. switch match[2] {
  29. case "m":
  30. return &durationInterval{time.Duration(num) * time.Minute}, nil
  31. case "h":
  32. return &durationInterval{time.Duration(num) * time.Hour}, nil
  33. case "d":
  34. return &durationInterval{time.Duration(num) * timeutil.Day}, nil
  35. case "w":
  36. return &weekInterval{int(num)}, nil
  37. default:
  38. panic(fmt.Sprintf("NewInterval: regex failure on unit '%s'", def))
  39. }
  40. }
  41. type durationInterval struct {
  42. duration time.Duration
  43. }
  44. func (d *durationInterval) Add(t time.Time, i int) time.Time {
  45. return t.Add(d.duration * time.Duration(i))
  46. }
  47. func (d *durationInterval) Truncate(time time.Time) time.Time {
  48. return time.UTC().Truncate(d.duration)
  49. }
  50. // weekInterval is an interval that tracks multiples of weeks with the week starting on Sunday
  51. type weekInterval struct {
  52. count int
  53. }
  54. func (w *weekInterval) Add(t time.Time, num int) time.Time {
  55. return t.Add(timeutil.Week * time.Duration(num*w.count))
  56. }
  57. // Truncate to the nearest Sunday that is a multiple of the count starting from 0000-31-12
  58. func (w *weekInterval) Truncate(t time.Time) time.Time {
  59. // add a day to Sundays to prevent times that would truncate to themselves from going to the previous step
  60. if t.UTC().Weekday() == time.Sunday {
  61. t = t.UTC().AddDate(0, 0, 1)
  62. }
  63. // truncate to monday using a weekly duration multiple (0001-01-01 was a monday) then subtract a day
  64. return t.UTC().Truncate(timeutil.Week * time.Duration(w.count)).Add(-timeutil.Day)
  65. }