Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a NewCustomLocalTimeZone #22

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ func TestTzidPresent(t *testing.T) {
if !ok {
t.Error("error when initializing client")
}
z.mu.RLock()
defer z.mu.RUnlock()
_, ok = z.tzData["id"]
if ok {
t.Error("unexpected feature with empty tzid")
Expand All @@ -118,8 +116,6 @@ func TestPolygons(t *testing.T) {
if !ok {
t.Error("error when initializing client")
}
z.mu.RLock()
defer z.mu.RUnlock()
for tzid, d := range z.tzData {
if d.polygon != nil {
for _, ring := range *d.polygon {
Expand All @@ -140,13 +136,6 @@ func BenchmarkGetZone(b *testing.B) {
if err != nil {
b.Errorf("cannot initialize test cases: %v", err)
}

// Ensure client has finished loading data
_, err = client.GetZone(Point{0, 0})
if err != nil {
b.Errorf("cannot initialize timezone client: %v", err)
}

b.Run("GetZone on large cities", func(b *testing.B) {
Loop:
for n := 0; n < b.N; {
Expand Down
27 changes: 13 additions & 14 deletions localtimezone.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ func init() {
type LocalTimeZone interface {
GetZone(p Point) (tzids []string, err error)
GetOneZone(p Point) (tzid string, err error)
LoadGeoJSON(io.Reader) error
}

type tzData struct {
Expand All @@ -95,7 +94,6 @@ type tzData struct {
type localTimeZone struct {
tzids []string
tzData map[string]tzData
mu sync.RWMutex
}

var _ LocalTimeZone = &localTimeZone{}
Expand All @@ -120,13 +118,22 @@ func NewMockLocalTimeZone() LocalTimeZone {
return &z
}

// NewCustomLocalTimeZone creates a new LocalTimeZone but based on custom
// passed-in json data
// The client is threadsafe
func NewCustomLocalTimeZone(data io.Reader) (LocalTimeZone, error) {
z := localTimeZone{}
err := z.loadGeoJSON(data)
return &z, err
}

func (z *localTimeZone) load(shapeFile []byte) error {
g, err := gzip.NewReader(bytes.NewBuffer(shapeFile))
if err != nil {
return err
}

err = z.LoadGeoJSON(g)
err = z.loadGeoJSON(g)
_ = g.Close()
if err != nil {
return err
Expand Down Expand Up @@ -156,8 +163,6 @@ func (z *localTimeZone) getZone(point Point, single bool) (tzids []string, err e
if p[0] > 180 || p[0] < -180 || p[1] > 90 || p[1] < -90 {
return nil, ErrOutOfRange
}
z.mu.RLock()
defer z.mu.RUnlock()
for _, id := range z.tzids {
d := z.tzData[id]
if !d.bound.Contains(p) {
Expand Down Expand Up @@ -266,10 +271,8 @@ func (z *localTimeZone) buildCache(features []*geojson.Feature) {
sort.Strings(z.tzids)
}

// LoadGeoJSON loads a custom GeoJSON shapefile from a Reader
func (z *localTimeZone) LoadGeoJSON(r io.Reader) error {
z.mu.Lock()

// loadGeoJSON loads a custom GeoJSON shapefile from a Reader
func (z *localTimeZone) loadGeoJSON(r io.Reader) error {
var buf bytes.Buffer
_, err := buf.ReadFrom(r)
if err != nil {
Expand All @@ -279,14 +282,10 @@ func (z *localTimeZone) LoadGeoJSON(r io.Reader) error {
if err != nil {
z.tzData = make(map[string]tzData)
z.tzids = []string{}
z.mu.Unlock()
return err
}
z.tzData = make(map[string]tzData, TZCount) // Possibly the incorrect length in case of Mock or custom data
z.tzids = []string{} // Cannot set a length or else array will be full of empty strings
go func(features []*geojson.Feature) {
defer z.mu.Unlock()
z.buildCache(features)
}(orbData.Features)
z.buildCache(orbData.Features)
return nil
}
34 changes: 3 additions & 31 deletions localtimezone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,6 @@ func BenchmarkZones(b *testing.B) {
if !ok {
b.Errorf("cannot initialize timezone client")
}

// Ensure client has finished loading data
_, err = z.GetZone(Point{0, 0})
if err != nil {
b.Errorf("cannot initialize timezone client: %v", err)
}

b.Run("polygon centers", func(b *testing.B) {
Loop:
for n := 0; n < b.N; {
Expand Down Expand Up @@ -329,28 +322,16 @@ func BenchmarkZones(b *testing.B) {
func BenchmarkClientInit(b *testing.B) {
b.Run("main client", func(b *testing.B) {
for n := 0; n < b.N; {
c, err := NewLocalTimeZone()
_, err := NewLocalTimeZone()
if err != nil {
b.Errorf("client could not initialize because of %v", err)
}
cStruct, ok := c.(*localTimeZone)
if !ok {
b.Errorf("cannot initialize timezone client")
}
cStruct.mu.RLock()
defer cStruct.mu.RUnlock()
n++
}
})
b.Run("mock client", func(b *testing.B) {
for n := 0; n < b.N; {
c := NewMockLocalTimeZone()
cStruct, ok := c.(*localTimeZone)
if !ok {
b.Errorf("cannot initialize timezone client")
}
cStruct.mu.RLock()
defer cStruct.mu.RUnlock()
NewMockLocalTimeZone()
n++
}
})
Expand Down Expand Up @@ -427,15 +408,10 @@ func TestLoadGeoJSONMalformed(t *testing.T) {
if !ok {
t.Errorf("cannot initialize client")
}
err = c.LoadGeoJSON(reader)
err = c.loadGeoJSON(reader)
if err == nil {
t.Errorf("expected error, got %v", err)
}
unlocked := c.mu.TryLock()
if !unlocked {
t.Errorf("expected lock to be released")
}
defer c.mu.Unlock()

if len(c.tzData) != 0 {
t.Errorf("tzData not reset")
Expand All @@ -451,13 +427,9 @@ func TestLoadOverwrite(t *testing.T) {
if !ok {
t.Errorf("cannot initialize client")
}
c.mu.RLock()
lenTzData := len(c.tzData)
c.mu.RUnlock()

err = c.load(MockTZShapeFile)
c.mu.RLock()
defer c.mu.RUnlock()
if err != nil {
t.Errorf("cannot switch client to mock data, got %v", err)
}
Expand Down