pkg/timestamp/range.go (155 lines of code) (raw):

// Licensed to Apache Software Foundation (ASF) under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Apache Software Foundation (ASF) licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package timestamp import ( "time" modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1" ) // TimeRange is a range of periods into which data can be written or retrieved. type TimeRange struct { Start time.Time End time.Time IncludeStart bool IncludeEnd bool } // Before returns whether the TimeRange is before the other time. func (t TimeRange) Before(other time.Time) bool { if t.IncludeEnd { return t.End.Before(other) } return !t.End.After(other) } // Contains returns whether the unixNano is in the TimeRange. func (t TimeRange) Contains(unixNano int64) bool { tp := time.Unix(0, unixNano) if t.Start.Equal(tp) { return t.IncludeStart } if t.End.Equal(tp) { return t.IncludeEnd } return !tp.Before(t.Start) && !tp.After(t.End) } // Overlapping returns whether TimeRanges intersect each other. func (t TimeRange) Overlapping(other TimeRange) bool { if t.Start.Equal(other.End) { return t.IncludeStart && other.IncludeEnd } if other.Start.Equal(t.End) { return t.IncludeEnd && other.IncludeStart } return !t.Start.After(other.End) && !other.Start.After(t.End) } // Include returns whether the TimeRange includes the other TimeRange. func (t TimeRange) Include(other TimeRange) bool { var start, end bool if t.Start.Equal(other.Start) { start = t.IncludeStart || !other.IncludeStart } else { start = !t.Start.After(other.Start) } if t.End.Equal(other.End) { end = t.IncludeEnd || !other.IncludeEnd } else { end = !t.End.Before(other.End) } return start && end } // Duration converts TimeRange to time.Duration. func (t TimeRange) Duration() time.Duration { return t.End.Sub(t.Start) } // String shows the string representation. func (t TimeRange) String() string { var buf []byte if t.IncludeStart { buf = []byte("[") } else { buf = []byte("(") } buf = append(buf, t.Start.String()...) buf = append(buf, ", "...) buf = append(buf, t.End.String()...) if t.IncludeEnd { buf = append(buf, "]"...) } else { buf = append(buf, ")"...) } return string(buf) } // NewInclusiveTimeRange returns TimeRange includes start and end time. func NewInclusiveTimeRange(start, end time.Time) TimeRange { return TimeRange{ Start: start, End: end, IncludeStart: true, IncludeEnd: true, } } // NewInclusiveTimeRangeDuration returns TimeRange includes start and end time. // It is created from a start time and the Duration. func NewInclusiveTimeRangeDuration(start time.Time, duration time.Duration) TimeRange { return NewTimeRangeDuration(start, duration, true, true) } // NewSectionTimeRange returns TimeRange includes start time. // This function is for creating blocks and segments of tsdb. func NewSectionTimeRange(start, end time.Time) TimeRange { return NewTimeRange(start, end, true, false) } // NewTimeRange returns TimeRange. func NewTimeRange(start, end time.Time, includeStart, includeEnd bool) TimeRange { return TimeRange{ Start: start, End: end, IncludeStart: includeStart, IncludeEnd: includeEnd, } } // NewTimeRangeDuration returns TimeRange from a start time and the Duration. func NewTimeRangeDuration(start time.Time, duration time.Duration, includeStart, includeEnd bool) TimeRange { return NewTimeRange(start, start.Add(duration), includeStart, includeEnd) } // FindRange returns the indices of the first and last elements in a sorted 'timestamps' slice that are within the min and max range. func FindRange[T int64 | uint64](timestamps []T, minVal, maxVal T) (int, int, bool) { if len(timestamps) == 0 { return -1, -1, false } isAsc := timestamps[0] <= timestamps[len(timestamps)-1] if isAsc && (timestamps[0] > maxVal || timestamps[len(timestamps)-1] < minVal) { return -1, -1, false } if !isAsc && (timestamps[0] < minVal || timestamps[len(timestamps)-1] > maxVal) { return -1, -1, false } start, end := -1, len(timestamps) for start < len(timestamps)-1 { start++ if isAsc && timestamps[start] >= minVal || !isAsc && timestamps[start] <= maxVal { break } } for end > 0 { end-- if isAsc && timestamps[end] <= maxVal || !isAsc && timestamps[end] >= minVal { break } } return start, end, start <= end } // Find returns the index of the target in the sorted 'timestamps' slice. func Find(timestamps []int64, target int64) int { if len(timestamps) == 0 { return -1 } if timestamps[0] > target || timestamps[len(timestamps)-1] < target { return -1 } left, right := 0, len(timestamps)-1 for left <= right { mid := (left + right) / 2 if timestamps[mid] == target { return mid } if timestamps[mid] < target { left = mid + 1 } else { right = mid - 1 } } return -1 } // PbHasOverlap checks if two TimeRanges have an overlap. // Returns true if they have at least one point in common, false otherwise. // Note: this follows [begin, end] (inclusive on both ends) // // other follows [begin, end) (inclusive begin, exclusive end) func PbHasOverlap(this, other *modelv1.TimeRange) bool { if this == nil || other == nil { return false } if this.Begin == nil || this.End == nil || other.Begin == nil || other.End == nil { return false } thisBegin := this.Begin.GetSeconds()*1e9 + int64(this.Begin.GetNanos()) thisEnd := this.End.GetSeconds()*1e9 + int64(this.End.GetNanos()) otherBegin := other.Begin.GetSeconds()*1e9 + int64(other.Begin.GetNanos()) otherEnd := other.End.GetSeconds()*1e9 + int64(other.End.GetNanos()) // Check for overlap: // - For x [begin, end] (inclusive on both ends) // - For other [begin, end) (inclusive begin, exclusive end) // They overlap when: // thisBegin <= otherEnd-1 && thisEnd >= otherBegin // Simplified to: thisBegin < otherEnd && thisEnd >= otherBegin return thisBegin < otherEnd && thisEnd >= otherBegin }