// Copyright 2022 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // License, and you may not use this file except in compliance with the Business Source License. // // As of the Change Date specified in that file, in accordance with // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. #include #include #include #include #include #include #include "utils/exceptions.hpp" #include "utils/memory.hpp" #include "utils/temporal.hpp" namespace { std::string ToString(const memgraph::utils::DateParameters &date_parameters) { return fmt::format("{:04d}-{:02d}-{:02d}", date_parameters.year, date_parameters.month, date_parameters.day); } std::string ToString(const memgraph::utils::LocalTimeParameters &local_time_parameters) { return fmt::format("{:02}:{:02d}:{:02d}", local_time_parameters.hour, local_time_parameters.minute, local_time_parameters.second); } struct TestDateParameters { memgraph::utils::DateParameters date_parameters; bool should_throw; }; inline constexpr std::array test_dates{ TestDateParameters{{-1996, 11, 22}, true}, TestDateParameters{{1996, -11, 22}, true}, TestDateParameters{{1996, 11, -22}, true}, TestDateParameters{{1, 13, 3}, true}, TestDateParameters{{1, 12, 32}, true}, TestDateParameters{{1, 2, 29}, true}, TestDateParameters{{2020, 2, 29}, false}, TestDateParameters{{1700, 2, 29}, true}, TestDateParameters{{1200, 2, 29}, false}, TestDateParameters{{10000, 12, 3}, true}}; struct TestLocalTimeParameters { memgraph::utils::LocalTimeParameters local_time_parameters; bool should_throw; }; inline constexpr std::array test_local_times{TestLocalTimeParameters{{.hour = 24}, true}, TestLocalTimeParameters{{.hour = -1}, true}, TestLocalTimeParameters{{.minute = -1}, true}, TestLocalTimeParameters{{.minute = 60}, true}, TestLocalTimeParameters{{.second = -1}, true}, TestLocalTimeParameters{{.minute = 60}, true}, TestLocalTimeParameters{{.millisecond = -1}, true}, TestLocalTimeParameters{{.millisecond = 1000}, true}, TestLocalTimeParameters{{.microsecond = -1}, true}, TestLocalTimeParameters{{.microsecond = 1000}, true}, TestLocalTimeParameters{{23, 59, 59, 999, 999}, false}, TestLocalTimeParameters{{0, 0, 0, 0, 0}, false}}; } // namespace TEST(TemporalTest, DateConstruction) { std::optional test_date; for (const auto [date_parameters, should_throw] : test_dates) { if (should_throw) { EXPECT_THROW(test_date.emplace(date_parameters), memgraph::utils::BasicException) << ToString(date_parameters); } else { EXPECT_NO_THROW(test_date.emplace(date_parameters)) << ToString(date_parameters); } } } TEST(TemporalTest, DateMicrosecondsSinceEpochConversion) { const auto check_microseconds = [](const auto date_parameters) { memgraph::utils::Date initial_date{date_parameters}; const auto microseconds = initial_date.MicrosecondsSinceEpoch(); memgraph::utils::Date new_date{microseconds}; ASSERT_EQ(initial_date, new_date); }; check_microseconds(memgraph::utils::DateParameters{2020, 11, 22}); check_microseconds(memgraph::utils::DateParameters{1900, 2, 22}); check_microseconds(memgraph::utils::DateParameters{0, 1, 1}); check_microseconds(memgraph::utils::DateParameters{1994, 12, 7}); ASSERT_THROW(check_microseconds(memgraph::utils::DateParameters{-10, 1, 1}), memgraph::utils::BasicException); { memgraph::utils::Date date{memgraph::utils::DateParameters{1970, 1, 1}}; ASSERT_EQ(date.MicrosecondsSinceEpoch(), 0); } { memgraph::utils::Date date{memgraph::utils::DateParameters{1910, 1, 1}}; ASSERT_LT(date.MicrosecondsSinceEpoch(), 0); } { memgraph::utils::Date date{memgraph::utils::DateParameters{2021, 1, 1}}; ASSERT_GT(date.MicrosecondsSinceEpoch(), 0); } } TEST(TemporalTest, LocalTimeConstruction) { std::optional test_local_time; for (const auto [local_time_parameters, should_throw] : test_local_times) { if (should_throw) { ASSERT_THROW(test_local_time.emplace(local_time_parameters), memgraph::utils::BasicException); } else { ASSERT_NO_THROW(test_local_time.emplace(local_time_parameters)); } } } TEST(TemporalTest, LocalTimeMicrosecondsSinceEpochConversion) { const auto check_microseconds = [](const memgraph::utils::LocalTimeParameters ¶meters) { memgraph::utils::LocalTime initial_local_time{parameters}; const auto microseconds = initial_local_time.MicrosecondsSinceEpoch(); memgraph::utils::LocalTime new_local_time{microseconds}; ASSERT_EQ(initial_local_time, new_local_time); }; check_microseconds(memgraph::utils::LocalTimeParameters{23, 59, 59, 999, 999}); check_microseconds(memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); check_microseconds(memgraph::utils::LocalTimeParameters{14, 8, 55, 321, 452}); } TEST(TemporalTest, LocalDateTimeMicrosecondsSinceEpochConversion) { const auto check_microseconds = [](const memgraph::utils::DateParameters date_parameters, const memgraph::utils::LocalTimeParameters &local_time_parameters) { memgraph::utils::LocalDateTime initial_local_date_time{date_parameters, local_time_parameters}; const auto microseconds = initial_local_date_time.MicrosecondsSinceEpoch(); memgraph::utils::LocalDateTime new_local_date_time{microseconds}; ASSERT_EQ(initial_local_date_time, new_local_date_time); }; check_microseconds(memgraph::utils::DateParameters{2020, 11, 22}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); check_microseconds(memgraph::utils::DateParameters{1900, 2, 22}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); check_microseconds(memgraph::utils::DateParameters{0, 1, 1}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); check_microseconds(memgraph::utils::DateParameters{1961, 1, 1}, memgraph::utils::LocalTimeParameters{15, 44, 12}); check_microseconds(memgraph::utils::DateParameters{1969, 12, 31}, memgraph::utils::LocalTimeParameters{23, 59, 59}); { memgraph::utils::LocalDateTime local_date_time(memgraph::utils::DateParameters{1970, 1, 1}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(), 0); } { memgraph::utils::LocalDateTime local_date_time(memgraph::utils::DateParameters{1970, 1, 1}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 1}); ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0); } { memgraph::utils::LocalTimeParameters local_time_parameters{12, 10, 40, 42, 42}; memgraph::utils::LocalDateTime local_date_time{memgraph::utils::DateParameters{1970, 1, 1}, local_time_parameters}; ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(), memgraph::utils::LocalTime{local_time_parameters}.MicrosecondsSinceEpoch()); } { memgraph::utils::LocalDateTime local_date_time(memgraph::utils::DateParameters{1910, 1, 1}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); ASSERT_LT(local_date_time.MicrosecondsSinceEpoch(), 0); } { memgraph::utils::LocalDateTime local_date_time(memgraph::utils::DateParameters{2021, 1, 1}, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}); ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0); } { // Assert ordering for dates prior the unix epoch. // If this test fails, our storage indexes will be incorrect. memgraph::utils::LocalDateTime ldt({1969, 12, 31}, {0, 0, 0}); memgraph::utils::LocalDateTime ldt2({1969, 12, 31}, {23, 59, 59}); ASSERT_LT(ldt.MicrosecondsSinceEpoch(), ldt2.MicrosecondsSinceEpoch()); } } TEST(TemporalTest, DurationConversion) { { memgraph::utils::Duration duration{{.minute = 123.25}}; const auto microseconds = duration.microseconds; memgraph::utils::LocalDateTime local_date_time{microseconds}; ASSERT_EQ(local_date_time.date.year, 1970); ASSERT_EQ(local_date_time.date.month, 1); ASSERT_EQ(local_date_time.date.day, 1); ASSERT_EQ(local_date_time.local_time.hour, 2); ASSERT_EQ(local_date_time.local_time.minute, 3); ASSERT_EQ(local_date_time.local_time.second, 15); }; } namespace { using namespace std::literals; inline constexpr std::array parsing_test_dates_extended{ std::make_pair("2020-11-22"sv, memgraph::utils::DateParameters{2020, 11, 22}), std::make_pair("2020-11"sv, memgraph::utils::DateParameters{2020, 11}), }; inline constexpr std::array parsing_test_dates_basic{ std::make_pair("20201122"sv, memgraph::utils::DateParameters{2020, 11, 22})}; inline constexpr std::array parsing_test_local_time_extended{ std::make_pair("19:23:21.123456"sv, memgraph::utils::LocalTimeParameters{19, 23, 21, 123, 456}), std::make_pair("19:23:21.123"sv, memgraph::utils::LocalTimeParameters{19, 23, 21, 123}), std::make_pair("19:23:21"sv, memgraph::utils::LocalTimeParameters{19, 23, 21}), std::make_pair("19:23"sv, memgraph::utils::LocalTimeParameters{19, 23}), std::make_pair("00:00:00.000000"sv, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}), std::make_pair("01:02:03.004005"sv, memgraph::utils::LocalTimeParameters{1, 2, 3, 4, 5}), }; inline constexpr std::array parsing_test_local_time_basic{ std::make_pair("192321.123456"sv, memgraph::utils::LocalTimeParameters{19, 23, 21, 123, 456}), std::make_pair("192321.123"sv, memgraph::utils::LocalTimeParameters{19, 23, 21, 123}), std::make_pair("192321"sv, memgraph::utils::LocalTimeParameters{19, 23, 21}), std::make_pair("1923"sv, memgraph::utils::LocalTimeParameters{19, 23}), std::make_pair("19"sv, memgraph::utils::LocalTimeParameters{19}), std::make_pair("000000.000000"sv, memgraph::utils::LocalTimeParameters{0, 0, 0, 0, 0}), std::make_pair("010203.004005"sv, memgraph::utils::LocalTimeParameters{1, 2, 3, 4, 5}), }; } // namespace TEST(TemporalTest, DateParsing) { for (const auto &[string, date_parameters] : parsing_test_dates_extended) { ASSERT_EQ(memgraph::utils::ParseDateParameters(string).first, date_parameters); } for (const auto &[string, date_parameters] : parsing_test_dates_basic) { ASSERT_EQ(memgraph::utils::ParseDateParameters(string).first, date_parameters); } ASSERT_THROW(memgraph::utils::ParseDateParameters("202-011-22"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDateParameters("2020-1-022"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDateParameters("2020-11-2-"), memgraph::utils::BasicException); } TEST(TemporalTest, LocalTimeParsing) { for (const auto &[string, local_time_parameters] : parsing_test_local_time_extended) { ASSERT_EQ(memgraph::utils::ParseLocalTimeParameters(string).first, local_time_parameters) << ToString(local_time_parameters); const auto time_string = fmt::format("T{}", string); ASSERT_EQ(memgraph::utils::ParseLocalTimeParameters(time_string).first, local_time_parameters) << ToString(local_time_parameters); } for (const auto &[string, local_time_parameters] : parsing_test_local_time_basic) { ASSERT_EQ(memgraph::utils::ParseLocalTimeParameters(string).first, local_time_parameters) << ToString(local_time_parameters); ASSERT_EQ(memgraph::utils::ParseLocalTimeParameters(fmt::format("T{}", string)).first, local_time_parameters) << ToString(local_time_parameters); } ASSERT_THROW(memgraph::utils::ParseLocalTimeParameters("19:20:21s"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseLocalTimeParameters("1920:21"), memgraph::utils::BasicException); } TEST(TemporalTest, LocalDateTimeParsing) { const auto check_local_date_time_combinations = [](const auto &dates, const auto &local_times, const bool is_valid) { for (const auto &[date_string, date_parameters] : dates) { for (const auto &[local_time_string, local_time_parameters] : local_times) { const auto local_date_time_string = fmt::format("{}T{}", date_string, local_time_string); if (is_valid) { EXPECT_EQ(memgraph::utils::ParseLocalDateTimeParameters(local_date_time_string), (std::pair{date_parameters, local_time_parameters})); } } } }; check_local_date_time_combinations(parsing_test_dates_basic, parsing_test_local_time_basic, true); check_local_date_time_combinations(parsing_test_dates_extended, parsing_test_local_time_extended, true); check_local_date_time_combinations(parsing_test_dates_basic, parsing_test_local_time_extended, false); check_local_date_time_combinations(parsing_test_dates_extended, parsing_test_local_time_basic, false); } void CheckDurationParameters(const auto &values, const auto &expected) { ASSERT_EQ(values.day, expected.day); ASSERT_EQ(values.hour, expected.hour); ASSERT_EQ(values.minute, expected.minute); ASSERT_EQ(values.second, expected.second); ASSERT_EQ(values.millisecond, expected.millisecond); ASSERT_EQ(values.microsecond, expected.microsecond); } TEST(TemporalTest, DurationParsing) { ASSERT_THROW(memgraph::utils::ParseDurationParameters("P12Y"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("P12Y32DT2M"), memgraph::utils::BasicException); CheckDurationParameters(memgraph::utils::ParseDurationParameters("PT26H"), memgraph::utils::DurationParameters{.hour = 26}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("PT2M"), memgraph::utils::DurationParameters{.minute = 2.0}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("PT22S"), memgraph::utils::DurationParameters{.second = 22}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("PT.33S"), memgraph::utils::DurationParameters{.second = 0.33}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("PT2M3S"), memgraph::utils::DurationParameters{.minute = 2.0, .second = 3.0}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("PT2.5H"), memgraph::utils::DurationParameters{.hour = 2.5}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P2DT2.5H"), memgraph::utils::DurationParameters{.day = 2.0, .hour = 2.5}); ASSERT_THROW(memgraph::utils::ParseDurationParameters("P2M3S"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PTM3S"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("P2M3Y"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PT2Y3M"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("12Y32DT2M"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PY"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters(""), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PT2M3SX"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PT2M3S32"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PT2.5M3S"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PT2.5M3.5S"), memgraph::utils::BasicException); ASSERT_THROW(memgraph::utils::ParseDurationParameters("PT2.5M3.-5S"), memgraph::utils::BasicException); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P1256.5D"), memgraph::utils::DurationParameters{1256.5}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P1222DT2H"), memgraph::utils::DurationParameters{1222, 2}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P1222DT2H44M"), memgraph::utils::DurationParameters{1222, 2, 44}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P22DT1H9M20S"), memgraph::utils::DurationParameters{22, 1, 9, 20}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P22DT1H9M20.100S"), memgraph::utils::DurationParameters{ 22, 1, 9, 20.1, }); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P-22222222DT1H9M20.100S"), memgraph::utils::DurationParameters{-22222222, 1, 9, 20.1}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P-22222222DT-10H8M21.200S"), memgraph::utils::DurationParameters{-22222222, -10, 8, 21.2}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P-22222222DT-1H-7M22.300S"), memgraph::utils::DurationParameters{-22222222, -1, -7, 22.3}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P-22222222DT-1H-6M-20.100S"), memgraph::utils::DurationParameters{-22222222, -1, -6, -20.1}); CheckDurationParameters(memgraph::utils::ParseDurationParameters("P-22222222DT-1H-5M-20.100S"), memgraph::utils::DurationParameters{-22222222, -1, -5, -20.1}); } TEST(TemporalTest, PrintDate) { const auto unix_epoch = memgraph::utils::Date(memgraph::utils::DateParameters{1970, 1, 1}); std::ostringstream stream; stream << unix_epoch; ASSERT_TRUE(stream); ASSERT_EQ(stream.view(), "1970-01-01"); } TEST(TemporalTest, PrintLocalTime) { const auto lt = memgraph::utils::LocalTime({13, 2, 40, 100, 50}); std::ostringstream stream; stream << lt; ASSERT_TRUE(stream); ASSERT_EQ(stream.view(), "13:02:40.100050"); } TEST(TemporalTest, PrintDuration) { const auto dur = memgraph::utils::Duration({1, 0, 0, 0, 0, 0}); std::ostringstream stream; stream << dur; ASSERT_TRUE(stream); ASSERT_EQ(stream.view(), "P1DT0H0M0.000000S"); stream.str(""); stream.clear(); const auto complex_dur = memgraph::utils::Duration({10, 3, 30, 33, 100, 50}); stream << complex_dur; ASSERT_TRUE(stream); ASSERT_EQ(stream.view(), "P10DT3H30M33.100050S"); stream.str(""); stream.clear(); const auto negative_dur = memgraph::utils::Duration({-10, -3, -30, -33, -100, -50}); stream << negative_dur; ASSERT_TRUE(stream); ASSERT_EQ(stream.view(), "P-10DT-3H-30M-33.100050S"); } TEST(TemporalTest, PrintLocalDateTime) { const auto unix_epoch = memgraph::utils::Date(memgraph::utils::DateParameters{1970, 1, 1}); const auto lt = memgraph::utils::LocalTime({13, 2, 40, 100, 50}); memgraph::utils::LocalDateTime ldt(unix_epoch, lt); std::ostringstream stream; stream << ldt; ASSERT_TRUE(stream); ASSERT_EQ(stream.view(), "1970-01-01T13:02:40.100050"); } TEST(TemporalTest, DurationAddition) { // a >= 0 && b >= 0 const auto zero = memgraph::utils::Duration(0); const auto one = memgraph::utils::Duration(1); const auto two = one + one; const auto four = two + two; ASSERT_EQ(two.microseconds, 2); ASSERT_EQ(four.microseconds, 4); const auto max = memgraph::utils::Duration(std::numeric_limits::max()); ASSERT_THROW(max + one, memgraph::utils::BasicException); ASSERT_EQ(zero + zero, zero); ASSERT_EQ(max + zero, max); // a < 0 && b < 0 const auto neg_one = -one; const auto neg_two = neg_one + neg_one; const auto neg_four = neg_two + neg_two; ASSERT_EQ(neg_two.microseconds, -2); ASSERT_EQ(neg_four.microseconds, -4); const auto min = memgraph::utils::Duration(std::numeric_limits::min()); ASSERT_THROW(min + neg_one, memgraph::utils::BasicException); ASSERT_EQ(min + zero, min); // a < 0, b > 0 && a > 0 , b < 0 ASSERT_EQ(neg_one + one, zero); ASSERT_EQ(neg_two + one, neg_one); ASSERT_EQ(neg_two + four, two); ASSERT_EQ(four + neg_two, two); // a {min, max} && b {min, max} ASSERT_EQ(min + max, neg_one); ASSERT_EQ(max + min, neg_one); ASSERT_THROW(min + min, memgraph::utils::BasicException); ASSERT_THROW(max + max, memgraph::utils::BasicException); } TEST(TemporalTest, DurationSubtraction) { // a >= 0 && b >= 0 const auto one = memgraph::utils::Duration(1); const auto two = one + one; const auto zero = one - one; ASSERT_EQ(zero.microseconds, 0); const auto neg_one = zero - one; const auto neg_two = zero - two; ASSERT_EQ(neg_one.microseconds, -1); ASSERT_EQ(neg_two.microseconds, -2); const auto max = memgraph::utils::Duration(std::numeric_limits::max()); const auto min_minus_one = memgraph::utils::Duration(std::numeric_limits::min() + 1); ASSERT_EQ(max - zero, max); ASSERT_EQ(zero - max, min_minus_one); // a < 0 && b < 0 ASSERT_EQ(neg_two - neg_two, zero); const auto min = memgraph::utils::Duration(std::numeric_limits::min()); ASSERT_THROW(min - one, memgraph::utils::BasicException); ASSERT_EQ(min - zero, min); // a < 0, b > 0 && a > 0 , b < 0 ASSERT_EQ(neg_one - one, neg_two); ASSERT_EQ(one - neg_one, two); const auto neg_three = memgraph::utils::Duration(-3); const auto three = -neg_three; ASSERT_EQ(neg_two - one, neg_three); ASSERT_EQ(one - neg_two, three); // a {min, max} && b {min, max} ASSERT_THROW(min - max, memgraph::utils::BasicException); ASSERT_THROW(max - min, memgraph::utils::BasicException); // There is no positive representation of min ASSERT_THROW(min - min, memgraph::utils::BasicException); ASSERT_EQ(max - max, zero); } TEST(TemporalTest, LocalTimeAndDurationAddition) { const auto half_past_one = memgraph::utils::LocalTime({1, 30, 10}); const auto three = half_past_one + memgraph::utils::Duration({10, 1, 30, 2, 22, 45}); const auto three_symmetrical = memgraph::utils::Duration({10, 1, 30, 2, 22, 45}) + half_past_one; ASSERT_EQ(three, memgraph::utils::LocalTime({3, 0, 12, 22, 45})); ASSERT_EQ(three_symmetrical, memgraph::utils::LocalTime({3, 0, 12, 22, 45})); const auto half_an_hour_before_midnight = memgraph::utils::LocalTime({23, 30, 10}); { const auto half_past_midnight = half_an_hour_before_midnight + memgraph::utils::Duration({1, 1, 0, 0}); ASSERT_EQ(half_past_midnight, memgraph::utils::LocalTime({.minute = 30, .second = 10})); } const auto identity = half_an_hour_before_midnight + memgraph::utils::Duration({.day = 1}); ASSERT_EQ(identity, half_an_hour_before_midnight); ASSERT_EQ(identity, half_an_hour_before_midnight + memgraph::utils::Duration({.day = 1, .hour = 24})); const auto an_hour_and_a_half_before_midnight = memgraph::utils::LocalTime({22, 30, 10}); ASSERT_EQ(half_an_hour_before_midnight + memgraph::utils::Duration({.hour = 23}), an_hour_and_a_half_before_midnight); const auto minus_one_hour = memgraph::utils::Duration({-10, -1, 0, 0, -20, -20}); const auto minus_one_hour_exact = memgraph::utils::Duration({.day = -10, .hour = -1}); { const auto half_past_midnight = half_past_one + minus_one_hour; ASSERT_EQ(half_past_midnight, memgraph::utils::LocalTime({0, 30, 9, 979, 980})); ASSERT_EQ(half_past_midnight + minus_one_hour_exact, memgraph::utils::LocalTime({23, 30, 9, 979, 980})); const auto minus_two_hours_thirty_mins = memgraph::utils::Duration({-10, -2, -30, -9}); ASSERT_EQ(half_past_midnight + minus_two_hours_thirty_mins, memgraph::utils::LocalTime({22, 0, 0, 979, 980})); ASSERT_NO_THROW(half_past_midnight + (memgraph::utils::Duration(std::numeric_limits::max()))); ASSERT_EQ(half_past_midnight + (memgraph::utils::Duration(std::numeric_limits::max())), memgraph::utils::LocalTime({4, 31, 4, 755, 787})); ASSERT_NO_THROW(half_past_midnight + (memgraph::utils::Duration(std::numeric_limits::min()))); ASSERT_EQ(half_past_midnight + (memgraph::utils::Duration(std::numeric_limits::min())), memgraph::utils::LocalTime({20, 29, 15, 204, 172})); } } TEST(TemporalTest, LocalTimeAndDurationSubtraction) { const auto half_past_one = memgraph::utils::LocalTime({1, 30, 10}); const auto midnight = half_past_one - memgraph::utils::Duration({10, 1, 30, 10}); ASSERT_EQ(midnight, memgraph::utils::LocalTime()); ASSERT_EQ(midnight - memgraph::utils::Duration({-10, -1, -30, -10}), memgraph::utils::LocalTime({1, 30, 10})); const auto almost_an_hour_and_a_half_before_midnight = midnight - memgraph::utils::Duration({10, 1, 30, 1, 20, 20}); ASSERT_EQ(almost_an_hour_and_a_half_before_midnight, memgraph::utils::LocalTime({22, 29, 58, 979, 980})); ASSERT_NO_THROW(midnight - (memgraph::utils::Duration(std::numeric_limits::max()))); ASSERT_EQ(midnight - (memgraph::utils::Duration(std::numeric_limits::max())), memgraph::utils::LocalTime({19, 59, 5, 224, 193})); ASSERT_THROW(midnight - memgraph::utils::Duration(std::numeric_limits::min()), memgraph::utils::BasicException); } TEST(TemporalTest, LocalTimeDeltaDuration) { const auto half_past_one = memgraph::utils::LocalTime({1, 30, 10}); const auto half_past_two = memgraph::utils::LocalTime({2, 30, 10}); const auto an_hour_negative = half_past_one - half_past_two; ASSERT_EQ(an_hour_negative, memgraph::utils::Duration({.hour = -1})); const auto an_hour = half_past_two - half_past_one; ASSERT_EQ(an_hour, memgraph::utils::Duration({.hour = 1})); } TEST(TemporalTest, DateAddition) { const auto unix_epoch = memgraph::utils::Date({1970, 1, 1}); const auto one_day_after_unix_epoch = unix_epoch + memgraph::utils::Duration({.day = 1}); const auto one_day_after_unix_epoch_symmetrical = memgraph::utils::Duration({.day = 1}) + unix_epoch; ASSERT_EQ(one_day_after_unix_epoch, memgraph::utils::Date({1970, 1, 2})); ASSERT_EQ(one_day_after_unix_epoch_symmetrical, one_day_after_unix_epoch); const auto one_month_after_unix_epoch = unix_epoch + memgraph::utils::Duration({.day = 31}); ASSERT_EQ(one_month_after_unix_epoch, memgraph::utils::Date({1970, 2, 1})); const auto one_year_after_unix_epoch = unix_epoch + memgraph::utils::Duration({.day = 365}); ASSERT_EQ(one_year_after_unix_epoch, memgraph::utils::Date({1971, 1, 1})); const auto last_day_of_unix_epoch = one_year_after_unix_epoch + memgraph::utils::Duration({.day = -1}); ASSERT_EQ(last_day_of_unix_epoch, memgraph::utils::Date({1970, 12, 31})); const auto one_day_before_unix_epoch = unix_epoch + memgraph::utils::Duration({.day = -1}); ASSERT_EQ(one_day_before_unix_epoch, memgraph::utils::Date({1969, 12, 31})); ASSERT_EQ(last_day_of_unix_epoch + memgraph::utils::Duration({.day = -31}), memgraph::utils::Date({1970, 11, 30})); ASSERT_THROW(unix_epoch + memgraph::utils::Duration(std::numeric_limits::max()), memgraph::utils::BasicException); ASSERT_THROW(unix_epoch + memgraph::utils::Duration(std::numeric_limits::min()), memgraph::utils::BasicException); } TEST(TemporalTest, DateSubstraction) { const auto day_after_unix_epoch = memgraph::utils::Date({1970, 1, 2}); const auto unix_epoch = day_after_unix_epoch - memgraph::utils::Duration({.day = 1}); ASSERT_EQ(unix_epoch, memgraph::utils::Date({1970, 1, 1})); ASSERT_EQ(memgraph::utils::Date({1971, 1, 1}) - memgraph::utils::Duration({.day = 1}), memgraph::utils::Date({1970, 12, 31})); ASSERT_EQ(memgraph::utils::Date({1971, 1, 1}) - memgraph::utils::Duration({.day = -1}), memgraph::utils::Date({1971, 1, 2})); ASSERT_THROW(unix_epoch - memgraph::utils::Duration(std::numeric_limits::max()), memgraph::utils::BasicException); ASSERT_THROW(unix_epoch - memgraph::utils::Duration(std::numeric_limits::min()), memgraph::utils::BasicException); } TEST(TemporalTest, DateDelta) { const auto unix_epoch = memgraph::utils::Date({1970, 1, 1}); const auto one_year_after_unix_epoch = memgraph::utils::Date({1971, 1, 1}); ASSERT_EQ(one_year_after_unix_epoch - unix_epoch, memgraph::utils::Duration({.day = 365})); ASSERT_EQ(unix_epoch - one_year_after_unix_epoch, memgraph::utils::Duration({.day = -365})); } TEST(TemporalTest, LocalDateTimeAdditionSubtraction) { const auto unix_epoch = memgraph::utils::LocalDateTime({1970, 1, 1}, {.hour = 12}); auto one_day_after_unix_epoch = unix_epoch + memgraph::utils::Duration({.hour = 24}); auto one_day_after_unix_epoch_symmetrical = memgraph::utils::Duration({.hour = 24}) + unix_epoch; ASSERT_EQ(one_day_after_unix_epoch, memgraph::utils::LocalDateTime({1970, 1, 2}, {.hour = 12})); ASSERT_EQ(one_day_after_unix_epoch_symmetrical, one_day_after_unix_epoch); const auto one_day_before_unix_epoch = memgraph::utils::LocalDateTime({1969, 12, 31}, {23, 59, 59}); ASSERT_EQ(one_day_before_unix_epoch + memgraph::utils::Duration({.second = 1}), memgraph::utils::LocalDateTime({1970, 1, 1}, {})); one_day_after_unix_epoch = unix_epoch + memgraph::utils::Duration({.day = 1}); ASSERT_EQ(one_day_after_unix_epoch, memgraph::utils::LocalDateTime({1970, 1, 2}, {.hour = 12})); ASSERT_EQ(one_day_after_unix_epoch + memgraph::utils::Duration({.day = -1}), unix_epoch); ASSERT_EQ(one_day_after_unix_epoch - memgraph::utils::Duration({.day = 1}), unix_epoch); ASSERT_THROW(one_day_after_unix_epoch + memgraph::utils::Duration(std::numeric_limits::max()), memgraph::utils::BasicException); ASSERT_THROW(one_day_after_unix_epoch + memgraph::utils::Duration(std::numeric_limits::min()), memgraph::utils::BasicException); ASSERT_THROW(one_day_after_unix_epoch - memgraph::utils::Duration(std::numeric_limits::max()), memgraph::utils::BasicException); ASSERT_THROW(one_day_after_unix_epoch - memgraph::utils::Duration(std::numeric_limits::min()), memgraph::utils::BasicException); } TEST(TemporalTest, LocalDateTimeDelta) { const auto unix_epoch = memgraph::utils::LocalDateTime({1970, 1, 1}, {1, 1, 1}); const auto one_year_after_unix_epoch = memgraph::utils::LocalDateTime({1971, 2, 1}, {12, 1, 1}); const auto two_years_after_unix_epoch = memgraph::utils::LocalDateTime({1972, 2, 1}, {1, 1, 1, 20, 34}); ASSERT_EQ(one_year_after_unix_epoch - unix_epoch, memgraph::utils::Duration({.day = 396, .hour = 11})); ASSERT_EQ(unix_epoch - one_year_after_unix_epoch, memgraph::utils::Duration({.day = -396, .hour = -11})); ASSERT_EQ(two_years_after_unix_epoch - unix_epoch, memgraph::utils::Duration({.day = 761, .millisecond = 20, .microsecond = 34})); } TEST(TemporalTest, DateConvertsToString) { const auto date1 = memgraph::utils::Date({1970, 1, 2}); const std::string date1_expected_str = "1970-01-02"; const auto date2 = memgraph::utils::Date({0000, 1, 1}); const std::string date2_expected_str = "0000-01-01"; const auto date3 = memgraph::utils::Date({2022, 7, 4}); const std::string date3_expected_str = "2022-07-04"; ASSERT_EQ(date1_expected_str, date1.ToString()); ASSERT_EQ(date2_expected_str, date2.ToString()); ASSERT_EQ(date3_expected_str, date3.ToString()); } TEST(TemporalTest, LocalTimeConvertsToString) { const auto lt1 = memgraph::utils::LocalTime({13, 2, 40, 100, 50}); const std::string lt1_expected_str = "13:02:40.100050"; const auto lt2 = memgraph::utils::LocalTime({13, 2, 40}); const std::string lt2_expected_str = "13:02:40.000000"; const auto lt3 = memgraph::utils::LocalTime({0, 0, 0}); const std::string lt3_expected_str = "00:00:00.000000"; const auto lt4 = memgraph::utils::LocalTime({3, 2, 4, 6, 7}); const std::string lt4_expected_str = "03:02:04.006007"; ASSERT_EQ(lt1_expected_str, lt1.ToString()); ASSERT_EQ(lt2_expected_str, lt2.ToString()); ASSERT_EQ(lt3_expected_str, lt3.ToString()); ASSERT_EQ(lt4_expected_str, lt4.ToString()); } TEST(TemporalTest, LocalDateTimeConvertsToString) { const auto ldt1 = memgraph::utils::LocalDateTime({1970, 1, 2}, {23, 02, 59}); const std::string ldt1_expected_str = "1970-01-02T23:02:59.000000"; const auto ldt2 = memgraph::utils::LocalDateTime({1970, 1, 2}, {23, 02, 59, 456, 123}); const std::string ldt2_expected_str = "1970-01-02T23:02:59.456123"; const auto ldt3 = memgraph::utils::LocalDateTime({1997, 8, 24}, {16, 32, 9}); const std::string ldt3_expected_str = "1997-08-24T16:32:09.000000"; ASSERT_EQ(ldt1_expected_str, ldt1.ToString()); ASSERT_EQ(ldt2_expected_str, ldt2.ToString()); ASSERT_EQ(ldt3_expected_str, ldt3.ToString()); } TEST(TemporalTest, DurationConvertsToString) { memgraph::utils::Duration duration1{{.minute = 2, .second = 2, .microsecond = 33}}; const std::string duration1_expected_str = "P0DT0H2M2.000033S"; memgraph::utils::Duration duration2{{.hour = 2.5, .minute = 2, .second = 2, .microsecond = 33}}; const std::string duration2_expected_str = "P0DT2H32M2.000033S"; memgraph::utils::Duration duration3{{.hour = 1.25, .minute = 2, .second = 2}}; const std::string duration3_expected_str = "P0DT1H17M2.000000S"; memgraph::utils::Duration duration4{{.day = 20, .hour = 1.25, .minute = 2, .second = 2}}; const std::string duration4_expected_str = "P20DT1H17M2.000000S"; memgraph::utils::Duration duration5{{.hour = -3, .minute = 2, .second = 2, .microsecond = -33}}; const std::string duration5_expected_str = "P0DT-2H-57M-58.000033S"; memgraph::utils::Duration duration6{{.day = -2, .hour = 3, .minute = 2, .second = 2, .microsecond = 33}}; const std::string duration6_expected_str = "P-1DT-20H-57M-57.999967S"; memgraph::utils::Duration duration7{{.day = 20, .hour = 72, .minute = 154, .second = 312}}; const std::string duration7_expected_str = "P23DT2H39M12.000000S"; memgraph::utils::Duration duration8{{.day = 1, .hour = 23, .minute = 59, .second = 60}}; const std::string duration8_expected_str = "P2DT0H0M0.000000S"; ASSERT_EQ(duration1_expected_str, duration1.ToString()); ASSERT_EQ(duration2_expected_str, duration2.ToString()); ASSERT_EQ(duration3_expected_str, duration3.ToString()); ASSERT_EQ(duration4_expected_str, duration4.ToString()); ASSERT_EQ(duration5_expected_str, duration5.ToString()); ASSERT_EQ(duration6_expected_str, duration6.ToString()); ASSERT_EQ(duration7_expected_str, duration7.ToString()); ASSERT_EQ(duration8_expected_str, duration8.ToString()); }