├── go.mod ├── renovate.json ├── assets └── logo.png ├── now_windows.go ├── .whitesource ├── .gitignore ├── .reviewdog.yml ├── now.go ├── fastime_bench_test.go ├── example └── main.go ├── LICENSE ├── Makefile ├── README.md ├── .circleci └── config.yml ├── fastime.go └── fastime_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kpango/fastime 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ice3man543/fastime/master/assets/logo.png -------------------------------------------------------------------------------- /now_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package fastime 4 | 5 | import "time" 6 | 7 | func (f *Fastime) now() time.Time { 8 | return time.Now() 9 | } 10 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "generalSettings": { 3 | "shouldScanRepo": true 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure" 7 | } 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /.reviewdog.yml: -------------------------------------------------------------------------------- 1 | runner: 2 | golint: 3 | cmd: golint $(go list ./...) 4 | megacheck: 5 | cmd: megacheck $(go list ./...) 6 | errorformat: 7 | - "%f:%l:%c:%m" 8 | errcheck: 9 | cmd: errcheck -asserts -ignoretests -blank $(go list ./...) 10 | errorformat: 11 | - "%f:%l:%c:%m" 12 | -------------------------------------------------------------------------------- /now.go: -------------------------------------------------------------------------------- 1 | // +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris 2 | 3 | package fastime 4 | 5 | import ( 6 | "syscall" 7 | "time" 8 | ) 9 | 10 | func (f *Fastime) now() time.Time { 11 | var tv syscall.Timeval 12 | err := syscall.Gettimeofday(&tv) 13 | if err != nil { 14 | return time.Now() 15 | } 16 | return time.Unix(0, syscall.TimevalToNsec(tv)) 17 | } 18 | -------------------------------------------------------------------------------- /fastime_bench_test.go: -------------------------------------------------------------------------------- 1 | package fastime 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | // BenchmarkFastime 9 | func BenchmarkFastime(b *testing.B) { 10 | b.ReportAllocs() 11 | b.ResetTimer() 12 | b.RunParallel(func(pb *testing.PB) { 13 | for pb.Next() { 14 | Now() 15 | } 16 | }) 17 | } 18 | 19 | // BenchmarkTime 20 | func BenchmarkTime(b *testing.B) { 21 | b.ReportAllocs() 22 | b.ResetTimer() 23 | b.RunParallel(func(pb *testing.PB) { 24 | for pb.Next() { 25 | time.Now() 26 | } 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/kpango/fastime" 8 | ) 9 | 10 | func main() { 11 | s1 := fastime.Now() 12 | s2 := fastime.Now() 13 | s3 := fastime.Now() 14 | time.Sleep(time.Second * 2) 15 | s4 := fastime.Now() 16 | 17 | time.Sleep(time.Second * 5) 18 | 19 | fmt.Printf("s1=%v\ns2=%v\ns3=%v\ns4=%v\n", s1, s2, s3, s4) 20 | 21 | fmt.Printf("nanonow %v\nnow unixnano %v\nnow add unixnano%v\nnanonow + dur %v\nstring %v", 22 | fastime.UnixNanoNow(), 23 | fastime.Now().Unix(), 24 | fastime.Now().Add(time.Second), 25 | fastime.UnixNanoNow()+int64(time.Second), 26 | string(fastime.FormattedNow())) 27 | 28 | for i := 0; i < 30; i++ { 29 | time.Sleep(time.Millisecond * 400) 30 | fmt.Println(fastime.Now()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yusuke Kato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO_VERSION:=$(shell go version) 2 | 3 | .PHONY: all clean bench bench-all profile lint test contributors update install 4 | 5 | all: clean install lint test bench 6 | 7 | clean: 8 | go clean ./... 9 | rm -rf ./*.log 10 | rm -rf ./*.svg 11 | rm -rf ./go.mod 12 | rm -rf ./go.sum 13 | rm -rf bench 14 | rm -rf pprof 15 | rm -rf vendor 16 | 17 | 18 | bench: clean 19 | go test -count=10 -run=NONE -bench . -benchmem 20 | 21 | profile: clean 22 | rm -rf bench 23 | mkdir bench 24 | mkdir pprof 25 | \ 26 | go test -count=10 -run=NONE -bench=BenchmarkFastime -benchmem -o pprof/fastime-test.bin -cpuprofile pprof/cpu-fastime.out -memprofile pprof/mem-fastime.out 27 | go tool pprof --svg pprof/fastime-test.bin pprof/cpu-fastime.out > cpu-fastime.svg 28 | go tool pprof --svg pprof/fastime-test.bin pprof/mem-fastime.out > mem-fastime.svg 29 | go-torch -f bench/cpu-fastime-graph.svg pprof/fastime-test.bin pprof/cpu-fastime.out 30 | go-torch --alloc_objects -f bench/mem-fastime-graph.svg pprof/fastime-test.bin pprof/mem-fastime.out 31 | \ 32 | go test -count=10 -run=NONE -bench=BenchmarkTime -benchmem -o pprof/default-test.bin -cpuprofile pprof/cpu-default.out -memprofile pprof/mem-default.out 33 | go tool pprof --svg pprof/default-test.bin pprof/mem-default.out > mem-default.svg 34 | go tool pprof --svg pprof/default-test.bin pprof/cpu-default.out > cpu-default.svg 35 | go-torch -f bench/cpu-default-graph.svg pprof/default-test.bin pprof/cpu-default.out 36 | go-torch --alloc_objects -f bench/mem-default-graph.svg pprof/default-test.bin pprof/mem-default.out 37 | \ 38 | mv ./*.svg bench/ 39 | 40 | cpu: 41 | go tool pprof pprof/fastime-test.bin pprof/cpu-fastime.out 42 | 43 | mem: 44 | go tool pprof --alloc_space pprof/fastime-test.bin pprof/mem-fastime.out 45 | 46 | lint: 47 | gometalinter --enable-all . | rg -v comment 48 | 49 | test: clean 50 | GO111MODULE=on go test --race -v $(go list ./... | rg -v vendor) 51 | 52 | contributors: 53 | git log --format='%aN <%aE>' | sort -fu > CONTRIBUTORS 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 7 | [![release](https://img.shields.io/github/release/kpango/fastime.svg)](https://github.com/kpango/fastime/releases/latest) 8 | [![CircleCI](https://circleci.com/gh/kpango/fastime.svg?style=shield)](https://circleci.com/gh/kpango/fastime) 9 | [![codecov](https://codecov.io/gh/kpango/fastime/branch/master/graph/badge.svg)](https://codecov.io/gh/kpango/fastime) 10 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b9fa9b846ec343d3860b8f69e802c09b)](https://www.codacy.com/app/i.can.feel.gravity/fastime?utm_source=github.com&utm_medium=referral&utm_content=kpango/fastime&utm_campaign=Badge_Grade) 11 | [![Go Report Card](https://goreportcard.com/badge/github.com/kpango/fastime)](https://goreportcard.com/report/github.com/kpango/fastime) 12 | [![GoDoc](http://godoc.org/github.com/kpango/fastime?status.svg)](http://godoc.org/github.com/kpango/fastime) 13 | [![Join the chat at https://gitter.im/kpango/fastime](https://badges.gitter.im/kpango/fastime.svg)](https://gitter.im/kpango/fastime?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 14 | fastime is a super fast time function library for Go with zero memory allocation. fastime returns the approximate time. 15 | 16 | ## Requirement 17 | Go 1.1 18 | 19 | ## Installation 20 | ```shell 21 | go get github.com/kpango/fastime 22 | ``` 23 | 24 | ## Example 25 | ```go 26 | now := fastime.Now() 27 | defer fastime.Stop() 28 | 29 | // Create Instance 30 | ft := fastime.New() 31 | defer ft.Stop() 32 | ft.Now() 33 | ``` 34 | 35 | ## Benchmark 36 | 37 | ``` 38 | go test -count=10 -run=NONE -bench . -benchmem 39 | goos: linux 40 | goarch: amd64 41 | pkg: github.com/kpango/fastime 42 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 43 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 44 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 45 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 46 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 47 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 48 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 49 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 50 | BenchmarkFastime-8 2000000000 0.45 ns/op 0 B/op 0 allocs/op 51 | BenchmarkFastime-8 2000000000 0.46 ns/op 0 B/op 0 allocs/op 52 | BenchmarkTime-8 1000000 1683 ns/op 0 B/op 0 allocs/op 53 | BenchmarkTime-8 1000000 1720 ns/op 0 B/op 0 allocs/op 54 | BenchmarkTime-8 1000000 1688 ns/op 0 B/op 0 allocs/op 55 | BenchmarkTime-8 1000000 1716 ns/op 0 B/op 0 allocs/op 56 | BenchmarkTime-8 1000000 1691 ns/op 0 B/op 0 allocs/op 57 | BenchmarkTime-8 1000000 1693 ns/op 0 B/op 0 allocs/op 58 | BenchmarkTime-8 1000000 1703 ns/op 0 B/op 0 allocs/op 59 | BenchmarkTime-8 1000000 1668 ns/op 0 B/op 0 allocs/op 60 | BenchmarkTime-8 1000000 1685 ns/op 0 B/op 0 allocs/op 61 | BenchmarkTime-8 1000000 1716 ns/op 0 B/op 0 allocs/op 62 | PASS 63 | ok github.com/kpango/fastime 26.873s 64 | ``` 65 | ## Contribution 66 | 1. Fork it ( https://github.com/kpango/fastime/fork ) 67 | 2. Create your feature branch (git checkout -b my-new-feature) 68 | 3. Commit your changes (git commit -am 'Add some feature') 69 | 4. Push to the branch (git push origin my-new-feature) 70 | 5. Create new Pull Request 71 | 72 | ## Author 73 | [kpango](https://github.com/kpango) 74 | 75 | ## LICENSE 76 | fastime released under MIT license, refer [LICENSE](https://github.com/kpango/fastime/blob/master/LICENSE) file. 77 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | alias: 2 | default: &default 3 | working_directory: /go/src/github.com/kpango/fastime 4 | docker: 5 | - image: circleci/golang:1.13.1 6 | environment: 7 | GOPATH: "/go" 8 | GO111MODULE: "on" 9 | REPO_NAME: "kpango" 10 | IMAGE_NAME: "fastime" 11 | GITHUB_API: "https://api.github.com/" 12 | DOCKER_USER: "kpango" 13 | setup_remote_docker: &setup_remote_docker 14 | version: 18.06.0-ce 15 | docker_layer_caching: true 16 | 17 | version: 2 18 | jobs: 19 | test: 20 | <<: *default 21 | steps: 22 | - checkout 23 | - restore_cache: 24 | key: gosum-{{ .Branch }}-{{ checksum "go.sum" }} 25 | - run: 26 | name: preparation 27 | command: | 28 | go mod vendor 29 | - run: 30 | name: run tests 31 | command: | 32 | go test -v -race -covermode=atomic -coverprofile=coverage.out ./... 33 | go tool cover -html=coverage.out -o coverage.html 34 | bash <(curl -s https://codecov.io/bash) 35 | - store_artifacts: 36 | path: ./coverage.html 37 | - save_cache: 38 | key: gosum-{{ .Branch }}-{{ checksum "go.sum" }} 39 | paths: 40 | - ./vendor 41 | versioning: 42 | <<: *default 43 | steps: 44 | - checkout 45 | - run: 46 | name: check 47 | command: | 48 | mkdir -p $HOME/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config 49 | LAST_COMMIT=`git log -1 --pretty=%B` 50 | VERSION=`git describe --abbrev=0 --tags` 51 | touch ./.tag 52 | if [ ! -z "`git diff $VERSION`" -o -z "$VERSION" ]; then 53 | VERSION=${VERSION:-'0.0.0'} 54 | MAJOR="${VERSION%%.*}"; VERSION="${VERSION#*.}" 55 | MINOR="${VERSION%%.*}"; VERSION="${VERSION#*.}" 56 | PATCH="${VERSION%%.*}"; VERSION="${VERSION#*.}" 57 | if echo $LAST_COMMIT | grep "\[\(major\|MAJOR\)\]" > /dev/null; then 58 | MAJOR=$((MAJOR+1)) 59 | echo "$MAJOR.0.0" > ./.tag 60 | elif echo $LAST_COMMIT | grep "\[\(minor\|MINOR\)\]" > /dev/null; then 61 | MINOR=$((MINOR+1)) 62 | echo "$MAJOR.$MINOR.0" > ./.tag 63 | elif echo $LAST_COMMIT | grep "\[\(patch\|PATCH\)\]" > /dev/null; then 64 | PATCH=$((PATCH+1)) 65 | echo "$MAJOR.$MINOR.$PATCH" > ./.tag 66 | fi 67 | fi 68 | - persist_to_workspace: 69 | root: . 70 | paths: 71 | - . 72 | push: 73 | <<: *default 74 | steps: 75 | - attach_workspace: 76 | at: . 77 | - run: 78 | name: push tag and check PR body 79 | command: | 80 | mkdir -p $HOME/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config 81 | TAG=`cat ./.tag` 82 | if [ ! -z "$TAG" ]; then 83 | echo $TAG 84 | git tag $TAG 85 | git push https://${GITHUB_ACCESS_TOKEN}:x-oauth-basic@github.com/${REPO_NAME}/${IMAGE_NAME} --tags 86 | fi 87 | - persist_to_workspace: 88 | root: . 89 | paths: 90 | - . 91 | gh_release: 92 | <<: *default 93 | steps: 94 | - attach_workspace: 95 | at: . 96 | - run: 97 | name: release 98 | command: | 99 | mkdir -p $HOME/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config 100 | TAG=`cat ./.tag` 101 | if [ ! -z "$TAG" ]; then 102 | echo "Create release: ${TAG}" 103 | curl -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" \ 104 | -X POST \ 105 | -d "{\"tag_name\": \"${TAG}\"}" \ 106 | ${GITHUB_API}repos/${REPO_NAME}/${IMAGE_NAME}/releases 107 | fi 108 | 109 | workflows: 110 | version: 2 111 | build: 112 | jobs: 113 | - test 114 | - versioning: 115 | filters: 116 | branches: 117 | only: 118 | - master 119 | - push: 120 | requires: 121 | - versioning 122 | - gh_release: 123 | requires: 124 | - push 125 | -------------------------------------------------------------------------------- /fastime.go: -------------------------------------------------------------------------------- 1 | package fastime 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | // Fastime is fastime's base struct, it's stores atomic time object 12 | type Fastime struct { 13 | running *atomic.Value 14 | t *atomic.Value 15 | ut int64 16 | unt int64 17 | uut uint32 18 | uunt uint32 19 | ft *atomic.Value 20 | format *atomic.Value 21 | cancel context.CancelFunc 22 | correctionDur time.Duration 23 | dur int64 24 | } 25 | 26 | var ( 27 | once sync.Once 28 | instance *Fastime 29 | ) 30 | 31 | func init() { 32 | once.Do(func() { 33 | instance = New().StartTimerD(context.Background(), time.Millisecond*5) 34 | }) 35 | } 36 | 37 | // New returns Fastime 38 | func New() *Fastime { 39 | running := new(atomic.Value) 40 | running.Store(false) 41 | return (&Fastime{ 42 | t: new(atomic.Value), 43 | running: running, 44 | format: func() *atomic.Value { 45 | av := new(atomic.Value) 46 | av.Store(time.RFC3339) 47 | return av 48 | }(), 49 | ft: func() *atomic.Value { 50 | av := new(atomic.Value) 51 | av.Store(make([]byte, 0, len(time.RFC3339))[:0]) 52 | return av 53 | }(), 54 | correctionDur: time.Millisecond * 100, 55 | }).refresh() 56 | } 57 | 58 | func (f *Fastime) update() *Fastime { 59 | return f.store(f.Now().Add(time.Duration(atomic.LoadInt64(&f.dur)))) 60 | } 61 | 62 | func (f *Fastime) refresh() *Fastime { 63 | return f.store(f.now()) 64 | } 65 | 66 | func (f *Fastime) store(t time.Time) *Fastime { 67 | f.t.Store(t) 68 | ut := t.Unix() 69 | unt := t.UnixNano() 70 | atomic.StoreInt64(&f.ut, ut) 71 | atomic.StoreInt64(&f.unt, unt) 72 | atomic.StoreUint32(&f.uut, *(*uint32)(unsafe.Pointer(&ut))) 73 | atomic.StoreUint32(&f.uunt, *(*uint32)(unsafe.Pointer(&unt))) 74 | form := f.format.Load().(string) 75 | f.ft.Store(t.AppendFormat(make([]byte, 0, len(form)), form)) 76 | return f 77 | } 78 | 79 | // SetFormat replaces time format 80 | func SetFormat(format string) *Fastime { 81 | return instance.SetFormat(format) 82 | } 83 | 84 | // SetFormat replaces time format 85 | func (f *Fastime) SetFormat(format string) *Fastime { 86 | f.format.Store(format) 87 | f.refresh() 88 | return f 89 | } 90 | 91 | // Now returns current time 92 | func Now() time.Time { 93 | return instance.Now() 94 | } 95 | 96 | // Stop stops stopping time refresh daemon 97 | func Stop() { 98 | instance.Stop() 99 | } 100 | 101 | // UnixNow returns current unix time 102 | func UnixNow() int64 { 103 | return instance.UnixNow() 104 | } 105 | 106 | // UnixNow returns current unix time 107 | func UnixUNow() uint32 { 108 | return instance.UnixUNow() 109 | } 110 | 111 | // UnixNanoNow returns current unix nano time 112 | func UnixNanoNow() int64 { 113 | return instance.UnixNanoNow() 114 | } 115 | 116 | // UnixNanoNow returns current unix nano time 117 | func UnixUNanoNow() uint32 { 118 | return instance.UnixUNanoNow() 119 | } 120 | 121 | // FormattedNow returns formatted byte time 122 | func FormattedNow() []byte { 123 | return instance.FormattedNow() 124 | } 125 | 126 | // StartTimerD provides time refresh daemon 127 | func StartTimerD(ctx context.Context, dur time.Duration) *Fastime { 128 | return instance.StartTimerD(ctx, dur) 129 | } 130 | 131 | // Now returns current time 132 | func (f *Fastime) Now() time.Time { 133 | return f.t.Load().(time.Time) 134 | } 135 | 136 | // Stop stops stopping time refresh daemon 137 | func (f *Fastime) Stop() { 138 | if f.running.Load().(bool) { 139 | f.cancel() 140 | atomic.StoreInt64(&f.dur, 0) 141 | return 142 | } 143 | } 144 | 145 | // UnixNow returns current unix time 146 | func (f *Fastime) UnixNow() int64 { 147 | return atomic.LoadInt64(&f.ut) 148 | } 149 | 150 | // UnixNow returns current unix time 151 | func (f *Fastime) UnixUNow() uint32 { 152 | return atomic.LoadUint32(&f.uut) 153 | } 154 | 155 | // UnixNanoNow returns current unix nano time 156 | func (f *Fastime) UnixNanoNow() int64 { 157 | return atomic.LoadInt64(&f.unt) 158 | } 159 | 160 | // UnixNanoNow returns current unix nano time 161 | func (f *Fastime) UnixUNanoNow() uint32 { 162 | return atomic.LoadUint32(&f.uunt) 163 | } 164 | 165 | // FormattedNow returns formatted byte time 166 | func (f *Fastime) FormattedNow() []byte { 167 | return f.ft.Load().([]byte) 168 | } 169 | 170 | // StartTimerD provides time refresh daemon 171 | func (f *Fastime) StartTimerD(ctx context.Context, dur time.Duration) *Fastime { 172 | if f.running.Load().(bool) { 173 | f.Stop() 174 | } 175 | f.refresh() 176 | 177 | var ct context.Context 178 | ct, f.cancel = context.WithCancel(ctx) 179 | go func() { 180 | f.running.Store(true) 181 | atomic.StoreInt64(&f.dur, dur.Nanoseconds()) 182 | ticker := time.NewTicker(time.Duration(atomic.LoadInt64(&f.dur))) 183 | ctick := time.NewTicker(f.correctionDur) 184 | for { 185 | select { 186 | case <-ct.Done(): 187 | f.running.Store(false) 188 | ticker.Stop() 189 | ctick.Stop() 190 | return 191 | case <-ticker.C: 192 | f.update() 193 | case <-ctick.C: 194 | <-ticker.C 195 | f.refresh() 196 | } 197 | } 198 | }() 199 | 200 | return f 201 | } 202 | -------------------------------------------------------------------------------- /fastime_test.go: -------------------------------------------------------------------------------- 1 | package fastime 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "sync/atomic" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestNew(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | }{ 15 | { 16 | name: "is daemon starts?", 17 | }, 18 | } 19 | for _, tt := range tests { 20 | t.Run(tt.name, func(t *testing.T) { 21 | f := New().StartTimerD(context.Background(), 10000) 22 | time.Sleep(time.Second * 2) 23 | if (time.Now().Unix() - f.Now().Unix()) > 1000 { 24 | t.Error("time is not correct so daemon is not started") 25 | } 26 | }) 27 | } 28 | } 29 | 30 | func TestNow(t *testing.T) { 31 | tests := []struct { 32 | name string 33 | }{ 34 | { 35 | name: "time equality", 36 | }, 37 | } 38 | for _, tt := range tests { 39 | t.Run(tt.name, func(t *testing.T) { 40 | if (time.Now().Unix() - Now().Unix()) > 1000 { 41 | t.Error("time is not correct") 42 | } 43 | }) 44 | } 45 | } 46 | 47 | func TestStop(t *testing.T) { 48 | tests := []struct { 49 | name string 50 | }{ 51 | { 52 | name: "check stop", 53 | }, 54 | } 55 | for _, tt := range tests { 56 | t.Run(tt.name, func(t *testing.T) { 57 | now := Now().Unix() 58 | if (time.Now().Unix() - now) > 1000 { 59 | t.Error("time is not correct") 60 | } 61 | Stop() 62 | time.Sleep(time.Second * 3) 63 | now = Now().Unix() 64 | if now == time.Now().Unix() { 65 | t.Error("refresh daemon stopped but time is same") 66 | } 67 | 68 | }) 69 | } 70 | } 71 | 72 | func TestFastime_Now(t *testing.T) { 73 | type fields struct { 74 | t atomic.Value 75 | cancel context.CancelFunc 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | want time.Time 81 | }{ 82 | { 83 | name: "time equality", 84 | }, 85 | } 86 | for _, tt := range tests { 87 | t.Run(tt.name, func(t *testing.T) { 88 | f := New().StartTimerD(context.Background(), 10000) 89 | if f.Now().Unix() != time.Now().Unix() { 90 | t.Error("time is not correct") 91 | } 92 | }) 93 | } 94 | } 95 | 96 | func TestFastime_Stop(t *testing.T) { 97 | type fields struct { 98 | t atomic.Value 99 | cancel context.CancelFunc 100 | } 101 | tests := []struct { 102 | name string 103 | fields fields 104 | }{ 105 | { 106 | name: "check stop", 107 | }, 108 | } 109 | for _, tt := range tests { 110 | t.Run(tt.name, func(t *testing.T) { 111 | f := New().StartTimerD(context.Background(), time.Nanosecond*5) 112 | time.Sleep(time.Second) 113 | now := f.Now().Unix() 114 | if (time.Now().Unix() - now) > 1000 { 115 | t.Error("time is not correct") 116 | } 117 | f.Stop() 118 | time.Sleep(time.Second * 3) 119 | now = f.Now().Unix() 120 | if now == time.Now().Unix() { 121 | t.Error("refresh daemon stopped but time is same") 122 | } 123 | 124 | }) 125 | } 126 | } 127 | 128 | func TestUnixNow(t *testing.T) { 129 | tests := []struct { 130 | name string 131 | }{ 132 | { 133 | name: "time equality", 134 | }, 135 | } 136 | for _, tt := range tests { 137 | t.Run(tt.name, func(t *testing.T) { 138 | if UnixNow() != Now().Unix() { 139 | t.Error("time is not correct") 140 | } 141 | }) 142 | } 143 | } 144 | 145 | func TestFastime_UnixNow(t *testing.T) { 146 | tests := []struct { 147 | name string 148 | }{ 149 | { 150 | name: "time equality", 151 | }, 152 | } 153 | 154 | f := New().StartTimerD(context.Background(), time.Millisecond) 155 | for _, tt := range tests { 156 | t.Run(tt.name, func(t *testing.T) { 157 | if f.UnixNow() != f.Now().Unix() { 158 | t.Error("time is not correct") 159 | } 160 | }) 161 | } 162 | } 163 | 164 | func TestUnixUNow(t *testing.T) { 165 | tests := []struct { 166 | name string 167 | }{ 168 | { 169 | name: "time equality", 170 | }, 171 | } 172 | for _, tt := range tests { 173 | t.Run(tt.name, func(t *testing.T) { 174 | if UnixUNow() != uint32(Now().Unix()) { 175 | t.Error("time is not correct") 176 | } 177 | }) 178 | } 179 | } 180 | 181 | func TestFastime_UnixUNow(t *testing.T) { 182 | tests := []struct { 183 | name string 184 | }{ 185 | { 186 | name: "time equality", 187 | }, 188 | } 189 | 190 | f := New().StartTimerD(context.Background(), time.Millisecond) 191 | for _, tt := range tests { 192 | t.Run(tt.name, func(t *testing.T) { 193 | if f.UnixUNow() != uint32(f.Now().Unix()) { 194 | t.Error("time is not correct") 195 | } 196 | }) 197 | } 198 | } 199 | 200 | func TestFastime_UnixNanoNow(t *testing.T) { 201 | tests := []struct { 202 | name string 203 | }{ 204 | { 205 | name: "time equality", 206 | }, 207 | } 208 | 209 | f := New().StartTimerD(context.Background(), time.Nanosecond) 210 | for _, tt := range tests { 211 | t.Run(tt.name, func(t *testing.T) { 212 | if f.UnixNanoNow() != f.Now().UnixNano() { 213 | t.Error("time is not correct") 214 | } 215 | }) 216 | } 217 | } 218 | 219 | func TestUnixUNanoNow(t *testing.T) { 220 | tests := []struct { 221 | name string 222 | }{ 223 | { 224 | name: "time equality", 225 | }, 226 | } 227 | for _, tt := range tests { 228 | t.Run(tt.name, func(t *testing.T) { 229 | if UnixUNanoNow() != uint32(Now().UnixNano()) { 230 | t.Error("time is not correct") 231 | } 232 | }) 233 | } 234 | } 235 | 236 | func TestFastime_UnixUNanoNow(t *testing.T) { 237 | tests := []struct { 238 | name string 239 | }{ 240 | { 241 | name: "time equality", 242 | }, 243 | } 244 | 245 | f := New().StartTimerD(context.Background(), time.Nanosecond) 246 | for _, tt := range tests { 247 | t.Run(tt.name, func(t *testing.T) { 248 | if f.UnixUNanoNow() != uint32(f.Now().UnixNano()) { 249 | t.Error("time is not correct") 250 | } 251 | }) 252 | } 253 | } 254 | 255 | func TestFastime_refresh(t *testing.T) { 256 | tests := []struct { 257 | name string 258 | f *Fastime 259 | }{ 260 | { 261 | name: "refresh", 262 | f: New(), 263 | }, 264 | } 265 | for _, tt := range tests { 266 | t.Run(tt.name, func(t *testing.T) { 267 | if got := tt.f.refresh(); time.Since(got.Now()) > time.Second { 268 | t.Errorf("time didn't refreshed Fastime.refresh() = %v", got.Now()) 269 | } 270 | }) 271 | } 272 | } 273 | 274 | func TestSetFormat(t *testing.T) { 275 | tests := []struct { 276 | name string 277 | format string 278 | }{ 279 | { 280 | name: "set RFC3339", 281 | format: time.RFC3339, 282 | }, 283 | } 284 | for _, tt := range tests { 285 | t.Run(tt.name, func(t *testing.T) { 286 | if got := SetFormat(tt.format); !reflect.DeepEqual(got.format.Load().(string), time.RFC3339) { 287 | t.Errorf("SetFormat() = %v, want %v", got.format.Load().(string), time.RFC3339) 288 | } 289 | }) 290 | } 291 | } 292 | func TestFastime_SetFormat(t *testing.T) { 293 | tests := []struct { 294 | name string 295 | f *Fastime 296 | format string 297 | }{ 298 | { 299 | name: "set RFC3339", 300 | f: New(), 301 | format: time.RFC3339, 302 | }, 303 | } 304 | for _, tt := range tests { 305 | t.Run(tt.name, func(t *testing.T) { 306 | if got := tt.f.SetFormat(tt.format); !reflect.DeepEqual(got.format.Load().(string), time.RFC3339) { 307 | t.Errorf("Fastime.SetFormat() = %v, want %v", got.format.Load().(string), time.RFC3339) 308 | } 309 | }) 310 | } 311 | } 312 | 313 | func TestFormattedNow(t *testing.T) { 314 | tests := []struct { 315 | name string 316 | }{ 317 | { 318 | name: "fetch", 319 | }, 320 | } 321 | for _, tt := range tests { 322 | t.Run(tt.name, func(t *testing.T) { 323 | t.Log(string(FormattedNow())) 324 | }) 325 | } 326 | } 327 | 328 | func TestFastime_FormattedNow(t *testing.T) { 329 | tests := []struct { 330 | name string 331 | f *Fastime 332 | }{ 333 | { 334 | name: "fetch", 335 | f: New(), 336 | }, 337 | } 338 | for _, tt := range tests { 339 | t.Run(tt.name, func(t *testing.T) { 340 | t.Log(string(tt.f.FormattedNow())) 341 | }) 342 | } 343 | } 344 | 345 | func TestFastime_now(t *testing.T) { 346 | tests := []struct { 347 | name string 348 | f *Fastime 349 | }{ 350 | { 351 | name: "now", 352 | f: New(), 353 | }, 354 | } 355 | for _, tt := range tests { 356 | t.Run(tt.name, func(t *testing.T) { 357 | if got := tt.f.now(); time.Since(got) > time.Second { 358 | t.Errorf("time didn't correct Fastime.now() = %v", got) 359 | } 360 | }) 361 | } 362 | } 363 | 364 | func TestFastime_update(t *testing.T) { 365 | tests := []struct { 366 | name string 367 | f *Fastime 368 | }{ 369 | { 370 | name: "update", 371 | f: New(), 372 | }, 373 | } 374 | for _, tt := range tests { 375 | t.Run(tt.name, func(t *testing.T) { 376 | if got := tt.f.refresh(); time.Since(got.Now()) > time.Second { 377 | t.Errorf("time didn't refreshed Fastime.update() = %v", got.Now()) 378 | } 379 | }) 380 | } 381 | } 382 | 383 | func TestFastime_store(t *testing.T) { 384 | tests := []struct { 385 | name string 386 | f *Fastime 387 | }{ 388 | { 389 | name: "store", 390 | f: New(), 391 | }, 392 | } 393 | for _, tt := range tests { 394 | t.Run(tt.name, func(t *testing.T) { 395 | n := tt.f.now() 396 | if got := tt.f.store(n); tt.f.Now().UnixNano() != n.UnixNano() { 397 | t.Errorf("time didn't match Fastime.store() = %v", got.Now()) 398 | } 399 | }) 400 | } 401 | 402 | } 403 | --------------------------------------------------------------------------------