Decoder: time allows extra precision (#710)
As discussed[1], this change allows times to provide precision beyond the nanosecond (nine digits fractional part). Extra precision is truncated according to the TOML specificiation. [1]: https://github.com/pelletier/go-toml/discussions/707
This commit is contained in:
@@ -232,6 +232,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) {
|
|||||||
|
|
||||||
if len(b) >= 1 && b[0] == '.' {
|
if len(b) >= 1 && b[0] == '.' {
|
||||||
frac := 0
|
frac := 0
|
||||||
|
precision := 0
|
||||||
digits := 0
|
digits := 0
|
||||||
|
|
||||||
for i, c := range b[1:] {
|
for i, c := range b[1:] {
|
||||||
@@ -241,23 +242,31 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
digits++
|
||||||
|
|
||||||
const maxFracPrecision = 9
|
const maxFracPrecision = 9
|
||||||
if i >= maxFracPrecision {
|
if i >= maxFracPrecision {
|
||||||
return t, nil, newDecodeError(b[i-1:i], "maximum precision for date time is nanosecond")
|
// go-toml allows decoding fractional seconds
|
||||||
|
// beyond the supported precision of 9
|
||||||
|
// digits. It truncates the fractional component
|
||||||
|
// to the supported precision and ignores the
|
||||||
|
// remaining digits.
|
||||||
|
//
|
||||||
|
// https://github.com/pelletier/go-toml/discussions/707
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
frac *= 10
|
frac *= 10
|
||||||
frac += int(c - '0')
|
frac += int(c - '0')
|
||||||
digits++
|
precision++
|
||||||
}
|
}
|
||||||
|
|
||||||
if digits == 0 {
|
if precision == 0 {
|
||||||
return t, nil, newDecodeError(b[:1], "nanoseconds need at least one digit")
|
return t, nil, newDecodeError(b[:1], "nanoseconds need at least one digit")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Nanosecond = frac * nspow[digits]
|
t.Nanosecond = frac * nspow[precision]
|
||||||
t.Precision = digits
|
t.Precision = precision
|
||||||
|
|
||||||
return t, b[1+digits:], nil
|
return t, b[1+digits:], nil
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -60,7 +60,8 @@ func (d *Decoder) SetStrict(strict bool) *Decoder {
|
|||||||
//
|
//
|
||||||
// When a TOML local date, time, or date-time is decoded into a time.Time, its
|
// When a TOML local date, time, or date-time is decoded into a time.Time, its
|
||||||
// value is represented in time.Local timezone. Otherwise the approriate Local*
|
// value is represented in time.Local timezone. Otherwise the approriate Local*
|
||||||
// structure is used.
|
// structure is used. For time values, precision up to the nanosecond is
|
||||||
|
// supported by truncating extra digits.
|
||||||
//
|
//
|
||||||
// Empty tables decoded in an interface{} create an empty initialized
|
// Empty tables decoded in an interface{} create an empty initialized
|
||||||
// map[string]interface{}.
|
// map[string]interface{}.
|
||||||
|
|||||||
+15
-8
@@ -2311,6 +2311,21 @@ func TestIssue708(t *testing.T) {
|
|||||||
require.Equal(t, map[string]string{"0": ""}, v)
|
require.Equal(t, map[string]string{"0": ""}, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue710(t *testing.T) {
|
||||||
|
v := map[string]toml.LocalTime{}
|
||||||
|
err := toml.Unmarshal([]byte(`0=00:00:00.0000000000`), &v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]toml.LocalTime{"0": {Precision: 9}}, v)
|
||||||
|
v1 := map[string]toml.LocalTime{}
|
||||||
|
err = toml.Unmarshal([]byte(`0=00:00:00.0000000001`), &v1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]toml.LocalTime{"0": {Precision: 9}}, v1)
|
||||||
|
v2 := map[string]toml.LocalTime{}
|
||||||
|
err = toml.Unmarshal([]byte(`0=00:00:00.1111111119`), &v2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]toml.LocalTime{"0": {Nanosecond: 111111111, Precision: 9}}, v2)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnmarshalDecodeErrors(t *testing.T) {
|
func TestUnmarshalDecodeErrors(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
@@ -2325,18 +2340,10 @@ func TestUnmarshalDecodeErrors(t *testing.T) {
|
|||||||
desc: "local time with fractional",
|
desc: "local time with fractional",
|
||||||
data: `a = 11:22:33.x`,
|
data: `a = 11:22:33.x`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "local time frac precision too large",
|
|
||||||
data: `a = 2021-05-09T11:22:33.99999999999`,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "wrong time offset separator",
|
desc: "wrong time offset separator",
|
||||||
data: `a = 1979-05-27T00:32:00.-07:00`,
|
data: `a = 1979-05-27T00:32:00.-07:00`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "missing fractional with tz",
|
|
||||||
data: `a = 2021-05-09T11:22:33.99999999999`,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "wrong time offset separator",
|
desc: "wrong time offset separator",
|
||||||
data: `a = 1979-05-27T00:32:00Z07:00`,
|
data: `a = 1979-05-27T00:32:00Z07:00`,
|
||||||
|
|||||||
Reference in New Issue
Block a user