github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/timeofday/time_of_day.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package timeofday
    12  
    13  import (
    14  	"bytes"
    15  	"math/rand"
    16  	"time"
    17  
    18  	"github.com/cockroachdb/cockroachdb-parser/pkg/util/duration"
    19  	"github.com/cockroachdb/cockroachdb-parser/pkg/util/strutil"
    20  	"github.com/cockroachdb/cockroachdb-parser/pkg/util/timeutil"
    21  )
    22  
    23  // TimeOfDay represents a time of day (no date), stored as microseconds since
    24  // midnight.
    25  type TimeOfDay int64
    26  
    27  const (
    28  	// Min is the minimum TimeOfDay value (midnight).
    29  	Min = TimeOfDay(0)
    30  
    31  	// Time2400 is a special value to represent the 24:00 input time
    32  	Time2400 = TimeOfDay(microsecondsPerDay)
    33  
    34  	// Max is the maximum TimeOfDay value (1 second before midnight)
    35  	Max = Time2400
    36  
    37  	microsecondsPerSecond = 1e6
    38  	microsecondsPerMinute = 60 * microsecondsPerSecond
    39  	microsecondsPerHour   = 60 * microsecondsPerMinute
    40  	microsecondsPerDay    = 24 * microsecondsPerHour
    41  	nanosPerMicro         = 1000
    42  	secondsPerDay         = 24 * 60 * 60
    43  )
    44  
    45  // New creates a TimeOfDay representing the specified time.
    46  func New(hour, min, sec, micro int) TimeOfDay {
    47  	hours := time.Duration(hour) * time.Hour
    48  	minutes := time.Duration(min) * time.Minute
    49  	seconds := time.Duration(sec) * time.Second
    50  	micros := time.Duration(micro) * time.Microsecond
    51  	return FromInt(int64((hours + minutes + seconds + micros) / time.Microsecond))
    52  }
    53  
    54  func (t TimeOfDay) String() string {
    55  	return string(t.AppendFormat(nil))
    56  }
    57  
    58  // AppendFormat appends this TimeOfDay format to the specified buffer.
    59  func (t TimeOfDay) AppendFormat(buf []byte) []byte {
    60  	buf = strutil.AppendInt(buf, t.Hour(), 2)
    61  	buf = append(buf, ':')
    62  	buf = strutil.AppendInt(buf, t.Minute(), 2)
    63  	buf = append(buf, ':')
    64  	buf = strutil.AppendInt(buf, t.Second(), 2)
    65  	micros := t.Microsecond()
    66  	if micros > 0 {
    67  		buf = append(buf, '.')
    68  		buf = strutil.AppendInt(buf, micros, 6)
    69  		buf = bytes.TrimRight(buf, "0")
    70  	}
    71  	return buf
    72  }
    73  
    74  // FromInt constructs a TimeOfDay from an int64, representing microseconds since
    75  // midnight. Inputs outside the range [0, microsecondsPerDay) are modded as
    76  // appropriate.
    77  func FromInt(i int64) TimeOfDay {
    78  	return TimeOfDay(positiveMod(i, microsecondsPerDay))
    79  }
    80  
    81  // positive_mod returns x mod y in the range [0, y). (Go's modulo operator
    82  // preserves sign.)
    83  func positiveMod(x, y int64) int64 {
    84  	if x < 0 {
    85  		return x%y + y
    86  	}
    87  	return x % y
    88  }
    89  
    90  // FromTime constructs a TimeOfDay from a time.Time, ignoring the date and time zone.
    91  func FromTime(t time.Time) TimeOfDay {
    92  	// Adjust for timezone offset so it won't affect the time. This is necessary
    93  	// at times, like when casting from a TIMESTAMPTZ.
    94  	_, offset := t.Zone()
    95  	unixSeconds := t.Unix() + int64(offset)
    96  
    97  	nanos := (unixSeconds%secondsPerDay)*int64(time.Second) + int64(t.Nanosecond())
    98  	return FromInt(nanos / nanosPerMicro)
    99  }
   100  
   101  // FromTimeAllow2400 assumes 24:00 time is possible from the given input,
   102  // otherwise falling back to FromTime.
   103  // It assumes time.Time is represented as lib/pq or as unix time.
   104  func FromTimeAllow2400(t time.Time) TimeOfDay {
   105  	if t.Day() != 1 {
   106  		return Time2400
   107  	}
   108  	return FromTime(t)
   109  }
   110  
   111  // ToTime converts a TimeOfDay to a time.Time, using the Unix epoch as the date.
   112  func (t TimeOfDay) ToTime() time.Time {
   113  	return timeutil.Unix(0, int64(t)*nanosPerMicro)
   114  }
   115  
   116  // Random generates a random TimeOfDay.
   117  func Random(rng *rand.Rand) TimeOfDay {
   118  	return TimeOfDay(rng.Int63n(microsecondsPerDay))
   119  }
   120  
   121  // Round takes a TimeOfDay, and rounds it to the given precision.
   122  func (t TimeOfDay) Round(precision time.Duration) TimeOfDay {
   123  	if t == Time2400 {
   124  		return t
   125  	}
   126  	ret := t.ToTime().Round(precision)
   127  	// Rounding Max should give Time2400, not 00:00.
   128  	// To catch this, see if we are comparing against the same day.
   129  	if ret.Day() != t.ToTime().Day() {
   130  		return Time2400
   131  	}
   132  	return FromTime(ret)
   133  }
   134  
   135  // Add adds a Duration to a TimeOfDay, wrapping into the next day if necessary.
   136  func (t TimeOfDay) Add(d duration.Duration) TimeOfDay {
   137  	return FromInt(int64(t) + d.Nanos()/nanosPerMicro)
   138  }
   139  
   140  // Difference returns the interval between t1 and t2, which may be negative.
   141  func Difference(t1 TimeOfDay, t2 TimeOfDay) duration.Duration {
   142  	return duration.MakeDuration(int64(t1-t2)*nanosPerMicro, 0, 0)
   143  }
   144  
   145  // Hour returns the hour specified by t, in the range [0, 24].
   146  func (t TimeOfDay) Hour() int {
   147  	if t == Time2400 {
   148  		return 24
   149  	}
   150  	return int(int64(t)%microsecondsPerDay) / microsecondsPerHour
   151  }
   152  
   153  // Minute returns the minute offset within the hour specified by t, in the
   154  // range [0, 59].
   155  func (t TimeOfDay) Minute() int {
   156  	return int(int64(t)%microsecondsPerHour) / microsecondsPerMinute
   157  }
   158  
   159  // Second returns the second offset within the minute specified by t, in the
   160  // range [0, 59].
   161  func (t TimeOfDay) Second() int {
   162  	return int(int64(t)%microsecondsPerMinute) / microsecondsPerSecond
   163  }
   164  
   165  // Microsecond returns the microsecond offset within the second specified by t,
   166  // in the range [0, 999999].
   167  func (t TimeOfDay) Microsecond() int {
   168  	return int(int64(t) % microsecondsPerSecond)
   169  }