-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
What version of Go are you using (go version)?
1.17.1 (play.golang.org)
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env)?
play.golang.org
What did you do?
https://play.golang.org/p/S21r1Xz1VlZ
package main
import (
"fmt"
"time"
)
func main() {
t, err := time.Parse(time.RFC3339, "2021-09-29T16:04:33.00123456789Z")
fmt.Println(t, t.Nanosecond(), err)
t, err = time.Parse(time.RFC3339, "2021-09-29T16:04:33.10123456789Z")
fmt.Println(t, t.Nanosecond(), err)
}What did you expect to see?
either fractional seconds truncated to nanosecond precision:
2021-09-29 16:04:33.001234567 +0000 UTC 1234567 <nil>
2021-09-29 16:04:33.101234567 +0000 UTC 101234567 <nil>
or fractional seconds rounded to nearest nanosecond:
2021-09-29 16:04:33.001234568 +0000 UTC 1234568 <nil>
2021-09-29 16:04:33.101234568 +0000 UTC 101234568 <nil>
or an error in both cases.
What did you see instead?
2021-09-29 16:04:33.123456789 +0000 UTC 123456789 <nil>
0001-01-01 00:00:00 +0000 UTC 0 parsing time "2021-09-29T16:04:33.10123456789Z": fractional second out of range
It appears that if more than 9 digits are provided for the fractional second part, they are interpreted all together as a numeric value for the nanoseconds part, regardless of leading zeros. Thus .0012345678Z is interpreted the same as .012345678Z.
Also, when more than 9 significant digits are provided, rather than truncating or rounding to the nearest nanosecond, parsing produces a fractional second out of range error because this error condition is triggered:
Lines 1426 to 1428 in 163871f
| if ns < 0 || 1e9 <= ns { | |
| rangeErrString = "fractional second" | |
| return |
This bug affects json.Unmarshal / time.UnmarshalJSON which uses the RFC3339 format internally:
Line 1305 in 163871f
| *t, err = Parse(`"`+RFC3339+`"`, string(data)) |
The code at fault is probably one or more of these call sites of parseNanoseconds:
Lines 1082 to 1086 in 163871f
| // No fractional second in the layout but we have one in the input. | |
| n := 2 | |
| for ; n < len(value) && isDigit(value, n); n++ { | |
| } | |
| nsec, rangeErrString, err = parseNanoseconds(value, n) |
Lines 1194 to 1201 in 163871f
| // stdFracSecond0 requires the exact number of digits as specified in | |
| // the layout. | |
| ndigit := 1 + digitsLen(std) | |
| if len(value) < ndigit { | |
| err = errBad | |
| break | |
| } | |
| nsec, rangeErrString, err = parseNanoseconds(value, ndigit) |
Lines 1209 to 1215 in 163871f
| // Take any number of digits, even more than asked for, | |
| // because it is what the stdSecond case would do. | |
| i := 0 | |
| for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { | |
| i++ | |
| } | |
| nsec, rangeErrString, err = parseNanoseconds(value, 1+i) |