]> Cypherpunks repositories - gostls13.git/commitdiff
time: support "," as separator for fractional seconds
authorEmmanuel T Odeke <emmanuel@orijtech.com>
Fri, 12 Mar 2021 01:34:09 +0000 (17:34 -0800)
committerEmmanuel Odeke <emmanuel@orijtech.com>
Tue, 16 Mar 2021 00:27:31 +0000 (00:27 +0000)
Accepts comma "," as a separator for fractional seconds
hence we now accept:
* 2006-01-02 15:04:05,999999999 -0700 MST
* Mon Jan _2 15:04:05,120007 2006
* Mon Jan 2 15:04:05,120007 2006

This change follows the recommendations of ISO 8601 per

   https://en.wikipedia.org/wiki/ISO_8601#cite_note-26

which states

   ISO 8601:2004(E), ISO, 2004-12-01, "4.2.2.4 ...
   the decimal fraction shall be divided from the integer
   part by the decimal sign specified in ISO 31-0, i.e.
   the comma [,] or full stop [.]. Of these, the comma
   is the preferred sign."

Unfortunately, I couldn't directly access the ISO 8601 document
because suddenly it is behind a paywall on the ISO website,
charging CHF 158 (USD 179) for 38 pages :-(

However, this follows publicly available cited literature, as well
as the recommendations from the proposal approval.

Fixes #6189
Updates #27746
Updates #26002
Updates #36145
Updates #43813
Fixes #43823

Change-Id: Ibe96064e8ee27c239be78c880fa561a1a41e190c
Reviewed-on: https://go-review.googlesource.com/c/go/+/300996
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Rob Pike <r@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>

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

index c4f3358f596c2a87c2a9bee83e53ae4bf216f131..75860358722c98e03c5eb0da58dd6e83453933a9 100644 (file)
@@ -26,13 +26,14 @@ import "errors"
 // compatibility with fixed-width Unix time formats.
 //
 // A decimal point followed by one or more zeros represents a fractional
-// second, printed to the given number of decimal places. A decimal point
-// followed by one or more nines represents a fractional second, printed to
-// the given number of decimal places, with trailing zeros removed.
+// second, printed to the given number of decimal places.
+// Either a comma or decimal point followed by one or more nines represents
+// a fractional second, printed to the given number of decimal places, with
+// trailing zeros removed.
 // When parsing (only), the input may contain a fractional second
 // field immediately after the seconds field, even if the layout does not
-// signify its presence. In that case a decimal point followed by a maximal
-// series of digits is parsed as a fractional second.
+// signify its presence. In that case either a comma or a decimal point
+// followed by a maximal series of digits is parsed as a fractional second.
 //
 // Numeric time zone offsets format as follows:
 //     -0700  ±hhmm
@@ -261,7 +262,7 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
                                return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
                        }
 
-               case '.': // .000 or .999 - repeated digits for fractional seconds.
+               case '.', ',': // ,000, or .000, or ,999, or .999 - repeated digits for fractional seconds.
                        if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
                                ch := layout[i+1]
                                j := i + 1
@@ -484,9 +485,10 @@ func (t Time) String() string {
 // desired output. The same display rules will then be applied to the time
 // value.
 //
-// A fractional second is represented by adding a period and zeros
-// to the end of the seconds section of layout string, as in "15:04:05.000"
-// to format a time stamp with millisecond precision.
+// A fractional second is represented by adding either a comma or a
+// period and zeros to the end of the seconds section of layout string,
+// as in "15:04:05,000" or "15:04:05.000" to format a time stamp with
+// millisecond precision.
 //
 // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
 // and convenient representations of the reference time. For more information
@@ -940,7 +942,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
                        }
                        // Special case: do we have a fractional second but no
                        // fractional second in the format?
-                       if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
+                       if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) {
                                _, std, _ = nextStdChunk(layout)
                                std &= stdMask
                                if std == stdFracSecond0 || std == stdFracSecond9 {
@@ -1070,7 +1072,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
                        value = value[ndigit:]
 
                case stdFracSecond9:
-                       if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
+                       if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] {
                                // Fractional second omitted.
                                break
                        }
@@ -1279,8 +1281,12 @@ func parseSignedOffset(value string) int {
        return len(value) - len(rem)
 }
 
+func commaOrPeriod(b byte) bool {
+       return b == '.' || b == ','
+}
+
 func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
-       if value[0] != '.' {
+       if !commaOrPeriod(value[0]) {
                err = errBad
                return
        }
index 4574d20319d20cdc09c3857ac45eb1ae6c9be760..9e96d4a29568c527d83569fc44c1bdcc3d64a111 100644 (file)
@@ -207,9 +207,13 @@ var parseTests = []ParseTest{
        {"ANSIC", ANSIC, "THU FEB 4 21:00:57 2010", false, true, 1, 0},
        {"ANSIC", ANSIC, "thu feb 4 21:00:57 2010", false, true, 1, 0},
        // Fractional seconds.
-       {"millisecond", "Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 21:00:57.012 2010", false, true, 1, 3},
-       {"microsecond", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb  4 21:00:57.012345 2010", false, true, 1, 6},
-       {"nanosecond", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb  4 21:00:57.012345678 2010", false, true, 1, 9},
+       {"millisecond:: dot separator", "Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 21:00:57.012 2010", false, true, 1, 3},
+       {"microsecond:: dot separator", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb  4 21:00:57.012345 2010", false, true, 1, 6},
+       {"nanosecond:: dot separator", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb  4 21:00:57.012345678 2010", false, true, 1, 9},
+       {"millisecond:: comma separator", "Mon Jan _2 15:04:05,000 2006", "Thu Feb  4 21:00:57.012 2010", false, true, 1, 3},
+       {"microsecond:: comma separator", "Mon Jan _2 15:04:05,000000 2006", "Thu Feb  4 21:00:57.012345 2010", false, true, 1, 6},
+       {"nanosecond:: comma separator", "Mon Jan _2 15:04:05,000000000 2006", "Thu Feb  4 21:00:57.012345678 2010", false, true, 1, 9},
+
        // Leading zeros in other places should not be taken as fractional seconds.
        {"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
        {"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2},
@@ -222,12 +226,20 @@ var parseTests = []ParseTest{
        // Accept any number of fractional second digits (including none) for .999...
        // In Go 1, .999... was completely ignored in the format, meaning the first two
        // cases would succeed, but the next four would not. Go 1.1 accepts all six.
+       // decimal "." separator.
        {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
        {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
        {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
        {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
        {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
        {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
+       // comma "," separator.
+       {"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
+       {"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
+       {"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
+       {"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
+       {"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
+       {"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
 
        // issue 4502.
        {"", StampNano, "Feb  4 21:00:57.012345678", false, false, -1, 9},