github.com/grafana/pyroscope@v1.18.0/pkg/frontend/split_by_interval.go (about) 1 package frontend 2 3 import ( 4 "time" 5 ) 6 7 // TimeIntervalIterator splits a time range into non-overlapping sub-ranges, 8 // where the boundary adjoining on the left is not included, e.g: 9 // 10 // [t1, t2), [t3, t4), ..., [tn-1, tn]. 11 // 12 // By default, a sub-range start time is a multiple of the interval. 13 // See WithAlignment option, if a custom alignment is needed. 14 type TimeIntervalIterator struct { 15 startTime int64 16 endTime int64 17 interval int64 18 alignment int64 19 } 20 21 type TimeInterval struct{ Start, End time.Time } 22 23 type TimeIntervalIteratorOption func(*TimeIntervalIterator) 24 25 // WithAlignment causes a sub-range start time to be a multiple of the 26 // alignment. This makes it possible for a sub-range to be shorter 27 // than the interval specified, but not more than by the alignment. 28 // 29 // The interval can't be less than the alignment. 30 func WithAlignment(a time.Duration) TimeIntervalIteratorOption { 31 return func(i *TimeIntervalIterator) { 32 i.alignment = a.Nanoseconds() 33 } 34 } 35 36 // NewTimeIntervalIterator returns a new interval iterator. 37 // If the interval is zero, the entire time span is taken as a single interval. 38 func NewTimeIntervalIterator(startTime, endTime time.Time, interval time.Duration, 39 options ...TimeIntervalIteratorOption) *TimeIntervalIterator { 40 i := &TimeIntervalIterator{ 41 startTime: startTime.UnixNano(), 42 endTime: endTime.UnixNano(), 43 interval: interval.Nanoseconds(), 44 } 45 if interval == 0 { 46 i.interval = 2 * endTime.Sub(startTime).Nanoseconds() 47 } 48 for _, option := range options { 49 option(i) 50 } 51 i.interval = max(i.interval, i.alignment) 52 return i 53 } 54 55 func (i *TimeIntervalIterator) Next() bool { return i.startTime < i.endTime } 56 57 func (i *TimeIntervalIterator) At() TimeInterval { 58 t := TimeInterval{Start: time.Unix(0, i.startTime)} 59 i.startTime += i.interval 60 if i.alignment > 0 { 61 // Sub-ranges start at a multiple of 'alignment'. 62 i.startTime -= i.interval % i.alignment 63 } else { 64 // Sub-ranges start at a multiple of 'interval'. 65 i.startTime -= i.startTime % i.interval 66 } 67 if i.endTime > i.startTime { 68 // -1 to ensure the adjacent ranges don't overlap. 69 // Could be an option. 70 t.End = time.Unix(0, i.startTime-1) 71 } else { 72 t.End = time.Unix(0, i.endTime) 73 } 74 return t 75 } 76 77 func (*TimeIntervalIterator) Err() error { return nil } 78 79 func (*TimeIntervalIterator) Close() error { return nil }