mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 08:40:50 +00:00
Fix bug of datetime64 parsed from string '1969-12-31 23:59:59.123' (#37039)
This commit is contained in:
parent
6094000d7c
commit
e33cfc889c
@ -156,7 +156,7 @@ inline DecimalComponents<DecimalType> splitWithScaleMultiplier(
|
||||
using T = typename DecimalType::NativeType;
|
||||
const auto whole = decimal.value / scale_multiplier;
|
||||
auto fractional = decimal.value % scale_multiplier;
|
||||
if (fractional < T(0))
|
||||
if (whole && fractional < T(0))
|
||||
fractional *= T(-1);
|
||||
|
||||
return {whole, fractional};
|
||||
@ -199,7 +199,7 @@ inline typename DecimalType::NativeType getFractionalPartWithScaleMultiplier(
|
||||
/// Anycase we make modulo before compare to make scale_multiplier > 1 unaffected.
|
||||
T result = decimal.value % scale_multiplier;
|
||||
if constexpr (!keep_sign)
|
||||
if (result < T(0))
|
||||
if (decimal.value / scale_multiplier && result < T(0))
|
||||
result = -result;
|
||||
|
||||
return result;
|
||||
|
@ -176,7 +176,7 @@ INSTANTIATE_TEST_SUITE_P(Basic,
|
||||
}
|
||||
},
|
||||
{
|
||||
"When scale is not 0 and whole part is 0.",
|
||||
"For positive Decimal value, with scale not 0, and whole part is 0.",
|
||||
123,
|
||||
3,
|
||||
{
|
||||
@ -184,6 +184,16 @@ INSTANTIATE_TEST_SUITE_P(Basic,
|
||||
123
|
||||
}
|
||||
},
|
||||
{
|
||||
"For negative Decimal value, with scale not 0, and whole part is 0.",
|
||||
-123,
|
||||
3,
|
||||
{
|
||||
0,
|
||||
-123
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"For negative Decimal value whole part is negative, fractional is non-negative.",
|
||||
-1234567'89,
|
||||
@ -216,6 +226,24 @@ INSTANTIATE_TEST_SUITE_P(Basic,
|
||||
187618332,
|
||||
123
|
||||
}
|
||||
},
|
||||
{
|
||||
"Negative timestamp 1969-12-31 23:59:59.123 UTC",
|
||||
DateTime64(-877),
|
||||
3,
|
||||
{
|
||||
0,
|
||||
-877
|
||||
}
|
||||
},
|
||||
{
|
||||
"Positive timestamp 1970-01-01 00:00:00.123 UTC",
|
||||
DateTime64(123),
|
||||
3,
|
||||
{
|
||||
0,
|
||||
123
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -931,12 +931,29 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
|
||||
++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)
|
||||
/// Case1:
|
||||
/// 1965-12-12 12:12:12.123
|
||||
/// => whole = -127914468, fractional = 123(coefficient>0)
|
||||
/// => new whole = -127914467, new fractional = 877(coefficient<0)
|
||||
///
|
||||
/// Case2:
|
||||
/// 1969-12-31 23:59:59.123
|
||||
/// => whole = -1, fractional = 123(coefficient>0)
|
||||
/// => new whole = 0, new fractional = -877(coefficient>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;
|
||||
if (components.whole)
|
||||
{
|
||||
/// whole keep the sign, fractional should be non-negative
|
||||
components.fractional = scale_multiplier - components.fractional;
|
||||
}
|
||||
else
|
||||
{
|
||||
/// when whole is zero, fractional should keep the sign
|
||||
components.fractional = components.fractional - scale_multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 9908870400 is time_t value for 2184-01-01 UTC (a bit over the last year supported by DateTime64)
|
||||
|
@ -805,11 +805,21 @@ 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)
|
||||
/// Case1:
|
||||
/// -127914467.877
|
||||
/// => whole = -127914467, fraction = 877(After DecimalUtils::split)
|
||||
/// => new whole = -127914468(1965-12-12 12:12:12), new fraction = 1000 - 877 = 123(.123)
|
||||
/// => 1965-12-12 12:12:12.123
|
||||
///
|
||||
/// Case2:
|
||||
/// -0.877
|
||||
/// => whole = 0, fractional = -877(After DecimalUtils::split)
|
||||
/// => whole = -1(1969-12-31 23:59:59), fractional = 1000 + (-877) = 123(.123)
|
||||
using T = typename DateTime64::NativeType;
|
||||
if (datetime64.value < 0 && components.fractional)
|
||||
{
|
||||
components.fractional = DecimalUtils::scaleMultiplier<T>(scale) + (components.whole ? T(-1) : T(1)) * components.fractional;
|
||||
--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);
|
||||
@ -989,7 +999,12 @@ void writeText(Decimal<T> x, UInt32 scale, WriteBuffer & ostr, bool trailing_zer
|
||||
{
|
||||
part = DecimalUtils::getFractionalPart(x, scale);
|
||||
if (part || trailing_zeros)
|
||||
{
|
||||
if (part < 0)
|
||||
part *= T(-1);
|
||||
|
||||
writeDecimalFractional(part, scale, ostr, trailing_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
-127914467.877
|
||||
187618332.123
|
||||
1969-12-31 23:59:59.123
|
||||
|
@ -1,2 +1,3 @@
|
||||
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));
|
||||
SELECT toDateTime64('1969-12-31 23:59:59.123', 3, 'UTC');
|
||||
|
Loading…
Reference in New Issue
Block a user