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