diff --git a/cast_test.go b/cast_test.go index 6bea164..3884620 100644 --- a/cast_test.go +++ b/cast_test.go @@ -41,6 +41,13 @@ func createNumberTestSteps(zero, one, eight, eightnegative, eightpoint31, eightp eightpoint31negative_32 = float64(float32(eightpoint31negative.(float64))) } + streight := eight + streightnegative := eightnegative + if kind == reflect.Float32 || kind == reflect.Float64 { + streight = eightpoint31 + streightnegative = eightpoint31negative + } + return []testStep{ {int(8), eight, false}, {int8(8), eight, false}, @@ -59,6 +66,7 @@ func createNumberTestSteps(zero, one, eight, eightnegative, eightpoint31, eightp {true, one, false}, {false, zero, false}, {"8", eight, false}, + {"8.31", streight, false}, {nil, zero, false}, {int(-8), eightnegative, isUint}, {int8(-8), eightnegative, isUint}, @@ -68,6 +76,7 @@ func createNumberTestSteps(zero, one, eight, eightnegative, eightpoint31, eightp {float32(-8.31), eightpoint31negative_32, isUint}, {float64(-8.31), eightpoint31negative, isUint}, {"-8", eightnegative, isUint}, + {"-8.31", streightnegative, isUint}, {jeight, eight, false}, {jminuseight, eightnegative, isUint}, {jfloateight, eight, false}, @@ -897,6 +906,15 @@ func BenchmarkTrimZeroDecimal(b *testing.B) { } } +func BenchmarkTruncateDecimals(b *testing.B) { + for i := 0; i < b.N; i++ { + truncateDecimals("") + truncateDecimals("123.") + truncateDecimals("120.1") + truncateDecimals("120.001") + } +} + func BenchmarkCommonTimeLayouts(b *testing.B) { for i := 0; i < b.N; i++ { for _, commonLayout := range []string{"2019-04-29", "2017-05-30T00:00:00Z"} { @@ -923,9 +941,8 @@ func TestIndirectPointers(t *testing.T) { func TestToTime(t *testing.T) { c := qt.New(t) - var jntime, jnetime json.Number + var jntime json.Number _ = json.Unmarshal([]byte("1234567890"), &jntime) - _ = json.Unmarshal([]byte("123.4567890"), &jnetime) tests := []struct { input interface{} expect time.Time @@ -968,7 +985,7 @@ func TestToTime(t *testing.T) { {time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, // errors {"2006", time.Time{}, true}, - {jnetime, time.Time{}, true}, + {nil, time.Time{}, true}, {testing.T{}, time.Time{}, true}, } @@ -1180,6 +1197,21 @@ func TestTrimZeroDecimal(t *testing.T) { } +func TestTruncateDecimals(t *testing.T) { + c := qt.New(t) + + c.Assert(truncateDecimals("+123.0"), qt.Equals, "+123") + c.Assert(truncateDecimals("-123.0000000000000000000001"), qt.Equals, "-123") + c.Assert(truncateDecimals("123."), qt.Equals, "123") + c.Assert(truncateDecimals("0.123"), qt.Equals, "0") + c.Assert(truncateDecimals("1230"), qt.Equals, "1230") + + c.Assert(truncateDecimals("0123"), qt.Equals, "0123") + c.Assert(truncateDecimals("0x123"), qt.Equals, "0x123") + c.Assert(truncateDecimals("foo"), qt.Equals, "foo") + +} + func assertTimeEqual(t *testing.T, expected, actual time.Time) { t.Helper() // Compare the dates using a numeric zone as there are cases where diff --git a/caste.go b/caste.go index d49bbf8..2d35147 100644 --- a/caste.go +++ b/caste.go @@ -281,7 +281,7 @@ func ToInt64E(i interface{}) (int64, error) { case float32: return int64(s), nil case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { return v, nil } @@ -333,7 +333,7 @@ func ToInt32E(i interface{}) (int32, error) { case float32: return int32(s), nil case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { return int32(v), nil } @@ -385,7 +385,7 @@ func ToInt16E(i interface{}) (int16, error) { case float32: return int16(s), nil case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { return int16(v), nil } @@ -437,7 +437,7 @@ func ToInt8E(i interface{}) (int8, error) { case float32: return int8(s), nil case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { return int8(v), nil } @@ -489,7 +489,7 @@ func ToIntE(i interface{}) (int, error) { case float32: return int(s), nil case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { return int(v), nil } @@ -522,7 +522,7 @@ func ToUintE(i interface{}) (uint, error) { switch s := i.(type) { case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed @@ -598,7 +598,7 @@ func ToUint64E(i interface{}) (uint64, error) { switch s := i.(type) { case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed @@ -674,7 +674,7 @@ func ToUint32E(i interface{}) (uint32, error) { switch s := i.(type) { case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed @@ -750,7 +750,7 @@ func ToUint16E(i interface{}) (uint16, error) { switch s := i.(type) { case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed @@ -826,7 +826,7 @@ func ToUint8E(i interface{}) (uint8, error) { switch s := i.(type) { case string: - v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) + v, err := strconv.ParseInt(truncateDecimals(trimZeroDecimal(s)), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed @@ -1496,3 +1496,29 @@ func trimZeroDecimal(s string) string { } return s } + +func truncateDecimals(s string) string { + v := -1 + for i := len(s) - 1; i >= 0; i-- { + if s[i] == '.' { + v = i + break + } + } + if v == -1 { + return s + } + for i := len(s) - 1; i > v; i-- { + if s[i] < '0' || s[i] > '9' { + return s + } + } + for i := v - 1; i >= 0; i-- { + if s[i] < '0' || s[i] > '9' { + if i != 0 || (s[i] != '+' && s[i] != '-') { + return s + } + } + } + return s[:v] +}