forked from tidwall/rtime
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtime.go
100 lines (93 loc) · 2.22 KB
/
rtime.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
package rtime
import (
"net/http"
"sort"
"sync"
"time"
)
var sites = []string{
"facebook.com", "microsoft.com", "amazon.com", "google.com",
"youtube.com", "twitter.com", "reddit.com", "netflix.com",
"bing.com",
}
var rmu sync.Mutex
var rtime time.Time
// Now returns the current remote time. If the remote time cannot be
// retrieved then the zero value for Time is returned. It's a good idea to
// test for zero after every call, such as:
//
// now := rtime.Now()
// if now.IsZero() {
// ... handle failure ...
// }
//
func Now() time.Time {
res := make([]time.Time, 0, len(sites))
results := make(chan time.Time, len(sites))
// get as many dates as quickly as possible
client := http.Client{
Timeout: time.Duration(time.Second * 2),
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
for _, site := range sites {
go func(site string) {
resp, err := client.Head("https://" + site)
if err == nil {
tm, err := time.Parse(time.RFC1123, resp.Header.Get("Date"))
resp.Body.Close()
if err == nil {
results <- tm
}
}
}(site)
}
for {
select {
case <-time.After(2 * time.Second):
return time.Time{}
case tm := <-results:
res = append(res, tm)
if len(res) >= 3 {
// We must have a minimum of three results. Find the two of three
// that have the least difference in time and take the smaller of
// the two.
type pair struct {
tm0 time.Time
tm1 time.Time
diff time.Duration
}
var list []pair
for i := 0; i < len(res); i++ {
for j := i + 1; j < len(res); j++ {
if i != j {
tm0, tm1 := res[i], res[j]
if tm0.After(tm1) {
tm0, tm1 = tm1, tm0
}
list = append(list, pair{tm0, tm1, tm1.Sub(tm0)})
}
}
}
sort.Slice(list, func(i, j int) bool {
if list[i].diff < list[j].diff {
return true
}
if list[i].diff > list[j].diff {
return false
}
return list[i].tm0.Before(list[j].tm0)
})
res := list[0].tm0.Local()
// Ensure that the new time is after the previous time.
rmu.Lock()
defer rmu.Unlock()
if res.After(rtime) {
rtime = res
}
return rtime
}
}
}
}