mirror of
https://github.com/golang/go
synced 2024-11-24 05:10:19 -07: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:
parent
c981874a5a
commit
e1c3f2158f
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user