Merge pull request #22060 from ClickHouse/to-start-of-interval-hour-align

Change behaviour of `toStartOfInterval` in backward incompatible way
This commit is contained in:
alexey-milovidov 2021-03-29 17:52:41 +03:00 committed by GitHub
commit 9fd1577cd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 151 additions and 11 deletions

View File

@ -853,15 +853,43 @@ public:
{
if (hours == 1)
return toStartOfHour(t);
/** We will round the hour number since the midnight.
* It may split the day into non-equal intervals.
* For example, if we will round to 11-hour interval,
* the day will be split to the intervals 00:00:00..10:59:59, 11:00:00..21:59:59, 22:00:00..23:59:59.
* In case of daylight saving time or other transitions,
* the intervals can be shortened or prolonged to the amount of transition.
*/
UInt64 seconds = hours * 3600;
t = roundDown(t, seconds);
const LUTIndex index = findIndex(t);
const Values & values = lut[index];
if (t >= 0 && offset_is_whole_number_of_hours_during_epoch)
return t;
time_t time = t - values.date;
if (time >= values.time_at_offset_change())
{
/// Align to new hour numbers before rounding.
time += values.amount_of_offset_change();
time = time / seconds * seconds;
/// TODO check if it's correct.
return toStartOfHour(t);
/// Should subtract the shift back but only if rounded time is not before shift.
if (time >= values.time_at_offset_change())
{
time -= values.amount_of_offset_change();
/// With cutoff at the time of the shift. Otherwise we may end up with something like 23:00 previous day.
if (time < values.time_at_offset_change())
time = values.time_at_offset_change();
}
}
else
{
time = time / seconds * seconds;
}
return values.date + time;
}
inline time_t toStartOfMinuteInterval(time_t t, UInt64 minutes) const
@ -869,6 +897,14 @@ public:
if (minutes == 1)
return toStartOfMinute(t);
/** In contrast to "toStartOfHourInterval" function above,
* the minute intervals are not aligned to the midnight.
* You will get unexpected results if for example, you round down to 60 minute interval
* and there was a time shift to 30 minutes.
*
* But this is not specified in docs and can be changed in future.
*/
UInt64 seconds = 60 * minutes;
return roundDown(t, seconds);
}

View File

@ -130,7 +130,6 @@ TEST(DateLUTTest, TimeValuesInMiddleOfRange)
EXPECT_EQ(lut.toRelativeQuarterNum(time), 8078 /*unsigned*/);
EXPECT_EQ(lut.toRelativeHourNum(time), 435736 /*time_t*/);
EXPECT_EQ(lut.toRelativeMinuteNum(time), 26144180 /*time_t*/);
EXPECT_EQ(lut.toStartOfHourInterval(time, 5), 1568646000 /*time_t*/);
EXPECT_EQ(lut.toStartOfMinuteInterval(time, 6), 1568650680 /*time_t*/);
EXPECT_EQ(lut.toStartOfSecondInterval(time, 7), 1568650811 /*time_t*/);
EXPECT_EQ(lut.toNumYYYYMM(time), 201909 /*UInt32*/);
@ -191,7 +190,6 @@ TEST(DateLUTTest, TimeValuesAtLeftBoderOfRange)
EXPECT_EQ(lut.toRelativeQuarterNum(time), 7880 /*unsigned*/); // ?
EXPECT_EQ(lut.toRelativeHourNum(time), 0 /*time_t*/);
EXPECT_EQ(lut.toRelativeMinuteNum(time), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfHourInterval(time, 5), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfMinuteInterval(time, 6), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfSecondInterval(time, 7), 0 /*time_t*/);
EXPECT_EQ(lut.toNumYYYYMM(time), 197001 /*UInt32*/);
@ -253,7 +251,6 @@ TEST(DateLUTTest, TimeValuesAtRightBoderOfRangeOfOldLUT)
EXPECT_EQ(lut.toRelativeQuarterNum(time), 8424 /*unsigned*/);
EXPECT_EQ(lut.toRelativeHourNum(time), 1192873 /*time_t*/);
EXPECT_EQ(lut.toRelativeMinuteNum(time), 71572397 /*time_t*/);
EXPECT_EQ(lut.toStartOfHourInterval(time, 5), 4294332000 /*time_t*/);
EXPECT_EQ(lut.toStartOfMinuteInterval(time, 6), 4294343520 /*time_t*/);
EXPECT_EQ(lut.toStartOfSecondInterval(time, 7), 4294343872 /*time_t*/);
EXPECT_EQ(lut.toNumYYYYMM(time), 210601 /*UInt32*/);

View File

@ -148,9 +148,9 @@ toStartOfInterval
2019-02-05 00:00:00
2019-02-03 00:00:00
2019-02-06 22:00:00
2019-02-06 21:00:00
2019-02-06 21:00:00
2019-02-06 03:00:00
2019-02-06 22:00:00
2019-02-06 18:00:00
2019-02-06 00:00:00
2019-02-06 22:57:00
2019-02-06 22:56:00
2019-02-06 22:55:00

View File

@ -0,0 +1,86 @@
2021-03-23 00:00:00
2021-03-23 11:00:00
2021-03-23 22:00:00
2021-03-23 13:00:00
2021-03-23 12:00:00
2021-03-23 00:00:00
2010-03-28 00:00:00 2010-03-28 00:00:00 1269723600
2010-03-28 00:15:00 2010-03-28 00:00:00 1269724500
2010-03-28 00:30:00 2010-03-28 00:00:00 1269725400
2010-03-28 00:45:00 2010-03-28 00:00:00 1269726300
2010-03-28 01:00:00 2010-03-28 00:00:00 1269727200
2010-03-28 01:15:00 2010-03-28 00:00:00 1269728100
2010-03-28 01:30:00 2010-03-28 00:00:00 1269729000
2010-03-28 01:45:00 2010-03-28 00:00:00 1269729900
2010-03-28 03:00:00 2010-03-28 03:00:00 1269730800
2010-03-28 03:15:00 2010-03-28 03:00:00 1269731700
2010-03-28 03:30:00 2010-03-28 03:00:00 1269732600
2010-03-28 03:45:00 2010-03-28 03:00:00 1269733500
2010-03-28 04:00:00 2010-03-28 04:00:00 1269734400
2010-03-28 04:15:00 2010-03-28 04:00:00 1269735300
2010-03-28 04:30:00 2010-03-28 04:00:00 1269736200
2010-03-28 04:45:00 2010-03-28 04:00:00 1269737100
2010-03-28 05:00:00 2010-03-28 04:00:00 1269738000
2010-03-28 05:15:00 2010-03-28 04:00:00 1269738900
2010-03-28 05:30:00 2010-03-28 04:00:00 1269739800
2010-03-28 05:45:00 2010-03-28 04:00:00 1269740700
2010-10-31 00:00:00 2010-10-31 00:00:00 1288468800
2010-10-31 00:15:00 2010-10-31 00:00:00 1288469700
2010-10-31 00:30:00 2010-10-31 00:00:00 1288470600
2010-10-31 00:45:00 2010-10-31 00:00:00 1288471500
2010-10-31 01:00:00 2010-10-31 00:00:00 1288472400
2010-10-31 01:15:00 2010-10-31 00:00:00 1288473300
2010-10-31 01:30:00 2010-10-31 00:00:00 1288474200
2010-10-31 01:45:00 2010-10-31 00:00:00 1288475100
2010-10-31 02:00:00 2010-10-31 02:00:00 1288476000
2010-10-31 02:15:00 2010-10-31 02:00:00 1288476900
2010-10-31 02:30:00 2010-10-31 02:00:00 1288477800
2010-10-31 02:45:00 2010-10-31 02:00:00 1288478700
2010-10-31 02:00:00 2010-10-31 02:00:00 1288479600
2010-10-31 02:15:00 2010-10-31 02:00:00 1288480500
2010-10-31 02:30:00 2010-10-31 02:00:00 1288481400
2010-10-31 02:45:00 2010-10-31 02:00:00 1288482300
2010-10-31 03:00:00 2010-10-31 02:00:00 1288483200
2010-10-31 03:15:00 2010-10-31 02:00:00 1288484100
2010-10-31 03:30:00 2010-10-31 02:00:00 1288485000
2010-10-31 03:45:00 2010-10-31 02:00:00 1288485900
2020-04-05 00:00:00 2020-04-05 00:00:00 1586005200
2020-04-05 00:15:00 2020-04-05 00:00:00 1586006100
2020-04-05 00:30:00 2020-04-05 00:00:00 1586007000
2020-04-05 00:45:00 2020-04-05 00:00:00 1586007900
2020-04-05 01:00:00 2020-04-05 00:00:00 1586008800
2020-04-05 01:15:00 2020-04-05 00:00:00 1586009700
2020-04-05 01:30:00 2020-04-05 00:00:00 1586010600
2020-04-05 01:45:00 2020-04-05 00:00:00 1586011500
2020-04-05 01:30:00 2020-04-05 00:00:00 1586012400
2020-04-05 01:45:00 2020-04-05 00:00:00 1586013300
2020-04-05 02:00:00 2020-04-05 02:00:00 1586014200
2020-04-05 02:15:00 2020-04-05 02:00:00 1586015100
2020-04-05 02:30:00 2020-04-05 02:00:00 1586016000
2020-04-05 02:45:00 2020-04-05 02:00:00 1586016900
2020-04-05 03:00:00 2020-04-05 02:00:00 1586017800
2020-04-05 03:15:00 2020-04-05 02:00:00 1586018700
2020-04-05 03:30:00 2020-04-05 02:00:00 1586019600
2020-04-05 03:45:00 2020-04-05 02:00:00 1586020500
2020-04-05 04:00:00 2020-04-05 04:00:00 1586021400
2020-04-05 04:15:00 2020-04-05 04:00:00 1586022300
2020-10-04 00:00:00 2020-10-04 00:00:00 1601731800
2020-10-04 00:15:00 2020-10-04 00:00:00 1601732700
2020-10-04 00:30:00 2020-10-04 00:00:00 1601733600
2020-10-04 00:45:00 2020-10-04 00:00:00 1601734500
2020-10-04 01:00:00 2020-10-04 00:00:00 1601735400
2020-10-04 01:15:00 2020-10-04 00:00:00 1601736300
2020-10-04 01:30:00 2020-10-04 00:00:00 1601737200
2020-10-04 01:45:00 2020-10-04 00:00:00 1601738100
2020-10-04 02:30:00 2020-10-04 02:30:00 1601739000
2020-10-04 02:45:00 2020-10-04 02:30:00 1601739900
2020-10-04 03:00:00 2020-10-04 02:30:00 1601740800
2020-10-04 03:15:00 2020-10-04 02:30:00 1601741700
2020-10-04 03:30:00 2020-10-04 02:30:00 1601742600
2020-10-04 03:45:00 2020-10-04 02:30:00 1601743500
2020-10-04 04:00:00 2020-10-04 04:00:00 1601744400
2020-10-04 04:15:00 2020-10-04 04:00:00 1601745300
2020-10-04 04:30:00 2020-10-04 04:00:00 1601746200
2020-10-04 04:45:00 2020-10-04 04:00:00 1601747100
2020-10-04 05:00:00 2020-10-04 04:00:00 1601748000
2020-10-04 05:15:00 2020-10-04 04:00:00 1601748900

View File

@ -0,0 +1,21 @@
-- Rounding down to hour intervals is aligned to midnight even if the interval length does not divide the whole day.
SELECT toStartOfInterval(toDateTime('2021-03-23 03:58:00'), INTERVAL 11 HOUR);
SELECT toStartOfInterval(toDateTime('2021-03-23 13:58:00'), INTERVAL 11 HOUR);
SELECT toStartOfInterval(toDateTime('2021-03-23 23:58:00'), INTERVAL 11 HOUR);
-- It should work correctly even in timezones with non-whole hours offset. India have +05:30.
SELECT toStartOfHour(toDateTime('2021-03-23 13:58:00', 'Asia/Kolkata'));
SELECT toStartOfInterval(toDateTime('2021-03-23 13:58:00', 'Asia/Kolkata'), INTERVAL 6 HOUR);
-- Specifying the interval longer than 24 hours is not correct, but it works as expected by just rounding to midnight.
SELECT toStartOfInterval(toDateTime('2021-03-23 13:58:00', 'Asia/Kolkata'), INTERVAL 66 HOUR);
-- In case of timezone shifts, rounding is performed to the hour number on "wall clock" time.
-- The intervals may become shorter or longer due to time shifts. For example, the three hour interval may actually last two hours.
-- If the same hour number on "wall clock" time correspond to multiple time points due to shifting backwards, the unspecified time point is selected among the candidates.
SELECT toDateTime('2010-03-28 00:00:00', 'Europe/Moscow') + INTERVAL 15 * number MINUTE AS src, toStartOfInterval(src, INTERVAL 2 HOUR) AS rounded, toUnixTimestamp(src) AS t FROM numbers(20);
SELECT toDateTime('2010-10-31 00:00:00', 'Europe/Moscow') + INTERVAL 15 * number MINUTE AS src, toStartOfInterval(src, INTERVAL 2 HOUR) AS rounded, toUnixTimestamp(src) AS t FROM numbers(20);
-- And this should work even for non whole number of hours shifts.
SELECT toDateTime('2020-04-05 00:00:00', 'Australia/Lord_Howe') + INTERVAL 15 * number MINUTE AS src, toStartOfInterval(src, INTERVAL 2 HOUR) AS rounded, toUnixTimestamp(src) AS t FROM numbers(20);
SELECT toDateTime('2020-10-04 00:00:00', 'Australia/Lord_Howe') + INTERVAL 15 * number MINUTE AS src, toStartOfInterval(src, INTERVAL 2 HOUR) AS rounded, toUnixTimestamp(src) AS t FROM numbers(20);