Message string
}
+// newParseError creates a new ParseError.
+// The provided value and valueElem are cloned to avoid escaping their values.
+func newParseError(layout, value, layoutElem, valueElem, message string) *ParseError {
+ valueCopy := cloneString(value)
+ valueElemCopy := cloneString(valueElem)
+ return &ParseError{layout, valueCopy, layoutElem, valueElemCopy, message}
+}
+
+// cloneString returns a string copy of s.
+// Do not use strings.Clone to avoid dependency on strings package.
+func cloneString(s string) string {
+ return string([]byte(s))
+}
+
// These are borrowed from unicode/utf8 and strconv and replicate behavior in
// that package, since we can't take a dependency on either.
const (
stdstr := layout[len(prefix) : len(layout)-len(suffix)]
value, err = skip(value, prefix)
if err != nil {
- return Time{}, &ParseError{alayout, avalue, prefix, value, ""}
+ return Time{}, newParseError(alayout, avalue, prefix, value, "")
}
if std == 0 {
if len(value) != 0 {
- return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + quote(value)}
+ return Time{}, newParseError(alayout, avalue, "", value, ": extra text: "+quote(value))
}
break
}
value = value[1+i:]
}
if rangeErrString != "" {
- return Time{}, &ParseError{alayout, avalue, stdstr, value, ": " + rangeErrString + " out of range"}
+ return Time{}, newParseError(alayout, avalue, stdstr, value, ": "+rangeErrString+" out of range")
}
if err != nil {
- return Time{}, &ParseError{alayout, avalue, stdstr, value, ""}
+ return Time{}, newParseError(alayout, avalue, stdstr, value, "")
}
}
if pmSet && hour < 12 {
}
}
if yday < 1 || yday > 365 {
- return Time{}, &ParseError{alayout, avalue, "", value, ": day-of-year out of range"}
+ return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year out of range")
}
if m == 0 {
m = (yday-1)/31 + 1
// If month, day already seen, yday's m, d must match.
// Otherwise, set them from m, d.
if month >= 0 && month != m {
- return Time{}, &ParseError{alayout, avalue, "", value, ": day-of-year does not match month"}
+ return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match month")
}
month = m
if day >= 0 && day != d {
- return Time{}, &ParseError{alayout, avalue, "", value, ": day-of-year does not match day"}
+ return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match day")
}
day = d
} else {
// Validate the day of the month.
if day < 1 || day > daysIn(Month(month), year) {
- return Time{}, &ParseError{alayout, avalue, "", value, ": day out of range"}
+ return Time{}, newParseError(alayout, avalue, "", value, ": day out of range")
}
if z != nil {
}
// Otherwise create fake zone to record offset.
- t.setLoc(FixedZone(zoneName, zoneOffset))
+ zoneNameCopy := cloneString(zoneName) // avoid leaking the input value
+ t.setLoc(FixedZone(zoneNameCopy, zoneOffset))
return t, nil
}
offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
offset *= 3600
}
- t.setLoc(FixedZone(zoneName, offset))
+ zoneNameCopy := cloneString(zoneName) // avoid leaking the input value
+ t.setLoc(FixedZone(zoneNameCopy, offset))
return t, nil
}
}
}
+const testdataRFC3339UTC = "2020-08-22T11:27:43.123456789Z"
+
func BenchmarkParseRFC3339UTC(b *testing.B) {
for i := 0; i < b.N; i++ {
- Parse(RFC3339, "2020-08-22T11:27:43.123456789Z")
+ Parse(RFC3339, testdataRFC3339UTC)
+ }
+}
+
+var testdataRFC3339UTCBytes = []byte(testdataRFC3339UTC)
+
+func BenchmarkParseRFC3339UTCBytes(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Parse(RFC3339, string(testdataRFC3339UTCBytes))
}
}
+const testdataRFC3339TZ = "2020-08-22T11:27:43.123456789-02:00"
+
func BenchmarkParseRFC3339TZ(b *testing.B) {
for i := 0; i < b.N; i++ {
- Parse(RFC3339, "2020-08-22T11:27:43.123456789-02:00")
+ Parse(RFC3339, testdataRFC3339TZ)
+ }
+}
+
+var testdataRFC3339TZBytes = []byte(testdataRFC3339TZ)
+
+func BenchmarkParseRFC3339TZBytes(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Parse(RFC3339, string(testdataRFC3339TZBytes))
}
}
}
}
+func TestUnmarshalTextAllocations(t *testing.T) {
+ in := []byte(testdataRFC3339UTC) // short enough to be stack allocated
+ if allocs := testing.AllocsPerRun(100, func() {
+ var t Time
+ t.UnmarshalText(in)
+ }); allocs != 0 {
+ t.Errorf("got %v allocs, want 0 allocs", allocs)
+ }
+}
+
// Issue 17720: Zero value of time.Month fails to print
func TestZeroMonthString(t *testing.T) {
if got, want := Month(0).String(), "%!Month(0)"; got != want {