1
0
Fork 0
datetime.link/zone.go

91 lines
2.0 KiB
Go

package main
import (
"time"
"github.com/serverwentdown/datetime.link/data"
"go.uber.org/zap"
)
// Zone represents any form of zone offset: this could be a city or a fixed
// offset from UTC
type Zone struct {
// City is optional
City *data.City
// Offset is optional
Offset *time.Location
}
// IsOffset is true when the Zone is an offset instead of a city
func (z Zone) IsOffset() bool {
return z.Offset != nil
}
// Name returns the name of the zone
func (z Zone) Name() string {
if z.IsOffset() {
return z.Offset.String()
} else if z.City != nil {
return z.City.FullName()
}
return ""
}
// FirstName returns the primary name of the zone
func (z Zone) FirstName() string {
if z.IsOffset() {
return z.Offset.String()
} else if z.City != nil {
if len(z.City.Admin1.Name) > 0 {
return z.City.Name + ", " + z.City.Admin1.Name
}
return z.City.Name
}
return ""
}
// Location returns the time.Location of the zone. Useful for other functions
func (z Zone) Location() *time.Location {
if z.IsOffset() {
return z.Offset
} else if z.City != nil {
loc, err := time.LoadLocation(z.City.Timezone)
if err != nil {
// This is a really bad situation
// TODO: validate this at boot time
l.Error("unable to find timezone", zap.String("timezone", z.City.Timezone))
}
return loc
}
return nil
}
// TimeOffset returns the timezone offset at a specific time
func (z Zone) TimeOffset(t time.Time) int {
if l := z.Location(); l != nil {
_, offset := t.In(l).Zone()
return offset
}
return -1 // TODO: better invalid handling
}
// ResolveZone resolves a zone string into a Zone
func ResolveZone(cities map[string]*data.City, zone string) (Zone, error) {
// Try parsing as a zone offset
offset, err := ParseZoneOffset(zone)
if err == nil {
offsetZone := time.FixedZone("UTC "+FormatZoneOffset(offset), offset)
return Zone{
Offset: offsetZone,
}, nil
}
// Parse as a city
city, err := SearchCities(cities, zone)
if err == nil {
return Zone{
City: city,
}, nil
}
return Zone{}, err
}