github.com/go-chrono/chrono@v0.0.0-20240102183611-532f0d0d7c34/extent.go (about) 1 package chrono 2 3 import ( 4 "fmt" 5 "math" 6 ) 7 8 // Extent represents a period of time measured in nanoseconds. 9 // The represented value is exactly equivalent to the standard library's time.Duration. 10 type Extent int64 11 12 // Common time-based durations relative to 1 nanosecond. 13 const ( 14 Nanosecond Extent = 1 15 Microsecond = 1000 * Nanosecond 16 Millisecond = 1000 * Microsecond 17 Second = 1000 * Millisecond 18 Minute = 60 * Second 19 Hour = 60 * Minute 20 ) 21 22 // Nanoseconds returns the extent as an integer nanosecond count. 23 func (e Extent) Nanoseconds() int64 { 24 return int64(e) 25 } 26 27 // Microseconds returns the duration as a floating point number of microseconds. 28 func (e Extent) Microseconds() float64 { 29 micros := e / Microsecond 30 nsec := e % micros 31 return float64(micros) + float64(nsec)/1e3 32 } 33 34 // Milliseconds returns the duration as a floating point number of milliseconds. 35 func (e Extent) Milliseconds() float64 { 36 millis := e / Millisecond 37 nsec := e % millis 38 return float64(millis) + float64(nsec)/1e6 39 } 40 41 // Seconds returns the duration as a floating point number of seconds. 42 func (e Extent) Seconds() float64 { 43 secs := e / Second 44 nsec := e % secs 45 return float64(secs) + float64(nsec)/1e9 46 } 47 48 // Minutes returns the duration as a floating point number of minutes. 49 func (e Extent) Minutes() float64 { 50 mins := e / Minute 51 nsec := e % mins 52 return float64(mins) + float64(nsec)/(60*1e9) 53 } 54 55 // Hours returns the duration as a floating point number of hours. 56 func (e Extent) Hours() float64 { 57 hours := e / Hour 58 nsec := e % hours 59 return float64(hours) + float64(nsec)/(60*60*1e9) 60 } 61 62 // Units returns the whole numbers of hours, minutes, seconds, and nanosecond offset represented by e. 63 func (e Extent) Units() (hours, mins, secs, nsec int) { 64 return extentUnits(int64(e)) 65 } 66 67 func extentUnits(e int64) (hours, mins, secs, nsec int) { 68 hours = int(e / oneHour) 69 mins = int(e/oneMinute) % 60 70 secs = int(e/oneSecond) % 60 71 nsec = int(e % oneSecond) 72 return 73 } 74 75 // Truncate returns the result of rounding e toward zero to a multiple of m. 76 func (e Extent) Truncate(m Extent) Extent { 77 return Extent(truncateExtent(int64(e), int64(m))) 78 } 79 80 func truncateExtent(e, m int64) int64 { 81 if m <= 0 { 82 return e 83 } 84 return e - e%m 85 } 86 87 // String returns a string formatted according to ISO 8601. 88 // It is equivalent to calling Format with no arguments. 89 func (e Extent) String() string { 90 return e.Format() 91 } 92 93 // Format the extent according to ISO 8601. 94 // Behaves the same as Duration.Format. 95 func (e Extent) Format(exclusive ...Designator) string { 96 abs := extentAbs(int64(e)) 97 out, neg := formatDuration(abs/oneSecond, uint32(abs%oneSecond), e < 0, exclusive...) 98 out = "P" + out 99 if neg { 100 out = "-" + out 101 } 102 return out 103 } 104 105 // Parse the time portion of an ISO 8601 duration. 106 // Behaves the same as Duration.Parse. 107 func (e *Extent) Parse(s string) error { 108 _, _, _, _, secs, nsec, neg, err := parseDuration(s, false, true) 109 if err != nil { 110 return err 111 } 112 113 if err := checkExtentRange(secs, nsec, neg); err != nil { 114 return err 115 } 116 117 *e = Extent(secs*int64(Second) + int64(nsec)) 118 if neg { 119 *e *= -1 120 } 121 122 return nil 123 } 124 125 func extentAbs(e int64) int64 { 126 if e < 0 { 127 return e * -1 128 } 129 return e 130 } 131 132 func checkExtentRange(secs int64, nsec uint32, neg bool) error { 133 if neg { 134 switch { 135 case -secs < minSeconds: 136 return fmt.Errorf("seconds underflow") 137 case -secs == minSeconds && nsec > uint32(maxNegNanos): 138 return fmt.Errorf("nanoseconds underflow") 139 } 140 } else { 141 switch { 142 case secs > maxSeconds: 143 return fmt.Errorf("seconds overflow") 144 case secs == maxSeconds && nsec > uint32(maxPosNanos): 145 return fmt.Errorf("nanoseconds overflow") 146 } 147 } 148 return nil 149 } 150 151 const ( 152 minSeconds = int64(math.MinInt64) / int64(Second) 153 maxNegNanos = -(int64(math.MinInt64) % -minSeconds) 154 maxSeconds = int64(math.MaxInt64) / int64(Second) 155 maxPosNanos = int64(math.MaxInt64) % maxSeconds 156 )