From 642b87ad9aaf6804f7f9a3f475baf9dd362df027 Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Mon, 2 Aug 2021 15:59:22 +0800 Subject: [PATCH] Move toml.LocalDatetime to internal package See the comment: writing a good API for this ties in to various other issues, and I'd rather not add something now that I'll regret later and that we're then stuck with due to compatibility. This is the safer option, and allows me to release a new version which gives people the bugfixes and TOML 1.0 support without having to wait until such a time I can implement all of this (and I'd rather solve some other issues first too). --- decode.go | 7 +------ decode_test.go | 14 ++++++++------ encode.go | 10 ++++++---- internal/tag/add.go | 8 ++++---- internal/tag/rm.go | 8 ++++---- internal/tz.go | 36 ++++++++++++++++++++++++++++++++++++ parse.go | 35 +++++------------------------------ 7 files changed, 64 insertions(+), 54 deletions(-) create mode 100644 internal/tz.go diff --git a/decode.go b/decode.go index 895d140c..d3d3b839 100644 --- a/decode.go +++ b/decode.go @@ -65,8 +65,7 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { // TOML table arrays correspond to either a slice of structs or a slice of maps. // // TOML datetimes correspond to Go time.Time values. Local datetimes are parsed -// in the local timezone and have the Location set to the decoders's Timezone -// value. This defaults to this computer's local timezone if not given. +// in the local timezone. // // All other TOML types (float, string, int, bool and array) correspond to the // obvious Go types. @@ -94,10 +93,6 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { // cyclic type is passed. type Decoder struct { r io.Reader - - // Timezone to use for local times. Defaults to this computer's local - // timezone if nil. - //Timezone *time.Location } // NewDecoder creates a new Decoder. diff --git a/decode_test.go b/decode_test.go index 92cb1c10..f51101f9 100644 --- a/decode_test.go +++ b/decode_test.go @@ -9,6 +9,8 @@ import ( "strings" "testing" "time" + + "github.com/BurntSushi/toml/internal" ) func TestDecodeReader(t *testing.T) { @@ -687,12 +689,12 @@ func TestDecodeDatetime(t *testing.T) { {"1979-05-27T07:32:12-07:00 # c", time.Date(1979, 05, 27, 07, 32, 12, 0, tz7)}, // Local times. - {"1979-05-27T07:32:00", time.Date(1979, 05, 27, 07, 32, 0, 0, LocalDatetime)}, - {"1979-05-27T07:32:00.999999", time.Date(1979, 05, 27, 07, 32, 0, 999999000, LocalDatetime)}, - {"1979-05-27T07:32:00.25", time.Date(1979, 05, 27, 07, 32, 0, 250000000, LocalDatetime)}, - {"1979-05-27", time.Date(1979, 05, 27, 0, 0, 0, 0, LocalDate)}, - {"07:32:00", time.Date(0, 1, 1, 07, 32, 0, 0, LocalTime)}, - {"07:32:00.999999", time.Date(0, 1, 1, 07, 32, 0, 999999000, LocalTime)}, + {"1979-05-27T07:32:00", time.Date(1979, 05, 27, 07, 32, 0, 0, internal.LocalDatetime)}, + {"1979-05-27T07:32:00.999999", time.Date(1979, 05, 27, 07, 32, 0, 999999000, internal.LocalDatetime)}, + {"1979-05-27T07:32:00.25", time.Date(1979, 05, 27, 07, 32, 0, 250000000, internal.LocalDatetime)}, + {"1979-05-27", time.Date(1979, 05, 27, 0, 0, 0, 0, internal.LocalDate)}, + {"07:32:00", time.Date(0, 1, 1, 07, 32, 0, 0, internal.LocalTime)}, + {"07:32:00.999999", time.Date(0, 1, 1, 07, 32, 0, 999999000, internal.LocalTime)}, } { t.Run(tt.in, func(t *testing.T) { var x struct{ D time.Time } diff --git a/encode.go b/encode.go index c8aa01b7..10d88ac6 100644 --- a/encode.go +++ b/encode.go @@ -12,6 +12,8 @@ import ( "strconv" "strings" "time" + + "github.com/BurntSushi/toml/internal" ) type tomlEncodeError struct{ error } @@ -184,17 +186,17 @@ func (enc *Encoder) eElement(rv reflect.Value) { case time.Time: // Using TextMarshaler adds extra quotes, which we don't want. format := time.RFC3339Nano switch v.Location() { - case LocalDatetime: + case internal.LocalDatetime: format = "2006-01-02T15:04:05.999999999" - case LocalDate: + case internal.LocalDate: format = "2006-01-02" - case LocalTime: + case internal.LocalTime: format = "15:04:05.999999999" } switch v.Location() { default: enc.wf(v.Format(format)) - case LocalDatetime, LocalDate, LocalTime: + case internal.LocalDatetime, internal.LocalDate, internal.LocalTime: enc.wf(v.In(time.UTC).Format(format)) } return diff --git a/internal/tag/add.go b/internal/tag/add.go index 4a23a54a..88f69503 100644 --- a/internal/tag/add.go +++ b/internal/tag/add.go @@ -5,7 +5,7 @@ import ( "math" "time" - "github.com/BurntSushi/toml" + "github.com/BurntSushi/toml/internal" ) // Add JSON tags to a data structure as expected by toml-test. @@ -44,11 +44,11 @@ func Add(key string, tomlData interface{}) interface{} { switch orig.Location() { default: return tag("datetime", orig.Format("2006-01-02T15:04:05.999999999Z07:00")) - case toml.LocalDatetime: + case internal.LocalDatetime: return tag("datetime-local", orig.Format("2006-01-02T15:04:05.999999999")) - case toml.LocalDate: + case internal.LocalDate: return tag("date-local", orig.Format("2006-01-02")) - case toml.LocalTime: + case internal.LocalTime: return tag("time-local", orig.Format("15:04:05.999999999")) } diff --git a/internal/tag/rm.go b/internal/tag/rm.go index 7be1224d..884cda15 100644 --- a/internal/tag/rm.go +++ b/internal/tag/rm.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - "github.com/BurntSushi/toml" + "github.com/BurntSushi/toml/internal" ) // Rempve JSON tags to a data structure as returned by toml-test. @@ -70,11 +70,11 @@ func untag(typed map[string]interface{}) interface{} { case "datetime": return parseTime(v, "2006-01-02T15:04:05.999999999Z07:00", nil) case "datetime-local": - return parseTime(v, "2006-01-02T15:04:05.999999999", toml.LocalDatetime) + return parseTime(v, "2006-01-02T15:04:05.999999999", internal.LocalDatetime) case "date-local": - return parseTime(v, "2006-01-02", toml.LocalDate) + return parseTime(v, "2006-01-02", internal.LocalDate) case "time-local": - return parseTime(v, "15:04:05.999999999", toml.LocalTime) + return parseTime(v, "15:04:05.999999999", internal.LocalTime) case "bool": switch v { case "true": diff --git a/internal/tz.go b/internal/tz.go new file mode 100644 index 00000000..022f15bc --- /dev/null +++ b/internal/tz.go @@ -0,0 +1,36 @@ +package internal + +import "time" + +// Timezones used for local datetime, date, and time TOML types. +// +// The exact way times and dates without a timezone should be interpreted is not +// well-defined in the TOML specification and left to the implementation. These +// defaults to current local timezone offset of the computer, but this can be +// changed by changing these variables before decoding. +// +// TODO: +// Ideally we'd like to offer people the ability to configure the used timezone +// by setting Decoder.Timezone and Encoder.Timezone; however, this is a bit +// tricky: the reason we use three different variables for this is to support +// round-tripping – without these specific TZ names we wouldn't know which +// format to use. +// +// There isn't a good way to encode this right now though, and passing this sort +// of information also ties in to various related issues such as string format +// encoding, encoding of comments, etc. +// +// So, for the time being, just put this in internal until we can write a good +// comprehensive API for doing all of this. +// +// The reason they're exported is because they're referred from in e.g. +// internal/tag. +// +// Note that this behaviour is valid according to the TOML spec as the exact +// behaviour is left up to implementations. +var ( + localOffset = func() int { _, o := time.Now().Zone(); return o }() + LocalDatetime = time.FixedZone("datetime-local", localOffset) + LocalDate = time.FixedZone("date-local", localOffset) + LocalTime = time.FixedZone("time-local", localOffset) +) diff --git a/parse.go b/parse.go index 8006c884..d9ae5db9 100644 --- a/parse.go +++ b/parse.go @@ -7,6 +7,8 @@ import ( "strings" "time" "unicode/utf8" + + "github.com/BurntSushi/toml/internal" ) type parser struct { @@ -298,41 +300,14 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) { return num, p.typeOfPrimitive(it) } -// Timezones used for local datetime, date, and time TOML types. -// -// The exact way times and dates without a timezone should be interpreted is not -// well-defined in the TOML specification and left to the implementation. These -// defaults to current local timezone offset of the computer, but this can be -// changed by changing these variables before decoding. -// -// The reason we use three different variables for this is to support -// round-tripping. -// -// TODO: these really shouldn't be package-level globals, and there also -// shouldn't be three variables. The problem is that the time is decoded in the -// parse stage, rather than the decode stage. -// -// Decoder and Encoder should both support a Timezone attribute instead. -// Round-tripping is more tricky though, as there isn't a way to pass this -// information yet. -// -// The reason they're exported is because they're referred from in e.g. -// internal/tag. -var ( - localOffset = func() int { _, o := time.Now().Zone(); return o }() - LocalDatetime = time.FixedZone("datetime-local", localOffset) - LocalDate = time.FixedZone("date-local", localOffset) - LocalTime = time.FixedZone("time-local", localOffset) -) - var dtTypes = []struct { fmt string zone *time.Location }{ {time.RFC3339Nano, time.Local}, - {"2006-01-02T15:04:05.999999999", LocalDatetime}, - {"2006-01-02", LocalDate}, - {"15:04:05.999999999", LocalTime}, + {"2006-01-02T15:04:05.999999999", internal.LocalDatetime}, + {"2006-01-02", internal.LocalDate}, + {"15:04:05.999999999", internal.LocalTime}, } func (p *parser) valueDatetime(it item) (interface{}, tomlType) {