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  }