github.com/songzhibin97/gkit@v1.2.13/internal/clock/time.go (about) 1 package clock 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 ) 8 9 const ( 10 TimeFormat = "2006-01-02 15:04:05" 11 DateFormat = "2006-01-02" 12 UnixTimeUnitOffset = uint64(time.Millisecond / time.Nanosecond) 13 ) 14 15 var ( 16 _ Clock = &RealClock{} 17 _ Clock = &MockClock{} 18 19 _ Ticker = &RealTicker{} 20 _ Ticker = &MockTicker{} 21 22 _ TickerCreator = &RealTickerCreator{} 23 _ TickerCreator = &MockTickerCreator{} 24 ) 25 26 var ( 27 currentClock *atomic.Value 28 currentTickerCreator *atomic.Value 29 ) 30 31 func init() { 32 realClock := NewRealClock() 33 currentClock = new(atomic.Value) 34 SetClock(realClock) 35 36 realTickerCreator := NewRealTickerCreator() 37 currentTickerCreator = new(atomic.Value) 38 SetTickerCreator(realTickerCreator) 39 } 40 41 // Clock 时钟接口 42 type Clock interface { 43 Now() time.Time 44 Sleep(d time.Duration) 45 GetTimeMillis() uint64 46 GetTimeNano() uint64 47 } 48 49 // clockWrapper is used for atomic operation. 50 type clockWrapper struct { 51 clock Clock 52 } 53 54 // RealClock 真实使用的Clock对象 55 type RealClock struct{} 56 57 func NewRealClock() *RealClock { 58 return &RealClock{} 59 } 60 61 func (t *RealClock) Now() time.Time { 62 return time.Now() 63 } 64 65 func (t *RealClock) Sleep(d time.Duration) { 66 time.Sleep(d) 67 } 68 69 func (t *RealClock) GetTimeMillis() uint64 { 70 tickerNow := GetTimestamp() 71 if tickerNow > uint64(0) { 72 return tickerNow 73 } 74 return uint64(time.Now().UnixNano()) / UnixTimeUnitOffset 75 } 76 77 func (t *RealClock) GetTimeNano() uint64 { 78 return uint64(t.Now().UnixNano()) 79 } 80 81 // MockClock 测试使用的Clock对象 82 type MockClock struct { 83 lock sync.RWMutex 84 now time.Time 85 } 86 87 func NewMockClock() *MockClock { 88 return &MockClock{ 89 now: time.Now(), 90 } 91 } 92 93 func (t *MockClock) Now() time.Time { 94 t.lock.RLock() 95 defer t.lock.RUnlock() 96 return t.now 97 } 98 99 func (t *MockClock) Sleep(d time.Duration) { 100 if d <= 0 { 101 return 102 } 103 t.lock.Lock() 104 t.now = t.now.Add(d) 105 t.lock.Unlock() 106 time.Sleep(time.Millisecond) 107 } 108 109 func (t *MockClock) GetTimeMillis() uint64 { 110 return uint64(t.Now().UnixNano()) / UnixTimeUnitOffset 111 } 112 113 func (t *MockClock) GetTimeNano() uint64 { 114 return uint64(t.Now().UnixNano()) 115 } 116 117 // Ticker time.Ticker 对象封装 118 type Ticker interface { 119 C() <-chan time.Time 120 Stop() 121 } 122 123 // RealTicker 真实使用的 Ticker 对象 124 type RealTicker struct { 125 t *time.Ticker 126 } 127 128 func NewRealTicker(d time.Duration) *RealTicker { 129 return &RealTicker{ 130 t: time.NewTicker(d), 131 } 132 } 133 134 func (t *RealTicker) C() <-chan time.Time { 135 return t.t.C 136 } 137 138 func (t *RealTicker) Stop() { 139 t.t.Stop() 140 } 141 142 // MockTicker 测试使用的 Ticker 对象 143 // MockTicker 和 MockClock 一般搭配使用 144 type MockTicker struct { 145 lock sync.Mutex 146 period time.Duration 147 c chan time.Time 148 last time.Time 149 stop chan struct{} 150 } 151 152 func NewMockTicker(d time.Duration) *MockTicker { 153 t := &MockTicker{ 154 period: d, 155 c: make(chan time.Time, 1), 156 last: Now(), 157 stop: make(chan struct{}), 158 } 159 160 go t.checkLoop() 161 162 return t 163 } 164 165 func (t *MockTicker) C() <-chan time.Time { 166 return t.c 167 } 168 169 func (t *MockTicker) Stop() { 170 close(t.stop) 171 } 172 173 func (t *MockTicker) check() { 174 t.lock.Lock() 175 defer t.lock.Unlock() 176 177 now := Now() 178 for next := t.last.Add(t.period); !next.After(now); next = next.Add(t.period) { 179 t.last = next 180 select { 181 case <-t.stop: 182 return 183 case t.c <- t.last: 184 default: 185 } 186 } 187 } 188 189 func (t *MockTicker) checkLoop() { 190 ticker := time.NewTicker(time.Microsecond) 191 for { 192 select { 193 case <-t.stop: 194 return 195 case <-ticker.C: 196 } 197 t.check() 198 } 199 } 200 201 // TickerCreator 实例化Ticker. 202 type TickerCreator interface { 203 NewTicker(d time.Duration) Ticker 204 } 205 206 // tickerCreatorWrapper 封装 atomic 操作 207 type tickerCreatorWrapper struct { 208 tickerCreator TickerCreator 209 } 210 211 // RealTickerCreator 创建真实的 RealTicker 和 time.Ticker 对象. 212 type RealTickerCreator struct{} 213 214 func NewRealTickerCreator() *RealTickerCreator { 215 return &RealTickerCreator{} 216 } 217 218 func (tc *RealTickerCreator) NewTicker(d time.Duration) Ticker { 219 return NewRealTicker(d) 220 } 221 222 // MockTickerCreator 创建 MockTicker 用于测试 223 // MockTickerCreator 和 MockClock 通常一起使用 224 type MockTickerCreator struct{} 225 226 func NewMockTickerCreator() *MockTickerCreator { 227 return &MockTickerCreator{} 228 } 229 230 func (tc *MockTickerCreator) NewTicker(d time.Duration) Ticker { 231 return NewMockTicker(d) 232 } 233 234 // SetClock 设置 Clock 235 func SetClock(c Clock) { 236 currentClock.Store(&clockWrapper{c}) 237 } 238 239 // CurrentClock 返回 Clock 对象 240 func CurrentClock() Clock { 241 return currentClock.Load().(*clockWrapper).clock 242 } 243 244 // SetTickerCreator 设置 Ticker 对象. 245 func SetTickerCreator(tc TickerCreator) { 246 currentTickerCreator.Store(&tickerCreatorWrapper{tc}) 247 } 248 249 // CurrentTickerCreator 获取 Ticker 对象 250 func CurrentTickerCreator() TickerCreator { 251 return currentTickerCreator.Load().(*tickerCreatorWrapper).tickerCreator 252 } 253 254 func NewTicker(d time.Duration) Ticker { 255 return CurrentTickerCreator().NewTicker(d) 256 } 257 258 // FormatTimeMillis 将Unix时间戳(ms)格式化为时间字符串 259 func FormatTimeMillis(tsMillis uint64) string { 260 return time.Unix(0, int64(tsMillis*UnixTimeUnitOffset)).Format(TimeFormat) 261 } 262 263 // FormatDate 将Unix时间戳(ms)格式化为日期字符串 264 func FormatDate(tsMillis uint64) string { 265 return time.Unix(0, int64(tsMillis*UnixTimeUnitOffset)).Format(DateFormat) 266 } 267 268 // GetTimeMillis 返回当前的Unix时间戳(ms) 269 func GetTimeMillis() uint64 { 270 return CurrentClock().GetTimeMillis() 271 } 272 273 // GetTimeNano 返回当前的Unix时间戳(ns) 274 func GetTimeNano() uint64 { 275 return CurrentClock().GetTimeNano() 276 } 277 278 // Now 返回当前本地时间。 279 func Now() time.Time { 280 return CurrentClock().Now() 281 } 282 283 func Sleep(d time.Duration) { 284 CurrentClock().Sleep(d) 285 }