Skip to content

Commit 201cb04

Browse files
obeydaianlancetaylor
authored andcommitted
time: quote original value in errors returned by ParseDuration
Quote original values passed as substring of ParseError.Message. Improves the user experience of ParseDuration by making it quote its original argument, for example: _, err := time.ParseDuration("for breakfast") will now produce an error, which when printed out is: time: invalid duration "for breakfast" instead of: time: invalid duration for breakfast Adapt test cases for format.Parse and format.ParseDuration. Fixes #38295 Change-Id: Ife322c8f3c859e1e4e8dd546d4cf0d519b4bfa81 Reviewed-on: https://go-review.googlesource.com/c/go/+/227878 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent 5a706d1 commit 201cb04

File tree

4 files changed

+90
-73
lines changed

4 files changed

+90
-73
lines changed

src/time/example_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ func ExampleParse() {
379379
// 2013-02-03 00:00:00 +0000 UTC
380380
// 2006-01-02 15:04:05 +0000 UTC
381381
// 2006-01-02 15:04:05 +0700 +0700
382-
// error parsing time "2006-01-02T15:04:05Z07:00": extra text: 07:00
382+
// error parsing time "2006-01-02T15:04:05Z07:00": extra text: "07:00"
383383
}
384384

385385
func ExampleParseInLocation() {

src/time/format.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
856856
}
857857
if std == 0 {
858858
if len(value) != 0 {
859-
return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
859+
return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + quote(value)}
860860
}
861861
break
862862
}
@@ -1390,7 +1390,7 @@ func ParseDuration(s string) (Duration, error) {
13901390
return 0, nil
13911391
}
13921392
if s == "" {
1393-
return 0, errors.New("time: invalid duration " + orig)
1393+
return 0, errors.New("time: invalid duration " + quote(orig))
13941394
}
13951395
for s != "" {
13961396
var (
@@ -1402,13 +1402,13 @@ func ParseDuration(s string) (Duration, error) {
14021402

14031403
// The next character must be [0-9.]
14041404
if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
1405-
return 0, errors.New("time: invalid duration " + orig)
1405+
return 0, errors.New("time: invalid duration " + quote(orig))
14061406
}
14071407
// Consume [0-9]*
14081408
pl := len(s)
14091409
v, s, err = leadingInt(s)
14101410
if err != nil {
1411-
return 0, errors.New("time: invalid duration " + orig)
1411+
return 0, errors.New("time: invalid duration " + quote(orig))
14121412
}
14131413
pre := pl != len(s) // whether we consumed anything before a period
14141414

@@ -1422,7 +1422,7 @@ func ParseDuration(s string) (Duration, error) {
14221422
}
14231423
if !pre && !post {
14241424
// no digits (e.g. ".s" or "-.s")
1425-
return 0, errors.New("time: invalid duration " + orig)
1425+
return 0, errors.New("time: invalid duration " + quote(orig))
14261426
}
14271427

14281428
// Consume unit.
@@ -1434,17 +1434,17 @@ func ParseDuration(s string) (Duration, error) {
14341434
}
14351435
}
14361436
if i == 0 {
1437-
return 0, errors.New("time: missing unit in duration " + orig)
1437+
return 0, errors.New("time: missing unit in duration " + quote(orig))
14381438
}
14391439
u := s[:i]
14401440
s = s[i:]
14411441
unit, ok := unitMap[u]
14421442
if !ok {
1443-
return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
1443+
return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
14441444
}
14451445
if v > (1<<63-1)/unit {
14461446
// overflow
1447-
return 0, errors.New("time: invalid duration " + orig)
1447+
return 0, errors.New("time: invalid duration " + quote(orig))
14481448
}
14491449
v *= unit
14501450
if f > 0 {
@@ -1453,13 +1453,13 @@ func ParseDuration(s string) (Duration, error) {
14531453
v += int64(float64(f) * (float64(unit) / scale))
14541454
if v < 0 {
14551455
// overflow
1456-
return 0, errors.New("time: invalid duration " + orig)
1456+
return 0, errors.New("time: invalid duration " + quote(orig))
14571457
}
14581458
}
14591459
d += v
14601460
if d < 0 {
14611461
// overflow
1462-
return 0, errors.New("time: invalid duration " + orig)
1462+
return 0, errors.New("time: invalid duration " + quote(orig))
14631463
}
14641464
}
14651465

src/time/format_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,12 @@ var parseErrorTests = []ParseErrorTest{
536536
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second out of range"},
537537
// issue 4502. StampNano requires exactly 9 digits of precision.
538538
{StampNano, "Dec 7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`},
539-
{StampNano, "Dec 7 11:22:01.0000000000", "extra text: 0"},
539+
{StampNano, "Dec 7 11:22:01.0000000000", `extra text: "0"`},
540540
// issue 4493. Helpful errors.
541-
{RFC3339, "2006-01-02T15:04:05Z07:00", `parsing time "2006-01-02T15:04:05Z07:00": extra text: 07:00`},
541+
{RFC3339, "2006-01-02T15:04:05Z07:00", `parsing time "2006-01-02T15:04:05Z07:00": extra text: "07:00"`},
542542
{RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`},
543543
{RFC3339, "2006-01-02T15:04:05_abc", `parsing time "2006-01-02T15:04:05_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as "Z07:00"`},
544-
{RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: _abc`},
544+
{RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: "_abc"`},
545545
// invalid second followed by optional fractional seconds
546546
{RFC3339, "2010-02-04T21:00:67.012345678-08:00", "second out of range"},
547547
// issue 21113

src/time/time_test.go

Lines changed: 76 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -810,86 +810,103 @@ func TestNotJSONEncodableTime(t *testing.T) {
810810

811811
var parseDurationTests = []struct {
812812
in string
813-
ok bool
814813
want Duration
815814
}{
816815
// simple
817-
{"0", true, 0},
818-
{"5s", true, 5 * Second},
819-
{"30s", true, 30 * Second},
820-
{"1478s", true, 1478 * Second},
816+
{"0", 0},
817+
{"5s", 5 * Second},
818+
{"30s", 30 * Second},
819+
{"1478s", 1478 * Second},
821820
// sign
822-
{"-5s", true, -5 * Second},
823-
{"+5s", true, 5 * Second},
824-
{"-0", true, 0},
825-
{"+0", true, 0},
821+
{"-5s", -5 * Second},
822+
{"+5s", 5 * Second},
823+
{"-0", 0},
824+
{"+0", 0},
826825
// decimal
827-
{"5.0s", true, 5 * Second},
828-
{"5.6s", true, 5*Second + 600*Millisecond},
829-
{"5.s", true, 5 * Second},
830-
{".5s", true, 500 * Millisecond},
831-
{"1.0s", true, 1 * Second},
832-
{"1.00s", true, 1 * Second},
833-
{"1.004s", true, 1*Second + 4*Millisecond},
834-
{"1.0040s", true, 1*Second + 4*Millisecond},
835-
{"100.00100s", true, 100*Second + 1*Millisecond},
826+
{"5.0s", 5 * Second},
827+
{"5.6s", 5*Second + 600*Millisecond},
828+
{"5.s", 5 * Second},
829+
{".5s", 500 * Millisecond},
830+
{"1.0s", 1 * Second},
831+
{"1.00s", 1 * Second},
832+
{"1.004s", 1*Second + 4*Millisecond},
833+
{"1.0040s", 1*Second + 4*Millisecond},
834+
{"100.00100s", 100*Second + 1*Millisecond},
836835
// different units
837-
{"10ns", true, 10 * Nanosecond},
838-
{"11us", true, 11 * Microsecond},
839-
{"12µs", true, 12 * Microsecond}, // U+00B5
840-
{"12μs", true, 12 * Microsecond}, // U+03BC
841-
{"13ms", true, 13 * Millisecond},
842-
{"14s", true, 14 * Second},
843-
{"15m", true, 15 * Minute},
844-
{"16h", true, 16 * Hour},
836+
{"10ns", 10 * Nanosecond},
837+
{"11us", 11 * Microsecond},
838+
{"12µs", 12 * Microsecond}, // U+00B5
839+
{"12μs", 12 * Microsecond}, // U+03BC
840+
{"13ms", 13 * Millisecond},
841+
{"14s", 14 * Second},
842+
{"15m", 15 * Minute},
843+
{"16h", 16 * Hour},
845844
// composite durations
846-
{"3h30m", true, 3*Hour + 30*Minute},
847-
{"10.5s4m", true, 4*Minute + 10*Second + 500*Millisecond},
848-
{"-2m3.4s", true, -(2*Minute + 3*Second + 400*Millisecond)},
849-
{"1h2m3s4ms5us6ns", true, 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond},
850-
{"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond},
845+
{"3h30m", 3*Hour + 30*Minute},
846+
{"10.5s4m", 4*Minute + 10*Second + 500*Millisecond},
847+
{"-2m3.4s", -(2*Minute + 3*Second + 400*Millisecond)},
848+
{"1h2m3s4ms5us6ns", 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond},
849+
{"39h9m14.425s", 39*Hour + 9*Minute + 14*Second + 425*Millisecond},
851850
// large value
852-
{"52763797000ns", true, 52763797000 * Nanosecond},
851+
{"52763797000ns", 52763797000 * Nanosecond},
853852
// more than 9 digits after decimal point, see https://golang.org/issue/6617
854-
{"0.3333333333333333333h", true, 20 * Minute},
853+
{"0.3333333333333333333h", 20 * Minute},
855854
// 9007199254740993 = 1<<53+1 cannot be stored precisely in a float64
856-
{"9007199254740993ns", true, (1<<53 + 1) * Nanosecond},
855+
{"9007199254740993ns", (1<<53 + 1) * Nanosecond},
857856
// largest duration that can be represented by int64 in nanoseconds
858-
{"9223372036854775807ns", true, (1<<63 - 1) * Nanosecond},
859-
{"9223372036854775.807us", true, (1<<63 - 1) * Nanosecond},
860-
{"9223372036s854ms775us807ns", true, (1<<63 - 1) * Nanosecond},
857+
{"9223372036854775807ns", (1<<63 - 1) * Nanosecond},
858+
{"9223372036854775.807us", (1<<63 - 1) * Nanosecond},
859+
{"9223372036s854ms775us807ns", (1<<63 - 1) * Nanosecond},
861860
// large negative value
862-
{"-9223372036854775807ns", true, -1<<63 + 1*Nanosecond},
861+
{"-9223372036854775807ns", -1<<63 + 1*Nanosecond},
863862
// huge string; issue 15011.
864-
{"0.100000000000000000000h", true, 6 * Minute},
863+
{"0.100000000000000000000h", 6 * Minute},
865864
// This value tests the first overflow check in leadingFraction.
866-
{"0.830103483285477580700h", true, 49*Minute + 48*Second + 372539827*Nanosecond},
867-
868-
// errors
869-
{"", false, 0},
870-
{"3", false, 0},
871-
{"-", false, 0},
872-
{"s", false, 0},
873-
{".", false, 0},
874-
{"-.", false, 0},
875-
{".s", false, 0},
876-
{"+.s", false, 0},
877-
{"3000000h", false, 0}, // overflow
878-
{"9223372036854775808ns", false, 0}, // overflow
879-
{"9223372036854775.808us", false, 0}, // overflow
880-
{"9223372036854ms775us808ns", false, 0}, // overflow
881-
// largest negative value of type int64 in nanoseconds should fail
882-
// see https://go-review.googlesource.com/#/c/2461/
883-
{"-9223372036854775808ns", false, 0},
865+
{"0.830103483285477580700h", 49*Minute + 48*Second + 372539827*Nanosecond},
884866
}
885867

886868
func TestParseDuration(t *testing.T) {
887869
for _, tc := range parseDurationTests {
888870
d, err := ParseDuration(tc.in)
889-
if tc.ok && (err != nil || d != tc.want) {
871+
if err != nil || d != tc.want {
890872
t.Errorf("ParseDuration(%q) = %v, %v, want %v, nil", tc.in, d, err, tc.want)
891-
} else if !tc.ok && err == nil {
873+
}
874+
}
875+
}
876+
877+
var parseDurationErrorTests = []struct {
878+
in string
879+
expect string
880+
}{
881+
// invalid
882+
{"", `""`},
883+
{"3", `"3"`},
884+
{"-", `"-"`},
885+
{"s", `"s"`},
886+
{".", `"."`},
887+
{"-.", `"-."`},
888+
{".s", `".s"`},
889+
{"+.s", `"+.s"`},
890+
{"1d", `"1d"`},
891+
// overflow
892+
{"9223372036854775810ns", `"9223372036854775810ns"`},
893+
{"9223372036854775808ns", `"9223372036854775808ns"`},
894+
// largest negative value of type int64 in nanoseconds should fail
895+
// see https://go-review.googlesource.com/#/c/2461/
896+
{"-9223372036854775808ns", `"-9223372036854775808ns"`},
897+
{"9223372036854776us", `"9223372036854776us"`},
898+
{"3000000h", `"3000000h"`},
899+
{"9223372036854775.808us", `"9223372036854775.808us"`},
900+
{"9223372036854ms775us808ns", `"9223372036854ms775us808ns"`},
901+
}
902+
903+
func TestParseDurationErrors(t *testing.T) {
904+
for _, tc := range parseDurationErrorTests {
905+
_, err := ParseDuration(tc.in)
906+
if err == nil {
892907
t.Errorf("ParseDuration(%q) = _, nil, want _, non-nil", tc.in)
908+
} else if !strings.Contains(err.Error(), tc.expect) {
909+
t.Errorf("ParseDuration(%q) = _, %q, error does not contain %q", tc.in, err, tc.expect)
893910
}
894911
}
895912
}

0 commit comments

Comments
 (0)