Skip to content

Commit f02a26b

Browse files
committed
time: support "," as separator for fractional seconds
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 <[email protected]> Run-TryBot: Emmanuel Odeke <[email protected]> Reviewed-by: Rob Pike <[email protected]> TryBot-Result: Go Bot <[email protected]>
1 parent a9b3c4b commit f02a26b

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

src/time/format.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ import "errors"
2626
// compatibility with fixed-width Unix time formats.
2727
//
2828
// A decimal point followed by one or more zeros represents a fractional
29-
// second, printed to the given number of decimal places. A decimal point
30-
// followed by one or more nines represents a fractional second, printed to
31-
// the given number of decimal places, with trailing zeros removed.
29+
// second, printed to the given number of decimal places.
30+
// Either a comma or decimal point followed by one or more nines represents
31+
// a fractional second, printed to the given number of decimal places, with
32+
// trailing zeros removed.
3233
// When parsing (only), the input may contain a fractional second
3334
// field immediately after the seconds field, even if the layout does not
34-
// signify its presence. In that case a decimal point followed by a maximal
35-
// series of digits is parsed as a fractional second.
35+
// signify its presence. In that case either a comma or a decimal point
36+
// followed by a maximal series of digits is parsed as a fractional second.
3637
//
3738
// Numeric time zone offsets format as follows:
3839
// -0700 ±hhmm
@@ -261,7 +262,7 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) {
261262
return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
262263
}
263264

264-
case '.': // .000 or .999 - repeated digits for fractional seconds.
265+
case '.', ',': // ,000, or .000, or ,999, or .999 - repeated digits for fractional seconds.
265266
if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
266267
ch := layout[i+1]
267268
j := i + 1
@@ -484,9 +485,10 @@ func (t Time) String() string {
484485
// desired output. The same display rules will then be applied to the time
485486
// value.
486487
//
487-
// A fractional second is represented by adding a period and zeros
488-
// to the end of the seconds section of layout string, as in "15:04:05.000"
489-
// to format a time stamp with millisecond precision.
488+
// A fractional second is represented by adding either a comma or a
489+
// period and zeros to the end of the seconds section of layout string,
490+
// as in "15:04:05,000" or "15:04:05.000" to format a time stamp with
491+
// millisecond precision.
490492
//
491493
// Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
492494
// and convenient representations of the reference time. For more information
@@ -940,7 +942,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
940942
}
941943
// Special case: do we have a fractional second but no
942944
// fractional second in the format?
943-
if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
945+
if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) {
944946
_, std, _ = nextStdChunk(layout)
945947
std &= stdMask
946948
if std == stdFracSecond0 || std == stdFracSecond9 {
@@ -1070,7 +1072,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
10701072
value = value[ndigit:]
10711073

10721074
case stdFracSecond9:
1073-
if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
1075+
if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] {
10741076
// Fractional second omitted.
10751077
break
10761078
}
@@ -1279,8 +1281,12 @@ func parseSignedOffset(value string) int {
12791281
return len(value) - len(rem)
12801282
}
12811283

1284+
func commaOrPeriod(b byte) bool {
1285+
return b == '.' || b == ','
1286+
}
1287+
12821288
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
1283-
if value[0] != '.' {
1289+
if !commaOrPeriod(value[0]) {
12841290
err = errBad
12851291
return
12861292
}

src/time/format_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,13 @@ var parseTests = []ParseTest{
207207
{"ANSIC", ANSIC, "THU FEB 4 21:00:57 2010", false, true, 1, 0},
208208
{"ANSIC", ANSIC, "thu feb 4 21:00:57 2010", false, true, 1, 0},
209209
// Fractional seconds.
210-
{"millisecond", "Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3},
211-
{"microsecond", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6},
212-
{"nanosecond", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9},
210+
{"millisecond:: dot separator", "Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3},
211+
{"microsecond:: dot separator", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6},
212+
{"nanosecond:: dot separator", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9},
213+
{"millisecond:: comma separator", "Mon Jan _2 15:04:05,000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3},
214+
{"microsecond:: comma separator", "Mon Jan _2 15:04:05,000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6},
215+
{"nanosecond:: comma separator", "Mon Jan _2 15:04:05,000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9},
216+
213217
// Leading zeros in other places should not be taken as fractional seconds.
214218
{"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
215219
{"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{
222226
// Accept any number of fractional second digits (including none) for .999...
223227
// In Go 1, .999... was completely ignored in the format, meaning the first two
224228
// cases would succeed, but the next four would not. Go 1.1 accepts all six.
229+
// decimal "." separator.
225230
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
226231
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
227232
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
228233
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
229234
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
230235
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
236+
// comma "," separator.
237+
{"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
238+
{"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
239+
{"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
240+
{"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
241+
{"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
242+
{"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
231243

232244
// issue 4502.
233245
{"", StampNano, "Feb 4 21:00:57.012345678", false, false, -1, 9},

0 commit comments

Comments
 (0)