SQL: Fix issue with mins & hours for DATEDIFF (#49252)

Previously, DATEDIFF for minutes and hours was doing a
rounding calculation using all the time fields (secs, msecs/micros/nanos).
Instead it should first truncate the 2 dates to the respective field (mins or hours)
zeroing out all the more detailed time fields and then make the subtraction.

(cherry picked from commit 124cd18e20429e19d52fd8dc383827ea5132d428)
This commit is contained in:
Marios Trivyzas 2019-11-19 13:40:34 +01:00
parent 19602fd573
commit fd1bb4a33a
5 changed files with 93 additions and 7 deletions

View File

@ -395,6 +395,20 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateTimeSeconds]
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateQuarters]
--------------------------------------------------
[NOTE]
For `hour` and `minute`, `DATEDIFF` doesn't do any rounding, but instead first truncates
the more detailed time fields on the 2 dates to zero and then calculates the subtraction.
[source, sql]
--------------------------------------------------
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateTimeHours]
--------------------------------------------------
[source, sql]
--------------------------------------------------
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateTimeMinutes]
--------------------------------------------------
[source, sql]
--------------------------------------------------
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateMinutes]

View File

@ -307,7 +307,7 @@ DATE_DIFF('mcs', '2019-09-04T11:25:21.123456Z'::datetime, '2019-09-04T11:22:33.9
diff_year | diff_quarter | diff_month | diff_week | diff_day | diff_hours | diff_min | diff_sec | diff_millis | diff_mcsec | diff_nsec
-----------+--------------+------------+-----------+----------+------------+----------+-----------+-------------+------------+----------
57 | -114 | 406 | -947 | 2825 | -123228 | 3762357 | -10265677 | 205849864 | -167135802 | 135802468
57 | -114 | 406 | -947 | 2825 | -123228 | 3762356 | -10265677 | 205849864 | -167135802 | 135802468
;
selectDiffWithDate

View File

@ -2495,6 +2495,27 @@ SELECT DATE_DIFF('week', '2019-09-04T11:22:33.000Z'::datetime, '2016-12-08T22:33
// end::dateDiffDateTimeWeeks
;
dateDiffDateTimeHours
// tag::dateDiffDateTimeHours
SELECT DATEDIFF('hours', '2019-11-10T12:10:00.000Z'::datetime, '2019-11-10T23:59:59.999Z'::datetime) AS "diffInHours";
diffInHours
------------------------
11
// end::dateDiffDateTimeHours
;
dateDiffDateTimeMinutes
// tag::dateDiffDateTimeMinutes
SELECT DATEDIFF('minute', '2019-11-10T12:10:00.000Z'::datetime, '2019-11-10T12:15:59.999Z'::datetime) AS "diffInMinutes";
diffInMinutes
------------------------
5
// end::dateDiffDateTimeMinutes
;
dateDiffDateTimeSeconds
// tag::dateDiffDateTimeSeconds
SELECT DATE_DIFF('seconds', '2019-09-04T11:22:33.123Z'::datetime, '2019-07-12T22:33:11.321Z'::datetime) AS "diffInSeconds";

View File

@ -116,12 +116,8 @@ public class DateDiff extends ThreeArgsDateTimeFunction {
}
private static long diffInMinutes(ZonedDateTime start, ZonedDateTime end) {
long secondsDiff = diffInSeconds(start, end);
if (secondsDiff > 0) {
return (long) Math.ceil(secondsDiff / 60.0d);
} else {
return (long) Math.floor(secondsDiff / 60.0d);
}
// Truncate first to minutes (ignore any seconds and sub-seconds fields)
return (end.toEpochSecond() / 60) - (start.toEpochSecond() / 60);
}
private static long diffInHours(ZonedDateTime start, ZonedDateTime end) {

View File

@ -284,6 +284,61 @@ public class DateDiffProcessorTests extends AbstractSqlWireSerializingTestCase<D
.makePipe().asProcessor().process(null));
assertEquals(-436, new DateDiff(Source.EMPTY, l("ww"), dt2, dt1, zoneId)
.makePipe().asProcessor().process(null));
dt1 = l(dateTime(1997, 9, 19, 0, 0, 0, 0));
dt2 = l(dateTime(2004, 8, 2, 7, 59, 23, 0));
assertEquals(60223, new DateDiff(Source.EMPTY, l("hour"), dt1, dt2, UTC)
.makePipe().asProcessor().process(null));
assertEquals(-60223, new DateDiff(Source.EMPTY, l("hours"), dt2, dt1, UTC)
.makePipe().asProcessor().process(null));
assertEquals(60223, new DateDiff(Source.EMPTY, l("hh"), dt1, dt2, zoneId)
.makePipe().asProcessor().process(null));
assertEquals(-60223, new DateDiff(Source.EMPTY, l("hh"), dt2, dt1, zoneId)
.makePipe().asProcessor().process(null));
dt1 = l(dateTime(1997, 9, 19, 0, 0, 0, 0));
dt2 = l(dateTime(2004, 8, 2, 7, 59, 59, 999999999));
assertEquals(60223, new DateDiff(Source.EMPTY, l("hour"), dt1, dt2, UTC)
.makePipe().asProcessor().process(null));
assertEquals(-60223, new DateDiff(Source.EMPTY, l("hours"), dt2, dt1, UTC)
.makePipe().asProcessor().process(null));
assertEquals(60223, new DateDiff(Source.EMPTY, l("hh"), dt1, dt2, zoneId)
.makePipe().asProcessor().process(null));
assertEquals(-60223, new DateDiff(Source.EMPTY, l("hh"), dt2, dt1, zoneId)
.makePipe().asProcessor().process(null));
dt1 = l(dateTime(2002, 4, 27, 0, 0, 0, 0));
dt2 = l(dateTime(2004, 7, 28, 12, 34, 28, 0));
assertEquals(1185874, new DateDiff(Source.EMPTY, l("minute"), dt1, dt2, UTC)
.makePipe().asProcessor().process(null));
assertEquals(-1185874, new DateDiff(Source.EMPTY, l("minutes"), dt2, dt1, UTC)
.makePipe().asProcessor().process(null));
assertEquals(1185874, new DateDiff(Source.EMPTY, l("mi"), dt1, dt2, zoneId)
.makePipe().asProcessor().process(null));
assertEquals(-1185874, new DateDiff(Source.EMPTY, l("n"), dt2, dt1, zoneId)
.makePipe().asProcessor().process(null));
dt1 = l(dateTime(1995, 9, 3, 0, 0, 0, 0));
dt2 = l(dateTime(2004, 7, 26, 12, 30, 34, 0));
assertEquals(4679310, new DateDiff(Source.EMPTY, l("minute"), dt1, dt2, UTC)
.makePipe().asProcessor().process(null));
assertEquals(-4679310, new DateDiff(Source.EMPTY, l("minutes"), dt2, dt1, UTC)
.makePipe().asProcessor().process(null));
assertEquals(4679310, new DateDiff(Source.EMPTY, l("mi"), dt1, dt2, zoneId)
.makePipe().asProcessor().process(null));
assertEquals(-4679310, new DateDiff(Source.EMPTY, l("n"), dt2, dt1, zoneId)
.makePipe().asProcessor().process(null));
dt1 = l(dateTime(1997, 5, 30, 0, 0, 0, 0));
dt2 = l(dateTime(2004, 7, 28, 23, 30, 59, 999999999));
assertEquals(3768450, new DateDiff(Source.EMPTY, l("minute"), dt1, dt2, UTC)
.makePipe().asProcessor().process(null));
assertEquals(-3768450, new DateDiff(Source.EMPTY, l("minutes"), dt2, dt1, UTC)
.makePipe().asProcessor().process(null));
assertEquals(3768450, new DateDiff(Source.EMPTY, l("mi"), dt1, dt2, zoneId)
.makePipe().asProcessor().process(null));
assertEquals(-3768450, new DateDiff(Source.EMPTY, l("n"), dt2, dt1, zoneId)
.makePipe().asProcessor().process(null));
}
public void testOverflow() {