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 }