diff options
Diffstat (limited to 'src/mongo/db/query/datetime')
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.cpp | 145 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.h | 52 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support_test.cpp | 315 |
3 files changed, 510 insertions, 2 deletions
diff --git a/src/mongo/db/query/datetime/date_time_support.cpp b/src/mongo/db/query/datetime/date_time_support.cpp index 85a0fde53ac..498a44880be 100644 --- a/src/mongo/db/query/datetime/date_time_support.cpp +++ b/src/mongo/db/query/datetime/date_time_support.cpp @@ -40,6 +40,7 @@ #include "mongo/base/init.h" #include "mongo/bson/util/builder.h" #include "mongo/db/service_context.h" +#include "mongo/platform/overflow_arithmetic.h" #include "mongo/util/assert_util.h" #include "mongo/util/ctype.h" #include "mongo/util/duration.h" @@ -582,4 +583,148 @@ StatusWith<std::string> TimeZone::formatDate(StringData format, Date_t date) con else return formatted.str(); } + +namespace { +auto const kMonthsInOneYear = 12LL; +auto const kDaysInNonLeapYear = 365LL; +auto const kHoursPerDay = 24LL; +auto const kMinutesPerHour = 60LL; +auto const kSecondsPerMinute = 60LL; +auto const kDaysPerWeek = 7LL; +auto const kQuartersPerYear = 4LL; +auto const kQuarterLengthInMonths = 3LL; +auto const kLeapYearReferencePoint = -1000000000L; + +/** + * Determines a number of leap years in a year range (leap year reference point; 'year']. + */ +inline long leapYearsSinceReferencePoint(long year) { + // Count a number of leap years that happened since the reference point, where a leap year is + // when year%4==0, excluding years when year%100==0, except when year%400==0. + auto yearsSinceReferencePoint = year - kLeapYearReferencePoint; + return yearsSinceReferencePoint / 4 - yearsSinceReferencePoint / 100 + + yearsSinceReferencePoint / 400; +} + +/** + * Sums the number of days in the Gregorian calendar in years: 'startYear', + * 'startYear'+1, .., 'endYear'-1. + */ +inline long long daysBetweenYears(long startYear, long endYear) { + return leapYearsSinceReferencePoint(endYear - 1) - leapYearsSinceReferencePoint(startYear - 1) + + (endYear - startYear) * kDaysInNonLeapYear; +} + +/** + * Determines a correction needed in number of hours when calculating passed hours between two time + * instants 'startInstant' and 'endInstant' due to the Daylight Savings Time. Returns 0, if both + * time instants 'startInstant' and 'endInstant' are either in Standard Time (ST) or in Daylight + * Saving Time (DST); returns 1, if 'endInstant' is in ST and 'startInstant' is in DST and + * 'endInstant' > 'startInstant' or 'endInstant' is in DST and 'startInstant' is in ST and + * 'endInstant' < 'startInstant'; otherwise returns -1. + */ +inline long long dstCorrection(timelib_time* startInstant, timelib_time* endInstant) { + return (startInstant->z - endInstant->z) / (kMinutesPerHour * kSecondsPerMinute); +} + +inline long long dateDiffYear(timelib_time* startInstant, timelib_time* endInstant) { + return endInstant->y - startInstant->y; +} + +/** + * Determines which quarter month 'month' belongs to. 'month' value range is 1..12. Returns a number + * of a quarter, where 0 corresponds to the first quarter. + */ +inline int quarter(int month) { + return (month - 1) / kQuarterLengthInMonths; +} +inline long long dateDiffQuarter(timelib_time* startInstant, timelib_time* endInstant) { + return quarter(endInstant->m) - quarter(startInstant->m) + + dateDiffYear(startInstant, endInstant) * kQuartersPerYear; +} +inline long long dateDiffMonth(timelib_time* startInstant, timelib_time* endInstant) { + return endInstant->m - startInstant->m + + dateDiffYear(startInstant, endInstant) * kMonthsInOneYear; +} +inline long long dateDiffDay(timelib_time* startInstant, timelib_time* endInstant) { + return timelib_day_of_year(endInstant->y, endInstant->m, endInstant->d) - + timelib_day_of_year(startInstant->y, startInstant->m, startInstant->d) + + daysBetweenYears(startInstant->y, endInstant->y); +} +inline long long dateDiffWeek(timelib_time* startInstant, timelib_time* endInstant) { + // We use 'timelib_iso_day_of_week()' since it considers Monday as the first day of the week. + return (dateDiffDay(startInstant, endInstant) + + timelib_iso_day_of_week(startInstant->y, startInstant->m, startInstant->d) - + timelib_iso_day_of_week(endInstant->y, endInstant->m, endInstant->d)) / + kDaysPerWeek; +} +inline long long dateDiffHour(timelib_time* startInstant, timelib_time* endInstant) { + return endInstant->h - startInstant->h + dateDiffDay(startInstant, endInstant) * kHoursPerDay + + dstCorrection(startInstant, endInstant); +} +inline long long dateDiffMinute(timelib_time* startInstant, timelib_time* endInstant) { + return endInstant->i - startInstant->i + + dateDiffHour(startInstant, endInstant) * kMinutesPerHour; +} +inline long long dateDiffSecond(timelib_time* startInstant, timelib_time* endInstant) { + return endInstant->s - startInstant->s + + dateDiffMinute(startInstant, endInstant) * kSecondsPerMinute; +} +inline long long dateDiffMillisecond(Date_t startDate, Date_t endDate) { + long long result; + uassert(5166308, + "dateDiff overflowed", + !overflow::sub(endDate.toMillisSinceEpoch(), startDate.toMillisSinceEpoch(), &result)); + return result; +} +} // namespace + +long long dateDiff(Date_t startDate, Date_t endDate, TimeUnit unit, const TimeZone& timezone) { + if (TimeUnit::millisecond == unit) { + return dateDiffMillisecond(startDate, endDate); + } + + // Translate the time instants to the given timezone. + auto startDateInTimeZone = timezone.getTimelibTime(startDate); + auto endDateInTimeZone = timezone.getTimelibTime(endDate); + switch (unit) { + case TimeUnit::year: + return dateDiffYear(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::quarter: + return dateDiffQuarter(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::month: + return dateDiffMonth(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::week: + return dateDiffWeek(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::day: + return dateDiffDay(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::hour: + return dateDiffHour(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::minute: + return dateDiffMinute(startDateInTimeZone.get(), endDateInTimeZone.get()); + case TimeUnit::second: + return dateDiffSecond(startDateInTimeZone.get(), endDateInTimeZone.get()); + default: + MONGO_UNREACHABLE; + } +} + +TimeUnit parseTimeUnit(const std::string& unitName) { + static const StringMap<TimeUnit> timeUnitNameToTimeUnitMap{ + {"year", TimeUnit::year}, + {"quarter", TimeUnit::quarter}, + {"month", TimeUnit::month}, + {"week", TimeUnit::week}, + {"day", TimeUnit::day}, + {"hour", TimeUnit::hour}, + {"minute", TimeUnit::minute}, + {"second", TimeUnit::second}, + {"millisecond", TimeUnit::millisecond}, + }; + auto iterator = timeUnitNameToTimeUnitMap.find(unitName); + uassert(ErrorCodes::FailedToParse, + str::stream() << "unknown time unit value: " << unitName, + iterator != timeUnitNameToTimeUnitMap.end()); + return iterator->second; +} } // namespace mongo diff --git a/src/mongo/db/query/datetime/date_time_support.h b/src/mongo/db/query/datetime/date_time_support.h index 0bc4a779180..0394b661ed2 100644 --- a/src/mongo/db/query/datetime/date_time_support.h +++ b/src/mongo/db/query/datetime/date_time_support.h @@ -311,10 +311,9 @@ public: */ static void validateToStringFormat(StringData format); static void validateFromStringFormat(StringData format); - -private: std::unique_ptr<_timelib_time, TimelibTimeDeleter> getTimelibTime(Date_t) const; +private: /** * Only works with 1 <= spaces <= 4 and 0 <= number <= 9999. If spaces is less than the digit * count of number we simply insert the number without padding. @@ -472,4 +471,53 @@ private: std::unique_ptr<_timelib_tzdb, TimeZoneDBDeleter> _timeZoneDatabase; }; +/** + * A set of standard measures of time used to express a length of time interval. + */ +enum class TimeUnit { + year, + quarter, // A quarter of a year. + month, + week, + day, + hour, + minute, + second, + millisecond +}; + +/** + * Parses a string representation of an enumerator of TimeUnit type 'unitName' into a value of type + * TimeUnit. Throws an exception with error code ErrorCodes::FailedToParse when passed an invalid + * name. + */ +TimeUnit parseTimeUnit(const std::string& unitName); + +/** + * Determines the number of upper boundaries of time intervals crossed when moving from time instant + * 'startDate' to time instant 'endDate' in time zone 'timezone'. The time intervals are of length + * equal to one 'unit' and aligned so that the lower/upper bound is located in time axis at instant + * n*'unit', where n is an integer. + * + * If 'endDate' < 'startDate', then the returned number of crossed boundaries is negative. + * + * For 'unit' values 'hour' and smaller, when there is a transition from Daylight Saving Time to + * standard time the function behaves as if standard time intervals overlap Daylight Saving Time + * intervals. When there is a transition from standard time to Daylight Saving Time the function + * behaves as if the last interval in standard time is longer by one hour. + * + * An example: if startDate=2011-01-31T00:00:00 (in 'timezone'), endDate=2011-02-01T00:00:00 (in + * 'timezone'), unit='month', then the function returns 1, since a month boundary at + * 2011-02-01T00:00:00 was crossed. + * + * The function operates in the Gregorian calendar. The function does not account for leap seconds. + * For time instants before year 1583 the proleptic Gregorian calendar is used. + * + * startDate - starting time instant in UTC time zone. + * endDate - ending time instant in UTC time zone. + * unit - length of time intervals. + * timezone - determines the timezone used for counting the boundaries as well as Daylight Saving + * Time rules. + */ +long long dateDiff(Date_t startDate, Date_t endDate, TimeUnit unit, const TimeZone& timezone); } // namespace mongo diff --git a/src/mongo/db/query/datetime/date_time_support_test.cpp b/src/mongo/db/query/datetime/date_time_support_test.cpp index fba52b3e029..247ed2b8e2b 100644 --- a/src/mongo/db/query/datetime/date_time_support_test.cpp +++ b/src/mongo/db/query/datetime/date_time_support_test.cpp @@ -29,7 +29,9 @@ #include "mongo/platform/basic.h" +#include <limits> #include <sstream> +#include <timelib.h> #include "mongo/db/query/datetime/date_time_support.h" #include "mongo/unittest/unittest.h" @@ -1191,5 +1193,318 @@ TEST(DayOfWeek, DayNumber) { } } +// Time zones for testing 'dateDiff()'. +const TimeZone kNewYorkTimeZone = kDefaultTimeZoneDatabase.getTimeZone("America/New_York"); +const TimeZone kAustraliaEuclaTimeZone = + kDefaultTimeZoneDatabase.getTimeZone("Australia/Eucla"); // UTC offset +08:45 + +// Verifies 'dateDiff()' with TimeUnit::year. +TEST(DateDiff, Year) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2000, 12, 31, 23, 59, 59, 500), + kNewYorkTimeZone.createFromDateParts(2000, 12, 31, 23, 59, 59, 999), + TimeUnit::year, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2000, 12, 31, 23, 59, 59, 500), + kNewYorkTimeZone.createFromDateParts(2001, 1, 1, 0, 0, 0, 0), + TimeUnit::year, + kNewYorkTimeZone)); + ASSERT_EQ(-1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2001, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2000, 12, 31, 23, 59, 59, 500), + TimeUnit::year, + kNewYorkTimeZone)); + ASSERT_EQ(999, + dateDiff(kNewYorkTimeZone.createFromDateParts(1002, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2001, 1, 1, 0, 0, 0, 0), + TimeUnit::year, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::month. +TEST(DateDiff, Month) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 2, 28, 23, 59, 59, 500), + kNewYorkTimeZone.createFromDateParts(2020, 2, 29, 23, 59, 59, 999), + TimeUnit::month, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 2, 29, 23, 59, 59, 999), + kNewYorkTimeZone.createFromDateParts(2020, 3, 1, 0, 0, 0, 0), + TimeUnit::month, + kNewYorkTimeZone)); + ASSERT_EQ(-14, + dateDiff(kNewYorkTimeZone.createFromDateParts(2010, 2, 28, 23, 59, 59, 999), + kNewYorkTimeZone.createFromDateParts(2008, 12, 31, 23, 59, 59, 500), + TimeUnit::month, + kNewYorkTimeZone)); + ASSERT_EQ(1500 * 12, + dateDiff(kNewYorkTimeZone.createFromDateParts(520, 3, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 3, 1, 0, 0, 0, 0), + TimeUnit::month, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::quarter. +TEST(DateDiff, Quarter) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 3, 31, 23, 59, 59, 999), + TimeUnit::quarter, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 4, 1, 0, 0, 0, 0), + TimeUnit::quarter, + kNewYorkTimeZone)); + ASSERT_EQ(-2001, + dateDiff(kNewYorkTimeZone.createFromDateParts(2000, 12, 31, 23, 59, 59, 500), + kNewYorkTimeZone.createFromDateParts(1500, 9, 30, 23, 59, 59, 999), + TimeUnit::quarter, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::week. +TEST(DateDiff, Week) { + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 2, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 9, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 2, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 15, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 2, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 2, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 2, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 2, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 3, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 9, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 15, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); + ASSERT_EQ(-5, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 10, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 10, 8, 0, 0, 0, 0), + TimeUnit::week, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::day. +TEST(DateDiff, Day) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 23, 59, 59, 999), + TimeUnit::day, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 9, 0, 0, 0, 0), + TimeUnit::day, + kNewYorkTimeZone)); + ASSERT_EQ(-1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 9, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 23, 59, 59, 999), + TimeUnit::day, + kNewYorkTimeZone)); + + // Verifies number of days in a year calculation. + ASSERT_EQ(369, + dateDiff(kNewYorkTimeZone.createFromDateParts(1999, 12, 30, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2001, 1, 2, 0, 0, 0, 0), + TimeUnit::day, + kNewYorkTimeZone)); + ASSERT_EQ(6575, + dateDiff(kNewYorkTimeZone.createFromDateParts(1583, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(1601, 1, 1, 0, 0, 0, 0), + TimeUnit::day, + kNewYorkTimeZone)); + ASSERT_EQ(-6575, + dateDiff(kNewYorkTimeZone.createFromDateParts(1601, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(1583, 1, 1, 0, 0, 0, 0), + TimeUnit::day, + kNewYorkTimeZone)); + ASSERT_EQ(29, + dateDiff(kNewYorkTimeZone.createFromDateParts(2004, 2, 10, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2004, 3, 10, 0, 0, 0, 0), + TimeUnit::day, + kNewYorkTimeZone)); + ASSERT_EQ(28, + dateDiff(kAustraliaEuclaTimeZone.createFromDateParts(2005, 2, 10, 0, 0, 0, 0), + kAustraliaEuclaTimeZone.createFromDateParts(2005, 3, 10, 0, 0, 0, 0), + TimeUnit::day, + kAustraliaEuclaTimeZone)); + + // Use timelib_day_of_year as an oracle to verify day calculations. + for (int year = -1000; year < 3000; ++year) { + int expectedNumberOfDays = timelib_day_of_year(year, 12, 31) + 1; + ASSERT_EQ(expectedNumberOfDays, + dateDiff(kNewYorkTimeZone.createFromDateParts(year, 2, 3, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(year + 1, 2, 3, 0, 0, 0, 0), + TimeUnit::day, + kNewYorkTimeZone)); + } +} + +// Verifies 'dateDiff()' with TimeUnit::hour. +TEST(DateDiff, Hour) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 59, 59, 999), + TimeUnit::hour, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 59, 59, 999), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 2, 0, 0, 0), + TimeUnit::hour, + kNewYorkTimeZone)); + ASSERT_EQ(-25, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 10, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 23, 59, 59, 999), + TimeUnit::hour, + kNewYorkTimeZone)); + + // Hour difference calculation in UTC offset +08:45 time zone. + ASSERT_EQ( + 1, + dateDiff( + kAustraliaEuclaTimeZone.createFromDateParts(2020, 11, 10, 20, 55, 0, 0) /*UTC 12:10*/, + kAustraliaEuclaTimeZone.createFromDateParts(2020, 11, 10, 21, 5, 0, 0) /*UTC 12:20*/, + TimeUnit::hour, + kAustraliaEuclaTimeZone)); + + // Test of transition from DST to standard time. + ASSERT_EQ(1, + dateDiff(kDefaultTimeZone.createFromDateParts( + 2020, 11, 1, 5, 0, 0, 0) /* America/New_York 1:00AM EDT (UTC-4)*/, + kDefaultTimeZone.createFromDateParts( + 2020, 11, 1, 6, 0, 0, 0) /* America/New_York 1:00AM EST (UTC-5)*/, + TimeUnit::hour, + kNewYorkTimeZone)); + ASSERT_EQ(-1, + dateDiff(kDefaultTimeZone.createFromDateParts( + 2020, 11, 1, 6, 0, 0, 0) /* America/New_York 1:00AM EST (UTC-5)*/, + kDefaultTimeZone.createFromDateParts( + 2020, 11, 1, 5, 0, 0, 0) /* America/New_York 1:00AM EDT (UTC-4)*/, + TimeUnit::hour, + kNewYorkTimeZone)); + + // Test of transition from standard time to DST. + ASSERT_EQ(1, + dateDiff(kDefaultTimeZone.createFromDateParts( + 2020, 3, 8, 6, 45, 0, 0) /* America/New_York 1:45AM EST (UTC-5)*/, + kDefaultTimeZone.createFromDateParts( + 2020, 3, 8, 7, 0, 0, 0) /* America/New_York 3:00AM EDT (UTC-4)*/, + TimeUnit::hour, + kNewYorkTimeZone)); + ASSERT_EQ(-1, + dateDiff(kDefaultTimeZone.createFromDateParts( + 2020, 3, 8, 7, 0, 0, 0) /* America/New_York 3:00AM EDT (UTC-4)*/, + kDefaultTimeZone.createFromDateParts( + 2020, 3, 8, 6, 45, 0, 0) /* America/New_York 1:45AM EST (UTC-5)*/, + TimeUnit::hour, + kNewYorkTimeZone)); + + // Longer period test. + ASSERT_EQ(17545, + dateDiff(kNewYorkTimeZone.createFromDateParts(1999, 1, 1, 0, 0, 0, 0), + kNewYorkTimeZone.createFromDateParts(2001, 1, 1, 1, 0, 0, 0), + TimeUnit::hour, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::minute. +TEST(DateDiff, Minute) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 59, 999), + TimeUnit::minute, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 59, 999), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 31, 0, 0), + TimeUnit::minute, + kNewYorkTimeZone)); + ASSERT_EQ(-25, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 55, 0, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 59, 999), + TimeUnit::minute, + kNewYorkTimeZone)); + ASSERT_EQ(234047495, + dateDiff(kNewYorkTimeZone.createFromDateParts(1585, 11, 8, 1, 55, 0, 0), + kNewYorkTimeZone.createFromDateParts(2030, 11, 8, 1, 30, 59, 999), + TimeUnit::minute, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::second. +TEST(DateDiff, Second) { + ASSERT_EQ(0, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 999), + TimeUnit::second, + kNewYorkTimeZone)); + ASSERT_EQ(1, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 999), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 16, 0), + TimeUnit::second, + kNewYorkTimeZone)); + ASSERT_EQ(-2401, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 2, 10, 16, 999), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 999), + TimeUnit::second, + kNewYorkTimeZone)); + ASSERT_EQ(1604971816, + dateDiff(kDefaultTimeZone.createFromDateParts(1970, 1, 1, 0, 0, 0, 0), + kDefaultTimeZone.createFromDateParts(2020, 11, 10, 1, 30, 16, 0), + TimeUnit::second, + kNewYorkTimeZone)); +} + +// Verifies 'dateDiff()' with TimeUnit::millisecond. +TEST(DateDiff, Millisecond) { + ASSERT_EQ(100, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 0), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 100), + TimeUnit::millisecond, + kNewYorkTimeZone)); + ASSERT_EQ(-1500, + dateDiff(kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 16, 500), + kNewYorkTimeZone.createFromDateParts(2020, 11, 8, 1, 30, 15, 0), + TimeUnit::millisecond, + kNewYorkTimeZone)); + ASSERT_EQ(1604971816000, + dateDiff(kDefaultTimeZone.createFromDateParts(1970, 1, 1, 0, 0, 0, 0), + kDefaultTimeZone.createFromDateParts(2020, 11, 10, 1, 30, 16, 0), + TimeUnit::millisecond, + kNewYorkTimeZone)); + + // Verifies numeric overflow handling. + ASSERT_THROWS_CODE(dateDiff(Date_t::fromMillisSinceEpoch(std::numeric_limits<long long>::min()), + Date_t::fromMillisSinceEpoch(std::numeric_limits<long long>::max()), + TimeUnit::millisecond, + kNewYorkTimeZone), + AssertionException, + 5166308); +} } // namespace } // namespace mongo |