time.go raw

   1  package time
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"math/big"
   7  	"strings"
   8  	"time"
   9  )
  10  
  11  const (
  12  	// dateTimeFormat is a IMF-fixdate formatted RFC3339 section 5.6
  13  	dateTimeFormatInput    = "2006-01-02T15:04:05.999999999Z"
  14  	dateTimeFormatInputNoZ = "2006-01-02T15:04:05.999999999"
  15  	dateTimeFormatOutput   = "2006-01-02T15:04:05.999Z"
  16  
  17  	// httpDateFormat is a date time defined by RFC 7231#section-7.1.1.1
  18  	// IMF-fixdate with no UTC offset.
  19  	httpDateFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
  20  	// Additional formats needed for compatibility.
  21  	httpDateFormatSingleDigitDay             = "Mon, _2 Jan 2006 15:04:05 GMT"
  22  	httpDateFormatSingleDigitDayTwoDigitYear = "Mon, _2 Jan 06 15:04:05 GMT"
  23  )
  24  
  25  var millisecondFloat = big.NewFloat(1e3)
  26  
  27  // FormatDateTime formats value as a date-time, (RFC3339 section 5.6)
  28  //
  29  // Example: 1985-04-12T23:20:50.52Z
  30  func FormatDateTime(value time.Time) string {
  31  	return value.UTC().Format(dateTimeFormatOutput)
  32  }
  33  
  34  // ParseDateTime parses a string as a date-time, (RFC3339 section 5.6)
  35  //
  36  // Example: 1985-04-12T23:20:50.52Z
  37  func ParseDateTime(value string) (time.Time, error) {
  38  	return tryParse(value,
  39  		dateTimeFormatInput,
  40  		dateTimeFormatInputNoZ,
  41  		time.RFC3339Nano,
  42  		time.RFC3339,
  43  	)
  44  }
  45  
  46  // FormatHTTPDate formats value as a http-date, (RFC 7231#section-7.1.1.1 IMF-fixdate)
  47  //
  48  // Example: Tue, 29 Apr 2014 18:30:38 GMT
  49  func FormatHTTPDate(value time.Time) string {
  50  	return value.UTC().Format(httpDateFormat)
  51  }
  52  
  53  // ParseHTTPDate parses a string as a http-date, (RFC 7231#section-7.1.1.1 IMF-fixdate)
  54  //
  55  // Example: Tue, 29 Apr 2014 18:30:38 GMT
  56  func ParseHTTPDate(value string) (time.Time, error) {
  57  	return tryParse(value,
  58  		httpDateFormat,
  59  		httpDateFormatSingleDigitDay,
  60  		httpDateFormatSingleDigitDayTwoDigitYear,
  61  		time.RFC850,
  62  		time.ANSIC,
  63  	)
  64  }
  65  
  66  // FormatEpochSeconds returns value as a Unix time in seconds with with decimal precision
  67  //
  68  // Example: 1515531081.123
  69  func FormatEpochSeconds(value time.Time) float64 {
  70  	ms := value.UnixNano() / int64(time.Millisecond)
  71  	return float64(ms) / 1e3
  72  }
  73  
  74  // ParseEpochSeconds returns value as a Unix time in seconds with with decimal precision
  75  //
  76  // Example: 1515531081.123
  77  func ParseEpochSeconds(value float64) time.Time {
  78  	f := big.NewFloat(value)
  79  	f = f.Mul(f, millisecondFloat)
  80  	i, _ := f.Int64()
  81  	// Offset to `UTC` because time.Unix returns the time value based on system
  82  	// local setting.
  83  	return time.Unix(0, i*1e6).UTC()
  84  }
  85  
  86  func tryParse(v string, formats ...string) (time.Time, error) {
  87  	var errs parseErrors
  88  	for _, f := range formats {
  89  		t, err := time.Parse(f, v)
  90  		if err != nil {
  91  			errs = append(errs, parseError{
  92  				Format: f,
  93  				Err:    err,
  94  			})
  95  			continue
  96  		}
  97  		return t, nil
  98  	}
  99  
 100  	return time.Time{}, fmt.Errorf("unable to parse time string, %w", errs)
 101  }
 102  
 103  type parseErrors []parseError
 104  
 105  func (es parseErrors) Error() string {
 106  	var s strings.Builder
 107  	for _, e := range es {
 108  		fmt.Fprintf(&s, "\n * %q: %v", e.Format, e.Err)
 109  	}
 110  
 111  	return "parse errors:" + s.String()
 112  }
 113  
 114  type parseError struct {
 115  	Format string
 116  	Err    error
 117  }
 118  
 119  // SleepWithContext will wait for the timer duration to expire, or until the context
 120  // is canceled. Whichever happens first. If the context is canceled the
 121  // Context's error will be returned.
 122  func SleepWithContext(ctx context.Context, dur time.Duration) error {
 123  	t := time.NewTimer(dur)
 124  	defer t.Stop()
 125  
 126  	select {
 127  	case <-t.C:
 128  		break
 129  	case <-ctx.Done():
 130  		return ctx.Err()
 131  	}
 132  
 133  	return nil
 134  }
 135