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 GetMono = (*Time).mono
ErrLocation = errLocation ErrLocation = errLocation
ReadFile = readFile ReadFile = readFile
LoadTzinfo = loadTzinfo
) )

View file

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

View file

@ -294,7 +294,7 @@ func LoadLocation(name string) (*Location, error) {
}) })
if *zoneinfo != "" { if *zoneinfo != "" {
if zoneData, err := loadTzinfoFromDirOrZip(*zoneinfo, name); err == nil { 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 return z, nil
} }
} }

View file

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

View file

@ -7,6 +7,7 @@ package time_test
import ( import (
"fmt" "fmt"
"os" "os"
"reflect"
"testing" "testing"
"time" "time"
) )
@ -116,3 +117,27 @@ func TestLocationNames(t *testing.T) {
t.Errorf(`invalid UTC location name: got %q want "UTC"`, time.UTC) 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")
}
}