github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logproto/timestamp.go (about)

     1  package logproto
     2  
     3  import (
     4  	"errors"
     5  	strconv "strconv"
     6  	time "time"
     7  
     8  	"github.com/gogo/protobuf/types"
     9  )
    10  
    11  const (
    12  	// Seconds field of the earliest valid Timestamp.
    13  	// This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
    14  	minValidSeconds = -62135596800
    15  	// Seconds field just after the latest valid Timestamp.
    16  	// This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
    17  	maxValidSeconds = 253402300800
    18  )
    19  
    20  // validateTimestamp determines whether a Timestamp is valid.
    21  // A valid timestamp represents a time in the range
    22  // [0001-01-01, 10000-01-01) and has a Nanos field
    23  // in the range [0, 1e9).
    24  //
    25  // If the Timestamp is valid, validateTimestamp returns nil.
    26  // Otherwise, it returns an error that describes
    27  // the problem.
    28  //
    29  // Every valid Timestamp can be represented by a time.Time, but the converse is not true.
    30  func validateTimestamp(ts *types.Timestamp) error {
    31  	if ts == nil {
    32  		return errors.New("timestamp: nil Timestamp")
    33  	}
    34  	if ts.Seconds < minValidSeconds {
    35  		return errors.New("timestamp: " + formatTimestamp(ts) + " before 0001-01-01")
    36  	}
    37  	if ts.Seconds >= maxValidSeconds {
    38  		return errors.New("timestamp: " + formatTimestamp(ts) + " after 10000-01-01")
    39  	}
    40  	if ts.Nanos < 0 || ts.Nanos >= 1e9 {
    41  		return errors.New("timestamp: " + formatTimestamp(ts) + ": nanos not in range [0, 1e9)")
    42  	}
    43  	return nil
    44  }
    45  
    46  // formatTimestamp is equivalent to fmt.Sprintf("%#v", ts)
    47  // but avoids the escape incurred by using fmt.Sprintf, eliminating
    48  // unnecessary heap allocations.
    49  func formatTimestamp(ts *types.Timestamp) string {
    50  	if ts == nil {
    51  		return "nil"
    52  	}
    53  
    54  	seconds := strconv.FormatInt(ts.Seconds, 10)
    55  	nanos := strconv.FormatInt(int64(ts.Nanos), 10)
    56  	return "&types.Timestamp{Seconds: " + seconds + ",\nNanos: " + nanos + ",\n}"
    57  }
    58  
    59  func SizeOfStdTime(t time.Time) int {
    60  	ts, err := timestampProto(t)
    61  	if err != nil {
    62  		return 0
    63  	}
    64  	return ts.Size()
    65  }
    66  
    67  func StdTimeMarshalTo(t time.Time, data []byte) (int, error) {
    68  	ts, err := timestampProto(t)
    69  	if err != nil {
    70  		return 0, err
    71  	}
    72  	return ts.MarshalTo(data)
    73  }
    74  
    75  func StdTimeUnmarshal(t *time.Time, data []byte) error {
    76  	ts := &types.Timestamp{}
    77  	if err := ts.Unmarshal(data); err != nil {
    78  		return err
    79  	}
    80  	tt, err := timestampFromProto(ts)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	*t = tt
    85  	return nil
    86  }
    87  
    88  func timestampFromProto(ts *types.Timestamp) (time.Time, error) {
    89  	// Don't return the zero value on error, because corresponds to a valid
    90  	// timestamp. Instead return whatever time.Unix gives us.
    91  	var t time.Time
    92  	if ts == nil {
    93  		t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
    94  	} else {
    95  		t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
    96  	}
    97  	return t, validateTimestamp(ts)
    98  }
    99  
   100  func timestampProto(t time.Time) (types.Timestamp, error) {
   101  	ts := types.Timestamp{
   102  		Seconds: t.Unix(),
   103  		Nanos:   int32(t.Nanosecond()),
   104  	}
   105  	return ts, validateTimestamp(&ts)
   106  }