]> Cypherpunks repositories - gostls13.git/commitdiff
time: propagate "," separator for fractional seconds into Format
authorkorzhao <korzhao95@gmail.com>
Sat, 28 Aug 2021 21:21:17 +0000 (05:21 +0800)
committerIan Lance Taylor <iant@golang.org>
Thu, 9 Sep 2021 22:17:48 +0000 (22:17 +0000)
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>

src/time/format.go
src/time/format_test.go

index 7ae89c557d59be4d4a0bb674428b801b95c548e6..c2bffb8ff68b8888b4cbfeb66df445fbd26ce473 100644 (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
index 1af41e2dfb315a65e6d154779d3665a4d7fdc268..93cbcf940139292ca02a5f721d381059c0f20d76 100644 (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)
+               }
+       }
+}