github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xtime/xtime.go (about)

     1  package xtime
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"time"
     9  )
    10  
    11  // ===
    12  // set
    13  // ===
    14  
    15  // SetYear sets the year value to given time and returns a new time.Time.
    16  func SetYear(t time.Time, year int) time.Time {
    17  	return time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
    18  }
    19  
    20  // SetMonth sets the month value to given time and returns a new time.Time.
    21  func SetMonth(t time.Time, month int) time.Time {
    22  	return time.Date(t.Year(), time.Month(month), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
    23  }
    24  
    25  // SetDay sets the dat value to given time and returns a new time.Time.
    26  func SetDay(t time.Time, day int) time.Time {
    27  	return time.Date(t.Year(), t.Month(), day, t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
    28  }
    29  
    30  // SetHour sets the hour value to given time and returns a new time.Time.
    31  func SetHour(t time.Time, hour int) time.Time {
    32  	return time.Date(t.Year(), t.Month(), t.Day(), hour, t.Minute(), t.Second(), t.Nanosecond(), t.Location())
    33  }
    34  
    35  // SetMinute sets the minute value to given time and returns a new time.Time.
    36  func SetMinute(t time.Time, minute int) time.Time {
    37  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), minute, t.Second(), t.Nanosecond(), t.Location())
    38  }
    39  
    40  // SetSecond sets the second value to given time and returns a new time.Time.
    41  func SetSecond(t time.Time, second int) time.Time {
    42  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), second, t.Nanosecond(), t.Location())
    43  }
    44  
    45  // SetMillisecond sets the millisecond value to given time and returns a new time.Time.
    46  func SetMillisecond(t time.Time, millisecond int) time.Time {
    47  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), millisecond*1e6, t.Location())
    48  }
    49  
    50  // SetMicrosecond sets the microsecond value to given time and returns a new time.Time.
    51  func SetMicrosecond(t time.Time, microsecond int) time.Time {
    52  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), microsecond*1e3, t.Location())
    53  }
    54  
    55  // SetNanosecond sets the nanosecond value to given time and returns a new time.Time.
    56  func SetNanosecond(t time.Time, nanosecond int) time.Time {
    57  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), nanosecond, t.Location())
    58  }
    59  
    60  // SetLocation sets the location value to given time and returns a new time.Time.
    61  func SetLocation(t time.Time, loc *time.Location) time.Time {
    62  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), loc)
    63  }
    64  
    65  // ==
    66  // to
    67  // ==
    68  
    69  // ToDate returns a new time.Time with the old year, month, day value and parsed location (see GetTimeLocation).
    70  func ToDate(t time.Time) time.Time {
    71  	return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, GetTimeLocation(t))
    72  }
    73  
    74  // ToDateTime returns a new time.Time with the old year, month, day, hour, minute, second value and parsed location (see GetTimeLocation).
    75  func ToDateTime(t time.Time) time.Time {
    76  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, GetTimeLocation(t))
    77  }
    78  
    79  // ToDateTimeNS returns a new time.Time with the old year, month, day, hour, minute, second, nanosecond value and parsed location (see GetTimeLocation).
    80  func ToDateTimeNS(t time.Time) time.Time {
    81  	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), GetTimeLocation(t))
    82  }
    83  
    84  // ===================
    85  // location & timezone
    86  // ===================
    87  
    88  // LocationDuration returns a time.Duration that equals to the duration for given time.Location.
    89  func LocationDuration(loc *time.Location) time.Duration {
    90  	t := time.Date(2020, time.Month(10), 1, 0, 0, 0, 0, loc)
    91  	tUtc := t.In(time.UTC)
    92  	t2 := time.Date(tUtc.Year(), tUtc.Month(), tUtc.Day(), tUtc.Hour(), tUtc.Minute(), tUtc.Second(), tUtc.Nanosecond(), loc)
    93  	return t.Sub(t2)
    94  }
    95  
    96  // GetTimeLocation returns a time.Location with empty name for given time.Time. Note that
    97  // time.Time.Location() will return an unusable location (UTC or Local or empty name).
    98  func GetTimeLocation(t time.Time) *time.Location {
    99  	du := LocationDuration(t.Location())
   100  	return time.FixedZone("", int(du.Seconds())) // use empty name
   101  }
   102  
   103  // GetLocalLocation returns a time.Location with empty name for representing time.Local.
   104  func GetLocalLocation() *time.Location {
   105  	du := LocationDuration(time.Local)
   106  	return time.FixedZone("", int(du.Seconds())) // Local name -> empty
   107  }
   108  
   109  // timezoneRegexp represents a UTC offset timezone format, such as `+0:0`, `-01`, `+08:00`, `-12:30`.
   110  // For more details of time.RFC3339 offset, see https://tools.ietf.org/html/rfc3339#section-4.2.
   111  var timezoneRegexp = regexp.MustCompile(`^([+-])([0-9]{1,2})(?::([0-9]{1,2}))?$`)
   112  
   113  var errWrongFormat = errors.New("xtime: wrong format timezone string")
   114  
   115  // ParseTimezone parses a UTC offset timezone string to time.Location (with UTC+00:00 name), format: `[+-][0-9]{1,2}(:[0-9]{1,2})?`.
   116  func ParseTimezone(timezone string) (*time.Location, error) {
   117  	matches := timezoneRegexp.FindAllStringSubmatch(timezone, 1)
   118  	if len(matches) == 0 || len(matches[0][1:]) < 3 {
   119  		return nil, errWrongFormat
   120  	}
   121  
   122  	group := matches[0][1:]
   123  	signStr, hourStr, minuteStr := group[0], group[1], group[2]
   124  	sign := +1
   125  	if signStr == "-" {
   126  		sign = -1
   127  	}
   128  	if minuteStr == "" {
   129  		minuteStr = "0"
   130  	}
   131  	hour, _ := strconv.Atoi(hourStr)     // no error
   132  	minute, _ := strconv.Atoi(minuteStr) // no error
   133  
   134  	name := fmt.Sprintf("UTC%s%02d:%02d", signStr, hour, minute) // UTC+00:00
   135  	offset := sign * (hour*3600 + minute*60)
   136  	return time.FixedZone(name, offset), nil
   137  }
   138  
   139  // TruncateTime returns the result of rounding t down to a multiple of duration (since the zero time). Note that if given time.Time is not in
   140  // time.UTC, time.Time.Truncate method will return a wrong result, so in this case please use xtime.TruncateTime.
   141  func TruncateTime(t time.Time, du time.Duration) time.Time {
   142  	if t.Location() == time.UTC {
   143  		return t.Truncate(du)
   144  	}
   145  	utcTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC)
   146  	r := utcTime.Truncate(du)
   147  	return time.Date(r.Year(), r.Month(), r.Day(), r.Hour(), r.Minute(), r.Second(), r.Nanosecond(), t.Location())
   148  }
   149  
   150  // ========
   151  // duration
   152  // ========
   153  
   154  // DurationNanosecondComponent returns the nanosecond component of the time.Duration.
   155  func DurationNanosecondComponent(d time.Duration) int {
   156  	ns := d
   157  	us := d / time.Microsecond
   158  	return int(ns - us*1e3) // (d / 1e3) * 1e3
   159  }
   160  
   161  // DurationMicrosecondComponent returns the microsecond component of the time.Duration.
   162  func DurationMicrosecondComponent(d time.Duration) int {
   163  	us := d / time.Microsecond
   164  	ms := d / time.Millisecond
   165  	return int(us - ms*1e3)
   166  }
   167  
   168  // DurationMillisecondComponent returns the millisecond component of the time.Duration.
   169  func DurationMillisecondComponent(d time.Duration) int {
   170  	ms := d / time.Millisecond
   171  	sec := d / time.Second
   172  	return int(ms - sec*1e3)
   173  }
   174  
   175  // DurationSecondComponent returns the second component of the time.Duration.
   176  func DurationSecondComponent(d time.Duration) int {
   177  	sec := d / time.Second
   178  	min := d / time.Minute
   179  	return int(sec - min*60)
   180  }
   181  
   182  // DurationMinuteComponent returns the minute component of the time.Duration.
   183  func DurationMinuteComponent(d time.Duration) int {
   184  	min := d / time.Minute
   185  	hour := d / time.Hour
   186  	return int(min - hour*60)
   187  }
   188  
   189  // DurationHourComponent returns the hour component of the time.Duration.
   190  func DurationHourComponent(d time.Duration) int {
   191  	hour := d / time.Hour
   192  	day := d / (time.Hour * 24)
   193  	return int(hour - day*24)
   194  }
   195  
   196  // DurationDayComponent returns the day component of the time.Duration.
   197  func DurationDayComponent(d time.Duration) int {
   198  	return int(d / (time.Hour * 24)) // total days
   199  }
   200  
   201  // DurationTotalNanoseconds returns the value of the time.Duration expressed in whole and fractional nanoseconds.
   202  func DurationTotalNanoseconds(d time.Duration) int64 {
   203  	return int64(d)
   204  }
   205  
   206  // DurationTotalMicroseconds returns the value of the time.Duration expressed in whole and fractional microseconds.
   207  func DurationTotalMicroseconds(d time.Duration) int64 {
   208  	return int64(d) / 1e3 // only return int64
   209  }
   210  
   211  // DurationTotalMilliseconds returns the value of the time.Duration expressed in whole and fractional milliseconds.
   212  func DurationTotalMilliseconds(d time.Duration) int64 {
   213  	return int64(d) / 1e6 // only return int64
   214  }
   215  
   216  // DurationTotalSeconds returns the value of the time.Duration expressed in whole and fractional seconds.
   217  func DurationTotalSeconds(d time.Duration) float64 {
   218  	sec := d / time.Second
   219  	nsec := d % time.Second
   220  	return float64(sec) + float64(nsec)/1e9 // a truncation to integer would make them not useful
   221  }
   222  
   223  // DurationTotalMinutes returns the value of the time.Duration expressed in whole and fractional minutes.
   224  func DurationTotalMinutes(d time.Duration) float64 {
   225  	min := d / time.Minute
   226  	nsec := d % time.Minute
   227  	return float64(min) + float64(nsec)/(60*1e9)
   228  }
   229  
   230  // DurationTotalHours returns the value of the time.Duration expressed in whole and fractional hours.
   231  func DurationTotalHours(d time.Duration) float64 {
   232  	hour := d / time.Hour
   233  	nsec := d % time.Hour
   234  	return float64(hour) + float64(nsec)/(60*60*1e9)
   235  }
   236  
   237  // DurationTotalDays returns the value of the time.Duration expressed in whole and fractional days.
   238  func DurationTotalDays(d time.Duration) float64 {
   239  	day := d / (time.Hour * 24)
   240  	nsec := d % (time.Hour * 24)
   241  	return float64(day) + float64(nsec)/(24*60*60*1e9)
   242  }
   243  
   244  // =====
   245  // clock
   246  // =====
   247  
   248  // Clock represents an interface used to determine the current time.
   249  type Clock interface {
   250  	Now() time.Time
   251  }
   252  
   253  // clockFn is an unexported type that implements Clock interface, see UTC and Local.
   254  type clockFn func() time.Time
   255  
   256  // Now implements the Clock interface.
   257  func (c clockFn) Now() time.Time {
   258  	return c()
   259  }
   260  
   261  var _ Clock = (*clockFn)(nil)
   262  
   263  var (
   264  	// UTC is a function that satisfies the Clock interface, which returns the current time in UTC timezone.
   265  	UTC Clock = clockFn(func() time.Time { return time.Now().UTC() })
   266  
   267  	// Local is a function that satisfies the Clock interface, which returns the current time in local timezone.
   268  	Local Clock = clockFn(time.Now)
   269  )
   270  
   271  // CustomClock returns a custom Clock with given time.Time pointer.
   272  func CustomClock(t *time.Time) Clock {
   273  	return clockFn(func() time.Time { return *t })
   274  }