Fix wrong result of datetime64 when negative (#35440)

This commit is contained in:
李扬 2022-04-20 06:11:31 -05:00 committed by GitHub
parent d77678d1d1
commit a1e54c3918
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 82 additions and 13 deletions

View File

@ -117,6 +117,31 @@ TEST_P(DecimalUtilsSplitAndCombineTest, getFractionalPartDecimal128)
testGetFractional<Decimal128>(GetParam());
}
class DecimalUtilsSplitAndCombineForDateTime64Test : public ::testing::TestWithParam<DecimalUtilsSplitAndCombineTestParam>
{};
// Unfortunately typed parametrized tests () are not supported in this version of gtest, so I have to emulate by hand.
TEST_P(DecimalUtilsSplitAndCombineForDateTime64Test, splitDateTime64)
{
testSplit<DateTime64>(GetParam());
}
TEST_P(DecimalUtilsSplitAndCombineForDateTime64Test, combineDateTime64)
{
testDecimalFromComponents<DateTime64>(GetParam());
}
TEST_P(DecimalUtilsSplitAndCombineForDateTime64Test, getWholePartDateTime64)
{
testGetWhole<DateTime64>(GetParam());
}
TEST_P(DecimalUtilsSplitAndCombineForDateTime64Test, getFractionalPartDateTime64)
{
testGetFractional<DateTime64>(GetParam());
}
}
// Intentionally small values that fit into 32-bit in order to cover Decimal32, Decimal64 and Decimal128 with single set of data.
@ -170,3 +195,27 @@ INSTANTIATE_TEST_SUITE_P(Basic,
}
})
);
INSTANTIATE_TEST_SUITE_P(Basic,
DecimalUtilsSplitAndCombineForDateTime64Test,
::testing::ValuesIn(std::initializer_list<DecimalUtilsSplitAndCombineTestParam>{
{
"Negative timestamp 1965-12-12 12:12:12.123 UTC",
DateTime64(-127943267877),
3,
{
-127943267,
877
}
},
{
"Positive timestamp 1975-12-12 12:12:12.123 UTC",
DateTime64(187618332123),
3,
{
187618332,
123
}
}
})
);

View File

@ -929,6 +929,15 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
/// Ignore digits that are out of precision.
while (!buf.eof() && isNumericASCII(*buf.position()))
++buf.position();
/// Keep sign of fractional part the same with whole part if datetime64 is negative
/// 1965-12-12 12:12:12.123 => whole = -127914468, fraction = 123(sign>0) -> new whole = -127914467, new fraction = 877(sign<0)
if (components.whole < 0 && components.fractional != 0)
{
const auto scale_multiplier = DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale);
++components.whole;
components.fractional = scale_multiplier - components.fractional;
}
}
/// 9908870400 is time_t value for 2184-01-01 UTC (a bit over the last year supported by DateTime64)
else if (whole >= 9908870400LL)

View File

@ -805,6 +805,13 @@ inline void writeDateTimeText(DateTime64 datetime64, UInt32 scale, WriteBuffer &
scale = scale > MaxScale ? MaxScale : scale;
auto components = DecimalUtils::split(datetime64, scale);
/// -127914467.877 => whole = -127914467, fraction = 877 => new whole = -127914468(1965-12-12 12:12:12), new fraction = 123(.123) => 1965-12-12 12:12:12.123
if (components.whole < 0 && components.fractional != 0)
{
--components.whole;
components.fractional = DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale) - components.fractional;
}
writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>(LocalDateTime(components.whole, time_zone), buf);
if (scale > 0)

View File

@ -4,6 +4,6 @@ Asia/Makassar 1234567891011 2009-02-14 07:31:31.011 1970-01-15 14:56:07.891011 1
non-const column
1234567891011 2009-02-13 23:31:31.011 1970-01-15 06:56:07.891011 1970-01-01 00:20:34.567891011
upper range bound
9904447342 2283-11-10 19:22:22.123 2283-11-10 19:22:22.123456 1925-01-01 00:00:00.586094827
9904447342 2283-11-10 19:22:22.123 2283-11-10 19:22:22.123456 1925-01-01 00:00:00.413905173
lower range bound
-1420066799 1925-01-01 01:00:01.123 1925-01-01 01:00:01.123456 1925-01-01 01:00:01.123456789
-1420066799 1925-01-01 01:00:00.877 1925-01-01 01:00:00.876544 1925-01-01 01:00:00.876543211

View File

@ -1,4 +1,4 @@
1940-10-09 22:13:17.6
2283-11-11 23:46:43.6
2283-11-11 23:46:40.1
1925-01-01 00:00:00.1
1925-01-01 00:00:00.9

View File

@ -14,7 +14,7 @@ test intervals
1980-12-12 12:12:12.123456
1930-12-12 12:12:12.123456
1930-12-12 12:12:12.123400
1930-12-12 12:12:12.123457
1930-12-12 12:12:12.123456
2220-12-12 12:12:12.123456
2220-12-12 12:12:12.123400
2220-12-12 12:12:12.123456
@ -25,7 +25,7 @@ test intervals
1980-12-12 12:12:12.123
1930-12-12 12:12:12.123
1930-12-12 12:12:12.120
1930-12-12 12:12:12.124
1930-12-12 12:12:12.123
2220-12-12 12:12:12.123
2220-12-12 12:12:12.120
2220-12-12 12:12:12.123
@ -34,8 +34,8 @@ test add[...]seconds()
1980-12-12 12:12:12.123456790
1980-12-12 12:12:12.123456701
1980-12-12 12:12:12.123456790
1930-12-12 12:12:12.123456788
1930-12-12 12:12:12.123456699
1930-12-12 12:12:12.123456790
1930-12-12 12:12:12.123456701
2220-12-12 12:12:12.123456790
2220-12-12 12:12:12.123456701
- test microseconds
@ -43,9 +43,9 @@ test add[...]seconds()
1980-12-12 12:12:12.123401
1980-12-12 12:12:12.12345778
1980-12-12 12:12:12.123457
1930-12-12 12:12:12.123455
1930-12-12 12:12:12.123399
1930-12-12 12:12:12.12345578
1930-12-12 12:12:12.123457
1930-12-12 12:12:12.123401
1930-12-12 12:12:12.12345778
2220-12-12 12:12:12.123457
2220-12-12 12:12:12.123401
2220-12-12 12:12:12.12345778
@ -54,9 +54,9 @@ test add[...]seconds()
1980-12-12 12:12:12.121
1980-12-12 12:12:12.124456
1980-12-12 12:12:12.124
1930-12-12 12:12:12.122
1930-12-12 12:12:12.119
1930-12-12 12:12:12.122456
1930-12-12 12:12:12.124
1930-12-12 12:12:12.121
1930-12-12 12:12:12.124456
2220-12-12 12:12:12.124
2220-12-12 12:12:12.121
2220-12-12 12:12:12.124456

View File

@ -0,0 +1,2 @@
-127914467.877
187618332.123

View File

@ -0,0 +1,2 @@
SELECT cast(toDateTime64('1965-12-12 12:12:12.123', 3, 'UTC') as Decimal64(3));
SELECT cast(toDateTime64('1975-12-12 12:12:12.123', 3, 'UTC') as Decimal64(3));