Skip to content

Commit 7e2a7a3

Browse files
committed
Timezones, partial.
1 parent e119bb4 commit 7e2a7a3

File tree

5 files changed

+181
-3
lines changed

5 files changed

+181
-3
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/go-chrono/chrono
22

3-
go 1.14
3+
go 1.16

unsafe.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
package chrono
22

3-
import _ "unsafe" // for go:linkname
3+
import (
4+
"sync"
5+
"time"
6+
_ "unsafe" // for go:linkname
7+
)
48

59
//go:linkname monotime runtime.nanotime
610
func monotime() int64
711

812
//go:linkname walltime runtime.walltime
9-
func walltime() (sec int64, nsec int32)
13+
func walltime() (secs int64, nsec int32)
14+
15+
//go:linkname zoneSources time.zoneSources
16+
var zoneSources []string
17+
18+
//go:linkname embeddedTzData tzdata.zipdata
19+
var embeddedTzData string
20+
21+
//go:linkname readEmbeddedTzData time.loadFromEmbeddedTZData
22+
var readEmbeddedTzData func(zipName string) (string, error)
23+
24+
//go:linkname initLocal time.initLocal
25+
func initLocal()
26+
27+
//go:linkname localLoc time.localLoc
28+
var localLoc time.Location
29+
30+
//go:linkname localOnce time.localOnce
31+
var localOnce sync.Once

zoned_date_time.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package chrono
2+
3+
type ZonedDateTime struct {
4+
zone Zone
5+
secs int64
6+
nsec int32
7+
}
8+
9+
func Current() ZonedDateTime {
10+
secs, nsec := walltime()
11+
return ZonedDateTime{
12+
zone: Local(),
13+
secs: secs,
14+
nsec: nsec,
15+
}
16+
}

zoned_date_time_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package chrono_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-chrono/chrono"
7+
)
8+
9+
func TestCurrent(t *testing.T) {
10+
chrono.Current()
11+
}

zones.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package chrono
2+
3+
import (
4+
"io/fs"
5+
"os"
6+
"path"
7+
"path/filepath"
8+
"strings"
9+
"sync"
10+
"time"
11+
"unicode"
12+
)
13+
14+
var zones Zones
15+
var zonesErr error
16+
var zonesOnce sync.Once
17+
18+
type Zones struct {
19+
zones map[string]Zone
20+
}
21+
22+
type Zone struct {
23+
loc *time.Location
24+
}
25+
26+
func UTC() Zone {
27+
return Zone{loc: time.UTC}
28+
}
29+
30+
func Local() Zone {
31+
localOnce.Do(initLocal)
32+
return Zone{loc: &localLoc}
33+
}
34+
35+
func LoadZones() (Zones, error) {
36+
zonesOnce.Do(func() {
37+
zones, zonesErr = loadZones()
38+
})
39+
return zones, zonesErr
40+
}
41+
42+
func LoadZone(name string) (Zone, error) {
43+
loc, err := time.LoadLocation(name)
44+
return Zone{loc: loc}, err
45+
}
46+
47+
func loadZones() (Zones, error) {
48+
sources := zoneSources
49+
if env := os.Getenv("ZONEINFO"); env != "" {
50+
sources = append([]string{env}, sources...)
51+
}
52+
53+
var firstErr error
54+
for _, source := range sources {
55+
zones, err := readTzDataFromDisk(source)
56+
if err != nil && firstErr == nil {
57+
firstErr = err
58+
} else if zones != nil {
59+
return *zones, nil
60+
}
61+
}
62+
63+
if readEmbeddedTzData != nil {
64+
// TODO
65+
/*r, err := zip.NewReader(bytes.NewReader([]byte(embeddedTzData)), int64(len(embeddedTzData)))
66+
if err != nil && firstErr == nil {
67+
firstErr = err
68+
}*/
69+
panic("embedded timezone data is not supported by github.com/go-chrono/chrono")
70+
}
71+
72+
return Zones{}, firstErr
73+
}
74+
75+
func readTzDataFromDisk(source string) (*Zones, error) {
76+
if len(source) >= 6 && source[len(source)-6:] == "tzdata" {
77+
// time.loadTzinfoFromTzdata(file, name string) ([]byte, error)
78+
panic("Android is not supported github.com/go-chrono/chrono")
79+
}
80+
81+
if filepath.Ext(source) == ".zip" {
82+
// TODO load from zip
83+
panic("zipped timezone files are not supported by github.com/go-chrono/chrono")
84+
}
85+
86+
if _, err := os.Open(path.Join(source, "UTC")); err != nil {
87+
return nil, nil
88+
}
89+
90+
var out Zones
91+
return &out, filepath.Walk(source, func(path string, info fs.FileInfo, err error) error {
92+
if info.IsDir() || err != nil {
93+
return nil
94+
}
95+
96+
name, err := filepath.Rel(source, path)
97+
if err != nil {
98+
return err
99+
}
100+
101+
switch {
102+
case
103+
len(name) == 0,
104+
name[0] == '/',
105+
name[0] == '\\',
106+
strings.Contains(name, "."),
107+
unicode.IsLower(rune(name[0])):
108+
return nil
109+
}
110+
111+
buf, err := os.ReadFile(path)
112+
if err != nil {
113+
return err
114+
}
115+
116+
zone, err := readTzFileData(name, buf)
117+
if err != nil {
118+
return err
119+
}
120+
121+
out.zones[name] = zone
122+
return nil
123+
})
124+
}
125+
126+
func readTzFileData(name string, data []byte) (Zone, error) {
127+
loc, err := time.LoadLocationFromTZData(name, data)
128+
return Zone{loc: loc}, err
129+
}

0 commit comments

Comments
 (0)