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  )