github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/time/range.go (about) 1 // Copyright (c) 2016 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 time 22 23 import ( 24 "fmt" 25 "time" 26 ) 27 28 // Range represents [start, end) 29 type Range struct { 30 Start UnixNano 31 End UnixNano 32 } 33 34 // IsEmpty returns whether the time range is empty. 35 func (r Range) IsEmpty() bool { 36 return r.Start.Equal(r.End) 37 } 38 39 // Equal returns whether two time ranges are equal. 40 func (r Range) Equal(other Range) bool { 41 return r.Start.Equal(other.Start) && r.End.Equal(other.End) 42 } 43 44 // Before determines whether r is before other. 45 func (r Range) Before(other Range) bool { 46 return !r.End.After(other.Start) 47 } 48 49 // After determines whether r is after other. 50 func (r Range) After(other Range) bool { 51 return other.Before(r) 52 } 53 54 // Contains determines whether r contains other. 55 func (r Range) Contains(other Range) bool { 56 return !r.Start.After(other.Start) && !r.End.Before(other.End) 57 } 58 59 // Overlaps determines whether r overlaps with other. 60 func (r Range) Overlaps(other Range) bool { 61 return r.End.After(other.Start) && r.Start.Before(other.End) 62 } 63 64 // Duration returns the duration of the range. 65 func (r Range) Duration() time.Duration { 66 return r.End.Sub(r.Start) 67 } 68 69 // Intersect calculates the intersection of the receiver range against the 70 // provided argument range iff there is an overlap between the two. It also 71 // returns a bool indicating if there was a valid intersection. 72 func (r Range) Intersect(other Range) (Range, bool) { 73 if !r.Overlaps(other) { 74 return Range{}, false 75 } 76 newRange := r 77 if newRange.Start.Before(other.Start) { 78 newRange.Start = other.Start 79 } 80 if newRange.End.After(other.End) { 81 newRange.End = other.End 82 } 83 return newRange, true 84 } 85 86 // Since returns the time range since a given point in time. 87 func (r Range) Since(t UnixNano) Range { 88 if t.Before(r.Start) { 89 return r 90 } 91 if t.After(r.End) { 92 return Range{} 93 } 94 return Range{Start: t, End: r.End} 95 } 96 97 // Merge merges the two ranges if they overlap. Otherwise, 98 // the gap between them is included. 99 func (r Range) Merge(other Range) Range { 100 start := MinUnixNano(r.Start, other.Start) 101 end := MaxUnixNano(r.End, other.End) 102 return Range{Start: start, End: end} 103 } 104 105 // Subtract removes the intersection between r and other 106 // from r, possibly splitting r into two smaller ranges. 107 func (r Range) Subtract(other Range) []Range { 108 if !r.Overlaps(other) { 109 return []Range{r} 110 } 111 if other.Contains(r) { 112 return nil 113 } 114 var res []Range 115 left := Range{r.Start, other.Start} 116 right := Range{other.End, r.End} 117 if r.Contains(other) { 118 if !left.IsEmpty() { 119 res = append(res, left) 120 } 121 if !right.IsEmpty() { 122 res = append(res, right) 123 } 124 return res 125 } 126 if !r.Start.After(other.Start) { 127 if !left.IsEmpty() { 128 res = append(res, left) 129 } 130 return res 131 } 132 if !right.IsEmpty() { 133 res = append(res, right) 134 } 135 return res 136 } 137 138 // IterateForward iterates through a time range by step size in the 139 // forwards direction. 140 func (r Range) IterateForward(stepSize time.Duration, f func(t UnixNano) (shouldContinue bool)) { 141 for t := r.Start; t.Before(r.End); t = t.Add(stepSize) { 142 if shouldContinue := f(t); !shouldContinue { 143 break 144 } 145 } 146 } 147 148 // IterateBackward iterates through a time range by step size in the 149 // backwards direction. 150 func (r Range) IterateBackward(stepSize time.Duration, f func(t UnixNano) (shouldContinue bool)) { 151 for t := r.End; t.After(r.Start); t = t.Add(-stepSize) { 152 if shouldContinue := f(t); !shouldContinue { 153 break 154 } 155 } 156 } 157 158 // String returns the string representation of the range. 159 func (r Range) String() string { 160 return fmt.Sprintf("(%v,%v)", r.Start, r.End) 161 }