github.com/m3db/m3@v1.5.0/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  }