time: enable Location loading from user provided timezone data

The return values of the LoadLocation are inherently dependent
on the runtime environment. Add LoadLocationFromTZData, whose
results depend only on the timezone data provided as arguments.

Fixes #20629

Change-Id: I43b181f4c05c219be3ec57327540263b7cb3b2aa
Reviewed-on: https://go-review.googlesource.com/68890
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Florian Uekermann 2017-10-06 17:16:43 +02:00 committed by Brad Fitzpatrick
parent 9a14cd9e75
commit 2951f909ea
5 changed files with 37 additions and 10 deletions

View file

@ -34,4 +34,5 @@ var (
GetMono = (*Time).mono
ErrLocation = errLocation
ReadFile = readFile
LoadTzinfo = loadTzinfo
)

View file

@ -18,11 +18,11 @@ func initTestingZone() {
localLoc = *z
}
var origZoneSources = zoneSources
var OrigZoneSources = zoneSources
func forceZipFileForTesting(zipOnly bool) {
zoneSources = make([]string, len(origZoneSources))
copy(zoneSources, origZoneSources)
zoneSources = make([]string, len(OrigZoneSources))
copy(zoneSources, OrigZoneSources)
if zipOnly {
zoneSources = zoneSources[len(zoneSources)-1:]
}

View file

@ -294,7 +294,7 @@ func LoadLocation(name string) (*Location, error) {
})
if *zoneinfo != "" {
if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil {
if z, err := newLocationFromTzinfo(name, zoneData); err == nil {
if z, err := LoadLocationFromTZData(name, zoneData); err == nil {
return z, nil
}
}

View file

@ -79,11 +79,12 @@ func byteString(p []byte) string {
var badData = errors.New("malformed time zone information")
// newLocationFromTzinfo returns the Location described by Tzinfo with the given name.
// The expected format for Tzinfo is that of a timezone file as they are found in the
// the IANA Time Zone database.
func newLocationFromTzinfo(name string, Tzinfo []byte) (*Location, error) {
d := dataIO{Tzinfo, false}
// LoadLocationFromTZData returns a Location with the given name
// initialized from the IANA Time Zone database-formatted data.
// The data should be in the format of a standard IANA time zone file
// (for example, the content of /etc/localtime on Unix systems).
func LoadLocationFromTZData(name string, data []byte) (*Location, error) {
d := dataIO{data, false}
// 4-byte magic "TZif"
if magic := d.read(4); string(magic) != "TZif" {
@ -390,7 +391,7 @@ func loadLocation(name string, sources []string) (z *Location, firstErr error) {
for _, source := range sources {
var zoneData, err = loadTzinfo(name, source)
if err == nil {
if z, err = newLocationFromTzinfo(name, zoneData); err == nil {
if z, err = LoadLocationFromTZData(name, zoneData); err == nil {
return z, nil
}
}

View file

@ -7,6 +7,7 @@ package time_test
import (
"fmt"
"os"
"reflect"
"testing"
"time"
)
@ -116,3 +117,27 @@ func TestLocationNames(t *testing.T) {
t.Errorf(`invalid UTC location name: got %q want "UTC"`, time.UTC)
}
}
func TestLoadLocationFromTzinfo(t *testing.T) {
time.ForceZipFileForTesting(true)
defer time.ForceZipFileForTesting(false)
const locationName = "Asia/Jerusalem"
reference, err := time.LoadLocation(locationName)
if err != nil {
t.Fatal(err)
}
tzinfo, err := time.LoadTzinfo(locationName, time.OrigZoneSources[len(time.OrigZoneSources)-1])
if err != nil {
t.Fatal(err)
}
sample, err := time.LoadLocationFromTZData(locationName, tzinfo)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(reference, sample) {
t.Errorf("return values of LoadLocationFromTZData and LoadLocation don't match")
}
}