aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkorzhao <korzhao95@gmail.com>2021-08-29 05:21:17 +0800
committerIan Lance Taylor <iant@golang.org>2021-09-15 21:59:48 +0000
commite79c297fa80c9ddd5d3037fa1465664eea0f53c0 (patch)
tree2ce02d60f045b71f3822f9f127b6a49768da0f2f
parent21a4e67ad58e3c4a7c5254f60cda5be5c3c450ff (diff)
downloadgo-e79c297fa80c9ddd5d3037fa1465664eea0f53c0.tar.gz
go-e79c297fa80c9ddd5d3037fa1465664eea0f53c0.zip
[release-branch.go1.17] time: propagate "," separator for fractional seconds into Format
In CL 300996 that fixed issue #6189, we made Parse recognize "," as a separator for fractional seconds. However, we didn't modify Format to propagate the separator verbatim from Parse. Without this change, we break prior functionality that relied on a comma being used in Format. For #48037 Fixes #48177 Change-Id: I6565a25e8657ca3747a58b25acba58f27cdcddc0 Reviewed-on: https://go-review.googlesource.com/c/go/+/345438 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> Trust: Cherry Mui <cherryyz@google.com> (cherry picked from commit e1c3f2158fe3129fb44cc92423cfa41e7b6d472c) Reviewed-on: https://go-review.googlesource.com/c/go/+/350149 Trust: Ian Lance Taylor <iant@golang.org>
-rw-r--r--src/time/format.go50
-rw-r--r--src/time/format_test.go20
2 files changed, 59 insertions, 11 deletions
diff --git a/src/time/format.go b/src/time/format.go
index f4b4f48142f..64a8edaf0ae 100644
--- a/src/time/format.go
+++ b/src/time/format.go
@@ -146,10 +146,11 @@ const (
stdFracSecond0 // ".0", ".00", ... , trailing zeros included
stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
- stdNeedDate = 1 << 8 // need month, day, year
- stdNeedClock = 2 << 8 // need hour, minute, second
- stdArgShift = 16 // extra argument in high bits, above low stdArgShift
- stdMask = 1<<stdArgShift - 1 // mask out argument
+ stdNeedDate = 1 << 8 // need month, day, year
+ stdNeedClock = 2 << 8 // need hour, minute, second
+ stdArgShift = 16 // extra argument in high bits, above low stdArgShift
+ stdSeparatorShift = 28 // extra argument in high 4 bits for fractional second separators
+ stdMask = 1<<stdArgShift - 1 // mask out argument
)
// std0x records the std values for "01", "02", ..., "06".
@@ -289,11 +290,11 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
}
// String of digits must end here - only fractional second is all digits.
if !isDigit(layout, j) {
- std := stdFracSecond0
+ code := stdFracSecond0
if layout[i+1] == '9' {
- std = stdFracSecond9
+ code = stdFracSecond9
}
- std |= (j - (i + 1)) << stdArgShift
+ std := stdFracSecond(code, j-(i+1), c)
return layout[0:i], std, layout[j:]
}
}
@@ -430,9 +431,36 @@ func atoi(s string) (x int, err error) {
return x, nil
}
+// The "std" value passed to formatNano contains two packed fields: the number of
+// digits after the decimal and the separator character (period or comma).
+// These functions pack and unpack that variable.
+func stdFracSecond(code, n, c int) int {
+ // Use 0xfff to make the failure case even more absurd.
+ if c == '.' {
+ return code | ((n & 0xfff) << stdArgShift)
+ }
+ return code | ((n & 0xfff) << stdArgShift) | 1<<stdSeparatorShift
+}
+
+func digitsLen(std int) int {
+ return (std >> stdArgShift) & 0xfff
+}
+
+func separator(std int) byte {
+ if (std >> stdSeparatorShift) == 0 {
+ return '.'
+ }
+ return ','
+}
+
// formatNano appends a fractional second, as nanoseconds, to b
// and returns the result.
-func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
+func formatNano(b []byte, nanosec uint, std int) []byte {
+ var (
+ n = digitsLen(std)
+ separator = separator(std)
+ trim = std&stdMask == stdFracSecond9
+ )
u := nanosec
var buf [9]byte
for start := len(buf); start > 0; {
@@ -452,7 +480,7 @@ func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
return b
}
}
- b = append(b, '.')
+ b = append(b, separator)
return append(b, buf[:n]...)
}
@@ -732,7 +760,7 @@ func (t Time) AppendFormat(b []byte, layout string) []byte {
b = appendInt(b, zone/60, 2)
b = appendInt(b, zone%60, 2)
case stdFracSecond0, stdFracSecond9:
- b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9)
+ b = formatNano(b, uint(t.Nanosecond()), std)
}
}
return b
@@ -1164,7 +1192,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
case stdFracSecond0:
// stdFracSecond0 requires the exact number of digits as specified in
// the layout.
- ndigit := 1 + (std >> stdArgShift)
+ ndigit := 1 + digitsLen(std)
if len(value) < ndigit {
err = errBad
break
diff --git a/src/time/format_test.go b/src/time/format_test.go
index 1af41e2dfb3..93cbcf94013 100644
--- a/src/time/format_test.go
+++ b/src/time/format_test.go
@@ -832,3 +832,23 @@ func TestQuote(t *testing.T) {
}
}
+
+// Issue 48037
+func TestFormatFractionalSecondSeparators(t *testing.T) {
+ tests := []struct {
+ s, want string
+ }{
+ {`15:04:05.000`, `21:00:57.012`},
+ {`15:04:05.999`, `21:00:57.012`},
+ {`15:04:05,000`, `21:00:57,012`},
+ {`15:04:05,999`, `21:00:57,012`},
+ }
+
+ // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2009
+ time := Unix(0, 1233810057012345600)
+ for _, tt := range tests {
+ if q := time.Format(tt.s); q != tt.want {
+ t.Errorf("Format(%q) = got %q, want %q", tt.s, q, tt.want)
+ }
+ }
+}