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  }