github.com/wfusion/gofusion@v1.1.14/common/utils/time.go (about) 1 package utils 2 3 import ( 4 "context" 5 "errors" 6 "math/rand" 7 "sync" 8 "time" 9 10 "go.uber.org/multierr" 11 ) 12 13 // UnixNano returns t as a Unix time, the number of nanoseconds elapsed 14 // since January 1, 1970 UTC. The result is undefined if the Unix time 15 // in nanoseconds cannot be represented by an int64 (a date before the year 16 // 1678 or after 2262). Note that this means the result of calling UnixNano 17 // on the zero Time is undefined. The result does not depend on the 18 // location associated with t. 19 const ( 20 minYear = 1678 21 maxYear = 2262 22 ) 23 24 // GetTime 将毫秒级的时间戳转换成时间 25 func GetTime(timestampMs int64) time.Time { 26 return time.UnixMilli(timestampMs) 27 } 28 29 // GetTimeStamp 将时间转换成毫秒级的时间戳 30 func GetTimeStamp(t time.Time) int64 { 31 if year := t.Year(); year >= maxYear || year < minYear { 32 return t.Unix() * 1e3 33 } 34 return t.UnixNano() / 1e6 35 } 36 37 // IsValidTimestamp 返回 false 表示无法对毫秒时间戳和 time.Time 进行精确转换 38 func IsValidTimestamp(timeMS int64) bool { 39 year := GetTime(timeMS).Year() 40 return year >= minYear && year < maxYear 41 } 42 43 type loopWithIntervalOption struct { 44 maxTimes uint 45 // jitter time 46 base, max time.Duration 47 ratio, exp float64 48 symmetric bool 49 } 50 51 // LoopJitterInterval Deprecated, try github.com/rican7/retry.Retry instead 52 func LoopJitterInterval(base, max time.Duration, ratio, exp float64, 53 symmetric bool) OptionFunc[loopWithIntervalOption] { 54 return func(o *loopWithIntervalOption) { 55 o.base = base 56 o.max = max 57 o.ratio = ratio 58 o.exp = exp 59 o.symmetric = symmetric 60 } 61 } 62 63 // LoopMaxTimes Deprecated, try github.com/rican7/retry.Retry instead 64 func LoopMaxTimes(maxTimes uint) OptionFunc[loopWithIntervalOption] { 65 return func(o *loopWithIntervalOption) { 66 o.maxTimes = maxTimes 67 } 68 } 69 70 // LoopWithInterval Deprecated, try github.com/rican7/retry.Retry instead 71 func LoopWithInterval(ctx context.Context, interval time.Duration, 72 fn func() bool, opts ...OptionExtender) (err error) { 73 var ( 74 maxTimes uint 75 nextInterval func() time.Duration 76 ) 77 78 opt := ApplyOptions[loopWithIntervalOption](opts...) 79 enableJitter := opt.base > 0 80 enableMaxTimes := opt.maxTimes > 0 81 if enableJitter { 82 nextInterval = NextJitterIntervalFunc(opt.base, opt.max, opt.ratio, opt.exp, opt.symmetric) 83 interval = nextInterval() 84 } 85 if enableMaxTimes { 86 maxTimes = opt.maxTimes 87 } 88 89 timer := time.NewTimer(interval) 90 defer timer.Stop() 91 for { 92 if fn() { 93 return 94 } 95 96 if enableMaxTimes { 97 if maxTimes--; maxTimes == 0 { 98 return multierr.Append(err, errors.New("exceed the maximum times")) 99 } 100 } 101 102 // time.Sleep 103 timer.Reset(interval) 104 select { 105 case <-ctx.Done(): 106 return ctx.Err() 107 case <-timer.C: 108 if enableJitter { 109 interval = nextInterval() 110 } 111 } 112 } 113 } 114 115 // NextJitterIntervalFunc generate a jitter and exponential power duration, inspired by net/http.(*Server).Shutdown 116 func NextJitterIntervalFunc(base, max time.Duration, ratio, exp float64, symmetric bool) func() time.Duration { 117 return func() (interval time.Duration) { 118 // add specified ratio jitter 119 if !symmetric { 120 // if ratio is 0.5 121 // then interval = base + random(0.5*base) <=> [base, 1.5*base) 122 // if ratio is 0.1 123 // then interval = base + random(0.1*base) <=> [base, 1.1*base) 124 _range := float64(base) * ratio * rand.Float64() 125 interval = base + time.Duration(_range) 126 } else { 127 // if ratio is 0.5 128 // then interval = base + random(0.5*base) - 0.25*base <=> [0.75*base, 1.25*base) 129 // if ratio is 0.1 130 // then interval = base + random(0.1*base) - 0.05*base <=> [0.95*base, 1.05*base) 131 _range := float64(base) * ratio * rand.Float64() 132 interval = base + time.Duration(_range) - time.Duration(float64(base)*(ratio/2)) 133 } 134 135 // double and clamp for next time 136 base = time.Duration(float64(base) * exp) 137 if base > max { 138 base = max 139 } 140 return interval 141 } 142 } 143 144 // WaitGroupTimeout adds timeout feature for sync.WaitGroup.Wait(). 145 // It returns true, when timeout. 146 type timeoutOption struct { 147 wg *sync.WaitGroup 148 } 149 150 func TimeoutWg(wg *sync.WaitGroup) OptionFunc[timeoutOption] { 151 return func(o *timeoutOption) { 152 o.wg = wg 153 } 154 } 155 156 func Timeout(timeout time.Duration, opts ...OptionExtender) bool { 157 opt := ApplyOptions[timeoutOption](opts...) 158 wgClosed := make(chan struct{}, 1) 159 go func() { 160 switch { 161 case opt.wg != nil: 162 opt.wg.Wait() 163 } 164 wgClosed <- struct{}{} 165 }() 166 167 timer := time.NewTimer(timeout) 168 defer timer.Stop() 169 select { 170 case <-wgClosed: 171 return false 172 case <-timer.C: 173 return true 174 } 175 }