1
0
mirror of https://github.com/golang/go synced 2024-09-29 20:14:29 -06:00

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.

Fixes #48037

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>
This commit is contained in:
korzhao 2021-08-29 05:21:17 +08:00 committed by Ian Lance Taylor
parent c981874a5a
commit e1c3f2158f
2 changed files with 59 additions and 11 deletions

View File

@ -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]...)
}
@ -733,7 +761,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
@ -1165,7 +1193,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

View File

@ -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)
}
}
}