github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/models/bounds.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package models 22 23 import ( 24 "fmt" 25 "math" 26 "time" 27 28 xtime "github.com/m3db/m3/src/x/time" 29 ) 30 31 // Bounds are the time bounds, start time is inclusive but end is exclusive. 32 type Bounds struct { 33 Start xtime.UnixNano 34 Duration time.Duration 35 StepSize time.Duration 36 } 37 38 // TimeForIndex returns the start time for a given index assuming 39 // a uniform step size. 40 func (b Bounds) TimeForIndex(idx int) (xtime.UnixNano, error) { 41 duration := time.Duration(idx) * b.StepSize 42 if b.Steps() == 0 || duration >= b.Duration { 43 return 0, fmt.Errorf("out of bounds, %d", idx) 44 } 45 46 return b.Start.Add(duration), nil 47 } 48 49 // End calculates the end time for the block and is exclusive. 50 func (b Bounds) End() xtime.UnixNano { 51 return b.Start.Add(b.Duration) 52 } 53 54 // Steps calculates the number of steps for the bounds. 55 func (b Bounds) Steps() int { 56 if b.StepSize <= 0 { 57 return 0 58 } 59 60 return int(b.Duration / b.StepSize) 61 } 62 63 // Contains returns whether the time lies between the bounds. 64 func (b Bounds) Contains(t xtime.UnixNano) bool { 65 diff := b.Start.Sub(t) 66 return diff >= 0 && diff < b.Duration 67 } 68 69 // Next returns the nth next bound from the current bound. 70 func (b Bounds) Next(n int) Bounds { 71 return b.nth(n, true) 72 } 73 74 // Previous returns the nth previous bound from the current bound. 75 func (b Bounds) Previous(n int) Bounds { 76 return b.nth(n, false) 77 } 78 79 func (b Bounds) nth(n int, forward bool) Bounds { 80 multiplier := time.Duration(n) 81 if !forward { 82 multiplier *= -1 83 } 84 85 blockDuration := b.Duration 86 start := b.Start.Add(blockDuration * multiplier) 87 return Bounds{ 88 Start: start, 89 Duration: blockDuration, 90 StepSize: b.StepSize, 91 } 92 } 93 94 // Blocks returns the number of blocks until time t. 95 func (b Bounds) Blocks(t xtime.UnixNano) int { 96 return int(b.Start.Sub(t) / b.Duration) 97 } 98 99 // String representation of the bounds. 100 func (b Bounds) String() string { 101 return fmt.Sprintf("start: %v, duration: %v, stepSize: %v, steps: %d", 102 b.Start, b.Duration, b.StepSize, b.Steps()) 103 } 104 105 // Nearest returns the nearest bound before the given time. 106 func (b Bounds) Nearest(t xtime.UnixNano) Bounds { 107 startTime := b.Start 108 duration := b.Duration 109 endTime := startTime.Add(duration) 110 step := b.StepSize 111 if t.After(startTime) { 112 for endTime.Before(t) { 113 startTime = endTime 114 endTime = endTime.Add(duration) 115 } 116 117 return Bounds{ 118 Start: startTime, 119 Duration: duration, 120 StepSize: step, 121 } 122 } 123 124 if startTime.After(t) { 125 diff := startTime.Sub(t) 126 timeDiff := math.Ceil(float64(diff) / float64(step)) 127 startTime = startTime.Add(-1 * time.Duration(timeDiff) * step) 128 } 129 130 return Bounds{ 131 Start: startTime, 132 Duration: duration, 133 StepSize: step, 134 } 135 } 136 137 // Equals is true if two bounds are equal, including step size. 138 func (b Bounds) Equals(other Bounds) bool { 139 if b.StepSize != other.StepSize { 140 return false 141 } 142 return b.Start.Equal(other.Start) && b.Duration == other.Duration 143 }