mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
145 lines
4.3 KiB
Go
145 lines
4.3 KiB
Go
package batch
|
|
|
|
import (
|
|
"time"
|
|
|
|
featurebase "github.com/featurebasedb/featurebase/v3"
|
|
"github.com/featurebasedb/featurebase/v3/errors"
|
|
)
|
|
|
|
var (
|
|
MinTimestampNano = time.Unix(-1<<32, 0).UTC() // 1833-11-24T17:31:44Z
|
|
MaxTimestampNano = time.Unix(1<<32, 0).UTC() // 2106-02-07T06:28:16Z
|
|
MinTimestamp = time.Unix(-62135596799, 0).UTC() // 0001-01-01T00:00:01Z
|
|
MaxTimestamp = time.Unix(253402300799, 0).UTC() // 9999-12-31T23:59:59Z
|
|
|
|
ErrTimestampOutOfRange = errors.New("", "value provided for timestamp field is out of range")
|
|
)
|
|
|
|
type TimeUnit string
|
|
|
|
const (
|
|
TimeUnitSeconds = TimeUnit(featurebase.TimeUnitSeconds)
|
|
TimeUnitMilliseconds = TimeUnit(featurebase.TimeUnitMilliseconds)
|
|
TimeUnitMicroseconds = TimeUnit(featurebase.TimeUnitMicroseconds)
|
|
TimeUnitUSeconds = TimeUnit(featurebase.TimeUnitUSeconds)
|
|
TimeUnitNanoseconds = TimeUnit(featurebase.TimeUnitNanoseconds)
|
|
)
|
|
|
|
// TimestampToInt64 converts the provided timestamp to an int64 as the number of
|
|
// units past the epoch.
|
|
func TimestampToInt64(unit TimeUnit, epoch time.Time, ts time.Time) (int64, error) {
|
|
var err error
|
|
|
|
unit, err = validateTimeUnit(unit)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "validating time unit")
|
|
}
|
|
|
|
epoch, err = validateEpoch(epoch)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "validating epoch")
|
|
}
|
|
|
|
// Check if the epoch alone is out-of-range. If so, ingest should halt,
|
|
// regardless of state of the timestamp out-of-range CLI option.
|
|
if err := validateTimestamp(unit, epoch); err != nil {
|
|
return 0, errors.Wrap(err, "validating epoch")
|
|
}
|
|
|
|
epochAsInt64 := timestampToInt(unit, epoch)
|
|
|
|
// Check if the timestamp is out-of-range.
|
|
if err := validateTimestamp(unit, ts); err != nil {
|
|
return 0, errors.Wrapf(ErrTimestampOutOfRange, "validating timestamp: %s", ts)
|
|
}
|
|
|
|
tsAsInt64 := timestampToInt(unit, ts)
|
|
|
|
return tsAsInt64 - epochAsInt64, nil
|
|
}
|
|
|
|
// validateTimeUnit checks if the time unit is supported. If the provided unit
|
|
// is blank, validateTimeUnit returns the default TimeUnit.
|
|
func validateTimeUnit(unit TimeUnit) (TimeUnit, error) {
|
|
switch unit {
|
|
case "":
|
|
return TimeUnitSeconds, nil
|
|
|
|
case TimeUnitSeconds,
|
|
TimeUnitMilliseconds,
|
|
TimeUnitMicroseconds,
|
|
TimeUnitUSeconds,
|
|
TimeUnitNanoseconds:
|
|
return unit, nil
|
|
}
|
|
|
|
return "", errors.Errorf("unsupported time unit: %s", unit)
|
|
}
|
|
|
|
// validateEpoch checks if the epoch is supported. If the provided epoch
|
|
// is "zero", validateEpoch returns the default epoch value.
|
|
func validateEpoch(epoch time.Time) (time.Time, error) {
|
|
if epoch.IsZero() {
|
|
return time.Unix(0, 0), nil
|
|
}
|
|
return epoch, nil
|
|
}
|
|
|
|
// validateTimestamp checks if the timestamp is within the range of what FB accepts.
|
|
func validateTimestamp(unit TimeUnit, ts time.Time) error {
|
|
// Min and Max timestamps that Featurebase accepts
|
|
var minStamp, maxStamp time.Time
|
|
switch unit {
|
|
case TimeUnitNanoseconds:
|
|
minStamp = MinTimestampNano
|
|
maxStamp = MaxTimestampNano
|
|
default:
|
|
minStamp = MinTimestamp
|
|
maxStamp = MaxTimestamp
|
|
}
|
|
|
|
if ts.Before(minStamp) || ts.After(maxStamp) {
|
|
return errors.Errorf("timestamp value (%v) must be within min: %v and max: %v", ts, minStamp, maxStamp)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// timestampToInt takes a time unit and a time.Time and converts it to an
|
|
// integer value.
|
|
func timestampToInt(unit TimeUnit, ts time.Time) int64 {
|
|
switch unit {
|
|
case TimeUnitSeconds:
|
|
return ts.Unix()
|
|
case TimeUnitMilliseconds:
|
|
return ts.UnixMilli()
|
|
case TimeUnitMicroseconds, TimeUnitUSeconds:
|
|
return ts.UnixMicro()
|
|
case TimeUnitNanoseconds:
|
|
return ts.UnixNano()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// intToTimestamp takes a timeunit and an integer value and converts it to
|
|
// time.Time.
|
|
func intToTimestamp(unit TimeUnit, val int64) (time.Time, error) {
|
|
switch unit {
|
|
case TimeUnitSeconds:
|
|
return time.Unix(val, 0).UTC(), nil
|
|
case TimeUnitMilliseconds:
|
|
return time.UnixMilli(val).UTC(), nil
|
|
case TimeUnitMicroseconds, TimeUnitUSeconds:
|
|
return time.UnixMicro(val).UTC(), nil
|
|
case TimeUnitNanoseconds:
|
|
return time.Unix(0, val).UTC(), nil
|
|
default:
|
|
return time.Time{}, errors.Errorf("Unknown time unit: '%v'", unit)
|
|
}
|
|
}
|
|
|
|
// Int64ToTimestamp converts the provided int64 to a timestamp based on the time unit
|
|
// and epoch.
|
|
func Int64ToTimestamp(unit TimeUnit, epoch time.Time, val int64) (time.Time, error) {
|
|
return intToTimestamp(unit, timestampToInt(unit, epoch)+val)
|
|
}
|