diff --git a/src/communication/bolt/v1/encoder/base_encoder.hpp b/src/communication/bolt/v1/encoder/base_encoder.hpp index 9aaef0e0a..36ee1774e 100644 --- a/src/communication/bolt/v1/encoder/base_encoder.hpp +++ b/src/communication/bolt/v1/encoder/base_encoder.hpp @@ -192,8 +192,11 @@ class BaseEncoder { void WriteDuration(const utils::Duration &duration) { WriteRAW(utils::UnderlyingCast(Marker::TinyStruct4)); WriteRAW(utils::UnderlyingCast(Signature::Duration)); - WriteInt(duration.Months()); - WriteInt(duration.SubMonthsAsDays()); + // This shall always be zero because internally we store microseconds + // and converting months to microseconds is an approximation. However, + // for the encoder, we implement ReadInt() to support the neo4j driver. + WriteInt(0); + WriteInt(duration.Days()); WriteInt(duration.SubDaysAsSeconds()); WriteInt(duration.SubSecondsAsNanoseconds()); } diff --git a/src/query/dump.cpp b/src/query/dump.cpp index 3d18818c3..5cee51b49 100644 --- a/src/query/dump.cpp +++ b/src/query/dump.cpp @@ -61,22 +61,22 @@ void DumpPreciseDouble(std::ostream *os, double value) { namespace { void DumpDate(std::ostream &os, const storage::TemporalData &value) { utils::Date date(value.microseconds); - os << fmt::format("DATE(\"{}\")", date); + os << "DATE(\"" << date << "\")"; } void DumpLocalTime(std::ostream &os, const storage::TemporalData &value) { utils::LocalTime lt(value.microseconds); - os << fmt::format("LOCALTIME(\"{}\")", lt); + os << "LOCALTIME(\"" << lt << "\")"; } void DumpLocalDateTime(std::ostream &os, const storage::TemporalData &value) { utils::LocalDateTime ldt(value.microseconds); - os << fmt::format("LOCALDATETIME(\"{}\")", ldt); + os << "LOCALDATETIME(\"" << ldt << "\")"; } void DumpDuration(std::ostream &os, const storage::TemporalData &value) { utils::Duration dur(value.microseconds); - os << fmt::format("DURATION(\"{}\")", dur); + os << "DURATION(\"" << dur << "\")"; } void DumpTemporalData(std::ostream &os, const storage::TemporalData &value) { diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp index c512d5358..d49766b97 100644 --- a/src/query/interpret/awesome_memgraph_functions.cpp +++ b/src/query/interpret/awesome_memgraph_functions.cpp @@ -1154,9 +1154,7 @@ TypedValue Duration(const TypedValue *args, int64_t nargs, const FunctionContext utils::DurationParameters duration_parameters; using namespace std::literals; - std::unordered_map parameter_mappings{std::pair{"year"sv, &duration_parameters.years}, - std::pair{"month"sv, &duration_parameters.months}, - std::pair{"day"sv, &duration_parameters.days}, + std::unordered_map parameter_mappings{std::pair{"day"sv, &duration_parameters.days}, std::pair{"hour"sv, &duration_parameters.hours}, std::pair{"minute"sv, &duration_parameters.minutes}, std::pair{"second"sv, &duration_parameters.seconds}, diff --git a/src/query/interpret/eval.hpp b/src/query/interpret/eval.hpp index 836d6cb05..5a6f03e27 100644 --- a/src/query/interpret/eval.hpp +++ b/src/query/interpret/eval.hpp @@ -279,14 +279,8 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> { return std::nullopt; }; auto maybe_duration = [this](const auto &dur, const auto &prop_name) -> std::optional<TypedValue> { - if (prop_name == "years") { - return TypedValue(dur.Years(), ctx_->memory); - } - if (prop_name == "months") { - return TypedValue(dur.Months(), ctx_->memory); - } if (prop_name == "days") { - return TypedValue(dur.SubMonthsAsDays(), ctx_->memory); + return TypedValue(dur.Days(), ctx_->memory); } if (prop_name == "hours") { return TypedValue(dur.SubDaysAsHours(), ctx_->memory); diff --git a/src/utils/temporal.cpp b/src/utils/temporal.cpp index 6f2bc6be8..438596bfa 100644 --- a/src/utils/temporal.cpp +++ b/src/utils/temporal.cpp @@ -503,7 +503,7 @@ size_t LocalDateTimeHash::operator()(const LocalDateTime &local_date_time) const } namespace { -std::optional<DurationParameters> TryParseIsoDurationString(std::string_view string) { +std::optional<DurationParameters> TryParseDurationString(std::string_view string) { DurationParameters duration_parameters; if (string.empty()) { @@ -554,26 +554,7 @@ std::optional<DurationParameters> TryParseIsoDurationString(std::string_view str return true; }; - const auto parse_duration_date_part = [&](auto date_string) { - if (!std::isdigit(date_string.front())) { - return false; - throw utils::BasicException("Invalid format of duration string"); - } - - if (!parse_and_assign(date_string, 'Y', duration_parameters.years)) { - return false; - } - if (date_string.empty()) { - return true; - } - - if (!parse_and_assign(date_string, 'M', duration_parameters.months)) { - return false; - } - if (date_string.empty()) { - return true; - } - + const auto parse_duration_days_part = [&](auto date_string) { if (!parse_and_assign(date_string, 'D', duration_parameters.days)) { return false; } @@ -582,10 +563,6 @@ std::optional<DurationParameters> TryParseIsoDurationString(std::string_view str }; const auto parse_duration_time_part = [&](auto time_string) { - if (!std::isdigit(time_string.front())) { - return false; - } - if (!parse_and_assign(time_string, 'H', duration_parameters.hours)) { return false; } @@ -610,7 +587,7 @@ std::optional<DurationParameters> TryParseIsoDurationString(std::string_view str auto t_position = string.find('T'); const auto date_string = string.substr(0, t_position); - if (!date_string.empty() && !parse_duration_date_part(date_string)) { + if (!date_string.empty() && !parse_duration_days_part(date_string)) { return std::nullopt; } @@ -631,15 +608,11 @@ std::optional<DurationParameters> TryParseIsoDurationString(std::string_view str const auto kSupportedDurationFormatsHelpMessage = fmt::format(R"help( "String representing duration should be in the following format: -P[nY][nM][nD]T[nH][nM][nS] +P[nD]T[nH][nM][nS] Symbol table: |---|---------| -| Y | YEAR | -|---|---------| -| M | MONTH | -|---|---------| -| D | DAY | +| D | DAYS | |---|---------| | H | HOURS | |---|---------| @@ -650,46 +623,21 @@ Symbol table: 'n' represents a number that can be an integer of ANY value, or a fraction IF it's the last value in the string. All the fields are optional. - -Alternatively, the string can contain LocalDateTime format: -P<local_date_time_string> -{} )help", kSupportedLocalDateTimeFormatsHelpMessage); // clang-format on DurationParameters ParseDurationParameters(std::string_view string) { - // https://en.wikipedia.org/wiki/ISO_8601#Durations // The string needs to start with P followed by one of the two options: // - string in a duration specific format - // - string in a combined date and time format (LocalDateTime string format) if (string.empty() || string.front() != 'P') { throw utils::BasicException("Duration string is empty."); } - if (auto maybe_duration_parameters = TryParseIsoDurationString(string); maybe_duration_parameters) { + if (auto maybe_duration_parameters = TryParseDurationString(string); maybe_duration_parameters) { return *maybe_duration_parameters; } - DurationParameters duration_parameters; - // remove P and try to parse local date time - string.remove_prefix(1); - - try { - const auto [date_parameters, local_time_parameters] = ParseLocalDateTimeParameters(string); - - duration_parameters.years = static_cast<double>(date_parameters.years); - duration_parameters.months = static_cast<double>(date_parameters.months); - duration_parameters.days = static_cast<double>(date_parameters.days); - duration_parameters.hours = static_cast<double>(local_time_parameters.hours); - duration_parameters.minutes = static_cast<double>(local_time_parameters.minutes); - duration_parameters.seconds = static_cast<double>(local_time_parameters.seconds); - duration_parameters.milliseconds = static_cast<double>(local_time_parameters.milliseconds); - duration_parameters.microseconds = static_cast<double>(local_time_parameters.microseconds); - - return duration_parameters; - } catch (const utils::BasicException &e) { - throw utils::BasicException("Invalid duration string. {}", kSupportedDurationFormatsHelpMessage); - } + throw utils::BasicException("Invalid duration string. {}", kSupportedDurationFormatsHelpMessage); } namespace { @@ -702,9 +650,7 @@ constexpr To CastChronoDouble(const double value) { Duration::Duration(int64_t microseconds) { this->microseconds = microseconds; } Duration::Duration(const DurationParameters ¶meters) { - microseconds = (CastChronoDouble<std::chrono::years, std::chrono::microseconds>(parameters.years) + - CastChronoDouble<std::chrono::months, std::chrono::microseconds>(parameters.months) + - CastChronoDouble<std::chrono::days, std::chrono::microseconds>(parameters.days) + + microseconds = (CastChronoDouble<std::chrono::days, std::chrono::microseconds>(parameters.days) + CastChronoDouble<std::chrono::hours, std::chrono::microseconds>(parameters.hours) + CastChronoDouble<std::chrono::minutes, std::chrono::microseconds>(parameters.minutes) + CastChronoDouble<std::chrono::seconds, std::chrono::microseconds>(parameters.seconds) + @@ -713,34 +659,14 @@ Duration::Duration(const DurationParameters ¶meters) { .count(); } -int64_t Duration::Years() const { - std::chrono::microseconds ms(microseconds); - return std::chrono::duration_cast<std::chrono::years>(ms).count(); -} - -int64_t Duration::Months() const { - std::chrono::microseconds ms(microseconds); - return std::chrono::duration_cast<std::chrono::months>(ms).count(); -} - int64_t Duration::Days() const { std::chrono::microseconds ms(microseconds); return std::chrono::duration_cast<std::chrono::days>(ms).count(); } -int64_t Duration::SubMonthsAsDays() const { - namespace chrono = std::chrono; - const auto months = chrono::months(Months()); - const auto micros = chrono::microseconds(microseconds); - return chrono::duration_cast<chrono::days>(micros - months).count(); -} - int64_t Duration::SubDaysAsSeconds() const { namespace chrono = std::chrono; - const auto months = chrono::months(Months()); - const auto days = chrono::days(SubMonthsAsDays()); - const auto micros = chrono::microseconds(microseconds); - return chrono::duration_cast<chrono::seconds>(micros - months - days).count(); + return chrono::duration_cast<chrono::seconds>(chrono::microseconds(SubDaysAsMicroseconds())).count(); } int64_t Duration::SubDaysAsHours() const { @@ -755,10 +681,7 @@ int64_t Duration::SubDaysAsMinutes() const { int64_t Duration::SubDaysAsMilliseconds() const { namespace chrono = std::chrono; - const auto months = chrono::months(Months()); - const auto days = chrono::days(SubMonthsAsDays()); - const auto micros = chrono::microseconds(microseconds); - return chrono::duration_cast<chrono::milliseconds>(micros - months - days).count(); + return chrono::duration_cast<chrono::milliseconds>(chrono::microseconds(SubDaysAsMicroseconds())).count(); } int64_t Duration::SubDaysAsNanoseconds() const { @@ -768,19 +691,16 @@ int64_t Duration::SubDaysAsNanoseconds() const { int64_t Duration::SubDaysAsMicroseconds() const { namespace chrono = std::chrono; - const auto months = chrono::months(Months()); - const auto days = chrono::days(SubMonthsAsDays()); + const auto days = chrono::days(Days()); const auto micros = chrono::microseconds(microseconds); - return (micros - months - days).count(); + return (micros - days).count(); } int64_t Duration::SubSecondsAsNanoseconds() const { namespace chrono = std::chrono; - const auto months = chrono::months(Months()); - const auto days = chrono::days(SubMonthsAsDays()); + const auto micros = chrono::microseconds(SubDaysAsMicroseconds()); const auto secs = chrono::seconds(SubDaysAsSeconds()); - const auto micros = chrono::microseconds(microseconds); - return chrono::duration_cast<chrono::nanoseconds>(micros - months - days - secs).count(); + return chrono::duration_cast<chrono::nanoseconds>(micros - secs).count(); } Duration Duration::operator-() const { diff --git a/src/utils/temporal.hpp b/src/utils/temporal.hpp index 09c91eddb..082db6121 100644 --- a/src/utils/temporal.hpp +++ b/src/utils/temporal.hpp @@ -41,8 +41,6 @@ bool Underflows(const TType &lhs, const TType &rhs) { } struct DurationParameters { - double years{0}; - double months{0}; double days{0}; double hours{0}; double minutes{0}; @@ -63,10 +61,7 @@ struct Duration { auto operator<=>(const Duration &) const = default; - int64_t Years() const; - int64_t Months() const; int64_t Days() const; - int64_t SubMonthsAsDays() const; int64_t SubDaysAsSeconds() const; int64_t SubDaysAsHours() const; int64_t SubDaysAsMinutes() const; @@ -76,16 +71,14 @@ struct Duration { int64_t SubSecondsAsNanoseconds() const; friend std::ostream &operator<<(std::ostream &os, const Duration &dur) { - // ISO 8601 extended format: P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]. + // Format [DD]T[hh]:[mm]:[ss]. namespace chrono = std::chrono; auto micros = chrono::microseconds(dur.microseconds); - const auto y = GetAndSubtractDuration<chrono::years>(micros); - const auto mo = GetAndSubtractDuration<chrono::months>(micros); const auto dd = GetAndSubtractDuration<chrono::days>(micros); const auto h = GetAndSubtractDuration<chrono::hours>(micros); const auto m = GetAndSubtractDuration<chrono::minutes>(micros); const auto s = GetAndSubtractDuration<chrono::seconds>(micros); - return os << fmt::format("P{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}.{:0>6}", y, mo, dd, h, m, s, micros.count()); + return os << fmt::format("P{:0>9}DT{:0>2}H{:0>2}M{:0>2}.{:0>6}S", dd, h, m, s, micros.count()); } Duration operator-() const; diff --git a/tests/unit/bolt_decoder.cpp b/tests/unit/bolt_decoder.cpp index 01d0df08d..9d504f29c 100644 --- a/tests/unit/bolt_decoder.cpp +++ b/tests/unit/bolt_decoder.cpp @@ -505,8 +505,7 @@ TEST_F(BoltDecoder, DurationOneSec) { const auto value = Value(utils::Duration(1)); const auto &dur = value.ValueDuration(); const auto nanos = dur.SubSecondsAsNanoseconds(); - ASSERT_EQ(dur.Months(), 0); - ASSERT_EQ(dur.SubMonthsAsDays(), 0); + ASSERT_EQ(dur.Days(), 0); ASSERT_EQ(dur.SubDaysAsSeconds(), 0); ASSERT_EQ(nanos, 1000); const auto *n_bytes = std::bit_cast<const uint8_t *>(&nanos); @@ -535,8 +534,7 @@ TEST_F(BoltDecoder, DurationMinusOneSec) { const auto value = Value(utils::Duration(-1)); const auto &dur = value.ValueDuration(); const auto nanos = dur.SubSecondsAsNanoseconds(); - ASSERT_EQ(dur.Months(), 0); - ASSERT_EQ(dur.SubMonthsAsDays(), 0); + ASSERT_EQ(dur.Days(), 0); ASSERT_EQ(dur.SubDaysAsSeconds(), 0); ASSERT_EQ(nanos, -1000); const auto *n_bytes = std::bit_cast<const uint8_t *>(&nanos); @@ -562,15 +560,14 @@ TEST_F(BoltDecoder, ArbitraryDuration) { TestDecoderBuffer buffer; DecoderT decoder(buffer); Value dv; - const auto value = Value(utils::Duration({1, 1, 1, 1, 1, 1, 1, 0})); + const auto value = Value(utils::Duration({15, 1, 2, 3, 5, 0})); const auto &dur = value.ValueDuration(); - ASSERT_EQ(dur.Months(), 13); - ASSERT_EQ(dur.SubMonthsAsDays(), 1); + ASSERT_EQ(dur.Days(), 15); const auto secs = dur.SubDaysAsSeconds(); - ASSERT_EQ(secs, 3661); + ASSERT_EQ(secs, 3723); const auto *sec_bytes = std::bit_cast<const uint8_t *>(&secs); const auto nanos = dur.SubSecondsAsNanoseconds(); - ASSERT_EQ(nanos, 1000000); + ASSERT_EQ(nanos, 5000000); const auto *nano_bytes = std::bit_cast<const uint8_t *>(&nanos); using Marker = communication::bolt::Marker; using Sig = communication::bolt::Signature; @@ -578,8 +575,8 @@ TEST_F(BoltDecoder, ArbitraryDuration) { std::array<uint8_t, 12> data = { Cast(Marker::TinyStruct4), Cast(Sig::Duration), - 0xD, - 0x1, + 0x0, + 0xF, Cast(Marker::Int16), sec_bytes[1], sec_bytes[0], diff --git a/tests/unit/bolt_encoder.cpp b/tests/unit/bolt_encoder.cpp index c7e13b591..9409072a6 100644 --- a/tests/unit/bolt_encoder.cpp +++ b/tests/unit/bolt_encoder.cpp @@ -323,8 +323,7 @@ TEST_F(BoltEncoder, DurationOneSec) { vals.push_back(value); ASSERT_EQ(bolt_encoder.MessageRecord(vals), true); const auto &dur = value.ValueDuration(); - ASSERT_EQ(dur.Months(), 0); - ASSERT_EQ(dur.SubMonthsAsDays(), 0); + ASSERT_EQ(dur.Days(), 0); ASSERT_EQ(dur.SubDaysAsSeconds(), 0); const auto nanos = dur.SubSecondsAsNanoseconds(); ASSERT_EQ(nanos, 1000); @@ -357,8 +356,7 @@ TEST_F(BoltEncoder, DurationMinusOneSec) { vals.push_back(value); ASSERT_EQ(bolt_encoder.MessageRecord(vals), true); const auto &dur = value.ValueDuration(); - ASSERT_EQ(dur.Months(), 0); - ASSERT_EQ(dur.SubMonthsAsDays(), 0); + ASSERT_EQ(dur.Days(), 0); ASSERT_EQ(dur.SubDaysAsSeconds(), 0); const auto nanos = dur.SubSecondsAsNanoseconds(); const auto *d_bytes = std::bit_cast<const uint8_t *>(&nanos); @@ -387,17 +385,17 @@ TEST_F(BoltEncoder, DurationMinusOneSec) { TEST_F(BoltEncoder, ArbitraryDuration) { output.clear(); std::vector<Value> vals; - const auto value = Value(utils::Duration({1, 1, 1, 1, 1, 1, 1, 0})); + const auto value = Value(utils::Duration({15, 1, 2, 3, 5, 0})); vals.push_back(value); ASSERT_EQ(bolt_encoder.MessageRecord(vals), true); const auto &dur = value.ValueDuration(); - ASSERT_EQ(dur.Months(), 13); - ASSERT_EQ(dur.SubMonthsAsDays(), 1); + // This is an approximation because of chrono::months to days conversion + ASSERT_EQ(dur.Days(), 15); const auto secs = dur.SubDaysAsSeconds(); - ASSERT_EQ(secs, 3661); + ASSERT_EQ(secs, 3723); const auto *sec_bytes = std::bit_cast<const uint8_t *>(&secs); const auto nanos = dur.SubSecondsAsNanoseconds(); - ASSERT_EQ(nanos, 1000000); + ASSERT_EQ(nanos, 5000000); const auto *nano_bytes = std::bit_cast<const uint8_t *>(&nanos); // 0x91 denotes the size of vals (it's 0x91 because it's anded -- see // WriteTypeSize in base_encoder.hpp). @@ -410,8 +408,8 @@ TEST_F(BoltEncoder, ArbitraryDuration) { 0x91, Cast(Marker::TinyStruct4), Cast(Sig::Duration), - 0xD, - 0x1, + 0x0, + 0xF, Cast(Marker::Int16), sec_bytes[1], sec_bytes[0], diff --git a/tests/unit/query_dump.cpp b/tests/unit/query_dump.cpp index 34e0b39c6..3821e1c7e 100644 --- a/tests/unit/query_dump.cpp +++ b/tests/unit/query_dump.cpp @@ -396,8 +396,8 @@ TEST(DumpTest, PropertyValue) { auto ldt = storage::PropertyValue( storage::TemporalData(storage::TemporalType::LocalDateTime, utils::LocalDateTime({1994, 12, 7}, {14, 10, 44, 99, 99}).MicrosecondsSinceEpoch())); - auto dur = storage::PropertyValue(storage::TemporalData(storage::TemporalType::Duration, - utils::Duration({1, 2, 3, 4, 5, 6, 10, 11}).microseconds)); + auto dur = storage::PropertyValue( + storage::TemporalData(storage::TemporalType::Duration, utils::Duration({3, 4, 5, 6, 10, 11}).microseconds)); auto list_value = storage::PropertyValue({map_value, null_value, double_value, dt, lt, ldt, dur}); CreateVertex(&dba, {}, {{"p1", list_value}, {"p2", str_value}}, false); ASSERT_FALSE(dba.Commit().HasError()); @@ -415,7 +415,7 @@ TEST(DumpTest, PropertyValue) { "CREATE (:__mg_vertex__ {__mg_id__: 0, `p1`: [{`prop 1`: 13, " "`prop``2```: true}, Null, -1.2, DATE(\"1994-12-07\"), " "LOCALTIME(\"14:10:44.099099\"), LOCALDATETIME(\"1994-12-07T14:10:44.099099\"), " - "DURATION(\"P0001-02-03T04:05:06.010011\")" + "DURATION(\"P000000003DT04H05M06.010011S\")" "], `p2`: \"hello \\'world\\'\"});", kDropInternalIndex, kRemoveInternalLabelProperty); } @@ -658,10 +658,9 @@ TEST(DumpTest, CheckStateSimpleGraph) { {{"time", storage::PropertyValue(storage::TemporalData( storage::TemporalType::LocalDateTime, utils::LocalDateTime({1994, 12, 7}, {14, 10, 44, 99, 99}).MicrosecondsSinceEpoch()))}}); - CreateEdge( - &dba, &w, &z, "Duration", - {{"time", storage::PropertyValue(storage::TemporalData( - storage::TemporalType::Duration, utils::Duration({1, 2, 3, 4, 5, 6, 10, 11}).microseconds))}}); + CreateEdge(&dba, &w, &z, "Duration", + {{"time", storage::PropertyValue(storage::TemporalData( + storage::TemporalType::Duration, utils::Duration({3, 4, 5, 6, 10, 11}).microseconds))}}); ASSERT_FALSE(dba.Commit().HasError()); } { diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp index 451f9ddad..9968ffb03 100644 --- a/tests/unit/query_expression_evaluator.cpp +++ b/tests/unit/query_expression_evaluator.cpp @@ -982,18 +982,8 @@ TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) { } TEST_F(ExpressionEvaluatorPropertyLookup, Duration) { - const utils::Duration dur({1994, 2, 10, 1, 30, 2, 22, 45}); + const utils::Duration dur({10, 1, 30, 2, 22, 45}); frame[symbol] = TypedValue(dur); - const std::pair years = std::make_pair("years", dba.NameToProperty("years")); - - const auto total_years = Value(years); - EXPECT_TRUE(total_years.IsInt()); - EXPECT_EQ(total_years.ValueInt(), 1994); - - const std::pair months = std::make_pair("months", dba.NameToProperty("months")); - const auto total_months = Value(months); - EXPECT_TRUE(total_months.IsInt()); - EXPECT_EQ(total_months.ValueInt(), 1994 * 12 + 2); const std::pair days = std::make_pair("days", dba.NameToProperty("days")); const auto total_days = Value(days); @@ -1980,19 +1970,14 @@ TEST_F(FunctionTest, LocalDateTime) { } TEST_F(FunctionTest, Duration) { - constexpr size_t microseconds_in_a_year = 31556952000000; - const auto dur = utils::Duration(microseconds_in_a_year); - EXPECT_EQ(EvaluateFunction("DURATION", "P1Y").ValueDuration(), dur); - const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"year", TypedValue(1972)}, - {"month", TypedValue(2)}, - {"day", TypedValue(3)}, + const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"day", TypedValue(3)}, {"hour", TypedValue(4)}, {"minute", TypedValue(5)}, {"second", TypedValue(6)}, {"millisecond", TypedValue(7)}, {"microsecond", TypedValue(8)}}); - EXPECT_EQ(EvaluateFunction("DURATION", map_param).ValueDuration(), utils::Duration({1972, 2, 3, 4, 5, 6, 7, 8})); + EXPECT_EQ(EvaluateFunction("DURATION", map_param).ValueDuration(), utils::Duration({3, 4, 5, 6, 7, 8})); EXPECT_THROW(EvaluateFunction("DURATION", "{}"), utils::BasicException); EXPECT_THROW(EvaluateFunction("DURATION", TypedValue(std::map<std::string, TypedValue>{{"hours", TypedValue(1970)}})), QueryRuntimeException); diff --git a/tests/unit/query_pretty_print.cpp b/tests/unit/query_pretty_print.cpp index 59d6511d1..f16131675 100644 --- a/tests/unit/query_pretty_print.cpp +++ b/tests/unit/query_pretty_print.cpp @@ -54,7 +54,7 @@ TEST_F(ExpressionPrettyPrinterTest, Literals) { storage::PropertyValue(storage::TemporalData(storage::TemporalType::LocalDateTime, 3)), storage::PropertyValue(storage::TemporalData(storage::TemporalType::Date, 4))}; EXPECT_EQ(ToString(LITERAL(storage::PropertyValue(tt_vec))), - "[DURATION(\"P0000-00-00T00:00:00.000001\"), LOCALTIME(\"00:00:00.000002\"), " + "[DURATION(\"P000000000DT00H00M00.000001S\"), LOCALTIME(\"00:00:00.000002\"), " "LOCALDATETIME(\"1970-01-01T00:00:00.000003\"), DATE(\"1970-01-01\")]"); } diff --git a/tests/unit/utils_temporal.cpp b/tests/unit/utils_temporal.cpp index 8d53ed879..81a2acf44 100644 --- a/tests/unit/utils_temporal.cpp +++ b/tests/unit/utils_temporal.cpp @@ -7,6 +7,7 @@ #include <gtest/gtest.h> #include "utils/exceptions.hpp" +#include "utils/memory.hpp" #include "utils/temporal.hpp" namespace { @@ -148,20 +149,6 @@ TEST(TemporalTest, LocalDateTimeMicrosecondsSinceEpochConversion) { } TEST(TemporalTest, DurationConversion) { - { - utils::Duration duration{{.years = 2.5}}; - const auto microseconds = duration.microseconds; - utils::LocalDateTime local_date_time{microseconds}; - ASSERT_EQ(local_date_time.date.years, 2 + 1970); - ASSERT_EQ(local_date_time.date.months, 6 + 1); - }; - { - utils::Duration duration{{.months = 26}}; - const auto microseconds = duration.microseconds; - utils::LocalDateTime local_date_time{microseconds}; - ASSERT_EQ(local_date_time.date.years, 2 + 1970); - ASSERT_EQ(local_date_time.date.months, 2 + 1); - }; { utils::Duration duration{{.minutes = 123.25}}; const auto microseconds = duration.microseconds; @@ -255,19 +242,24 @@ TEST(TemporalTest, LocalDateTimeParsing) { } void CheckDurationParameters(const auto &values, const auto &expected) { - ASSERT_NEAR(values.years, expected.years, 0.01); - ASSERT_NEAR(values.months, expected.months, 0.01); - ASSERT_NEAR(values.days, expected.days, 0.01); - ASSERT_NEAR(values.hours, expected.hours, 0.01); - ASSERT_NEAR(values.minutes, expected.minutes, 0.01); - ASSERT_NEAR(values.seconds, expected.seconds, 0.01); + ASSERT_EQ(values.days, expected.days); + ASSERT_EQ(values.hours, expected.hours); + ASSERT_EQ(values.minutes, expected.minutes); + ASSERT_EQ(values.seconds, expected.seconds); + ASSERT_EQ(values.milliseconds, expected.milliseconds); + ASSERT_EQ(values.microseconds, expected.microseconds); } TEST(TemporalTest, DurationParsing) { - CheckDurationParameters(utils::ParseDurationParameters("P12Y"), utils::DurationParameters{.years = 12.0}); - CheckDurationParameters(utils::ParseDurationParameters("P12Y32DT2M"), - utils::DurationParameters{.years = 12.0, .days = 32.0, .minutes = 2.0}); + ASSERT_THROW(utils::ParseDurationParameters("P12Y"), utils::BasicException); + ASSERT_THROW(utils::ParseDurationParameters("P12Y32DT2M"), utils::BasicException); + + CheckDurationParameters(utils::ParseDurationParameters("PT26H"), utils::DurationParameters{.hours = 26}); CheckDurationParameters(utils::ParseDurationParameters("PT2M"), utils::DurationParameters{.minutes = 2.0}); + CheckDurationParameters(utils::ParseDurationParameters("PT22S"), utils::DurationParameters{.seconds = 22}); + + CheckDurationParameters(utils::ParseDurationParameters("PT.33S"), utils::DurationParameters{.seconds = 0.33}); + CheckDurationParameters(utils::ParseDurationParameters("PT2M3S"), utils::DurationParameters{.minutes = 2.0, .seconds = 3.0}); CheckDurationParameters(utils::ParseDurationParameters("PT2.5H"), utils::DurationParameters{.hours = 2.5}); @@ -285,13 +277,14 @@ TEST(TemporalTest, DurationParsing) { ASSERT_THROW(utils::ParseDurationParameters("PT2M3S32"), utils::BasicException); ASSERT_THROW(utils::ParseDurationParameters("PT2.5M3S"), utils::BasicException); ASSERT_THROW(utils::ParseDurationParameters("PT2.5M3.5S"), utils::BasicException); - - CheckDurationParameters(utils::ParseDurationParameters("P20201122T192032"), - utils::DurationParameters{2020, 11, 22, 19, 20, 32}); - CheckDurationParameters(utils::ParseDurationParameters("P20201122T192032.333"), - utils::DurationParameters{2020, 11, 22, 19, 20, 32, 333}); - CheckDurationParameters(utils::ParseDurationParameters("P2020-11-22T19:20:32"), - utils::DurationParameters{2020, 11, 22, 19, 20, 32}); + CheckDurationParameters(utils::ParseDurationParameters("P1256.5D"), utils::DurationParameters{1256.5}); + CheckDurationParameters(utils::ParseDurationParameters("P1222DT2H"), utils::DurationParameters{1222, 2}); + CheckDurationParameters(utils::ParseDurationParameters("P1222DT2H44M"), utils::DurationParameters{1222, 2, 44}); + CheckDurationParameters(utils::ParseDurationParameters("P22DT1H9M20S"), utils::DurationParameters{22, 1, 9, 20}); + CheckDurationParameters(utils::ParseDurationParameters("P22DT1H9M20.100S"), + utils::DurationParameters{22, 1, 9, 20.100, 0, 0}); + CheckDurationParameters(utils::ParseDurationParameters("P22DT1H9M20.1000S"), + utils::DurationParameters{22, 1, 9, 20.100, 0, 0}); } TEST(TemporalTest, PrintDate) { @@ -311,17 +304,17 @@ TEST(TemporalTest, PrintLocalTime) { } TEST(TemporalTest, PrintDuration) { - const auto dur = utils::Duration({1, 0, 0, 0, 0, 0, 0, 0}); + const auto dur = utils::Duration({1, 0, 0, 0, 0, 0}); std::ostringstream stream; stream << dur; ASSERT_TRUE(stream); - ASSERT_EQ(stream.view(), "P0001-00-00T00:00:00.000000"); + ASSERT_EQ(stream.view(), "P000000001DT00H00M00.000000S"); stream.str(""); stream.clear(); - const auto complex_dur = utils::Duration({1, 5, 10, 3, 30, 33, 100, 50}); + const auto complex_dur = utils::Duration({10, 3, 30, 33, 100, 50}); stream << complex_dur; ASSERT_TRUE(stream); - ASSERT_EQ(stream.view(), "P0001-05-10T03:30:33.100050"); + ASSERT_EQ(stream.view(), "P000000010DT03H30M33.100050S"); /// stream.str(""); /// stream.clear(); /// TODO (kostasrim) @@ -420,14 +413,14 @@ TEST(TemporalTest, DurationSubtraction) { TEST(TemporalTest, LocalTimeAndDurationAddition) { const auto half_past_one = utils::LocalTime({1, 30, 10}); - const auto three = half_past_one + utils::Duration({1994, 2, 10, 1, 30, 2, 22, 45}); - const auto three_symmetrical = utils::Duration({1994, 2, 10, 1, 30, 2, 22, 45}) + half_past_one; + const auto three = half_past_one + utils::Duration({10, 1, 30, 2, 22, 45}); + const auto three_symmetrical = utils::Duration({10, 1, 30, 2, 22, 45}) + half_past_one; ASSERT_EQ(three, utils::LocalTime({3, 0, 12, 22, 45})); ASSERT_EQ(three_symmetrical, utils::LocalTime({3, 0, 12, 22, 45})); const auto half_an_hour_before_midnight = utils::LocalTime({23, 30, 10}); { - const auto half_past_midnight = half_an_hour_before_midnight + utils::Duration({1994, 1, 10, 1}); + const auto half_past_midnight = half_an_hour_before_midnight + utils::Duration({1, 1, 0, 0}); ASSERT_EQ(half_past_midnight, utils::LocalTime({.minutes = 30, .seconds = 10})); } const auto identity = half_an_hour_before_midnight + utils::Duration({.days = 1}); @@ -436,36 +429,36 @@ TEST(TemporalTest, LocalTimeAndDurationAddition) { const auto an_hour_and_a_half_before_midnight = utils::LocalTime({22, 30, 10}); ASSERT_EQ(half_an_hour_before_midnight + utils::Duration({.hours = 23}), an_hour_and_a_half_before_midnight); - const auto minus_one_hour = utils::Duration({-1994, -2, -10, -1, 0, 0, -20, -20}); - const auto minus_one_hour_exact = utils::Duration({-1994, -2, -10, -1}); + const auto minus_one_hour = utils::Duration({-10, -1, 0, 0, -20, -20}); + const auto minus_one_hour_exact = utils::Duration({.days = -10, .hours = -1}); { const auto half_past_midnight = half_past_one + minus_one_hour; ASSERT_EQ(half_past_midnight, utils::LocalTime({0, 30, 9, 979, 980})); ASSERT_EQ(half_past_midnight + minus_one_hour_exact, utils::LocalTime({23, 30, 9, 979, 980})); - const auto minus_two_hours_thirty_mins = utils::Duration({-1994, -2, -10, -2, -30, -9}); + const auto minus_two_hours_thirty_mins = utils::Duration({-10, -2, -30, -9}); ASSERT_EQ(half_past_midnight + minus_two_hours_thirty_mins, utils::LocalTime({22, 0, 0, 979, 980})); ASSERT_NO_THROW(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::max()))); ASSERT_EQ(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::max())), - utils::LocalTime({0, 22, 40, 755, 787})); + utils::LocalTime({4, 31, 4, 755, 787})); ASSERT_NO_THROW(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::min()))); ASSERT_EQ(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::min())), - utils::LocalTime({0, 37, 39, 204, 172})); + utils::LocalTime({20, 29, 15, 204, 172})); } } TEST(TemporalTest, LocalTimeAndDurationSubtraction) { const auto half_past_one = utils::LocalTime({1, 30, 10}); - const auto midnight = half_past_one - utils::Duration({1994, 2, 10, 1, 30, 10}); + const auto midnight = half_past_one - utils::Duration({10, 1, 30, 10}); ASSERT_EQ(midnight, utils::LocalTime()); - ASSERT_EQ(midnight - utils::Duration({-1994, -2, -10, -1, -30, -10}), utils::LocalTime({1, 30, 10})); + ASSERT_EQ(midnight - utils::Duration({-10, -1, -30, -10}), utils::LocalTime({1, 30, 10})); - const auto almost_an_hour_and_a_half_before_midnight = midnight - utils::Duration({1994, 2, 10, 1, 30, 1, 20, 20}); + const auto almost_an_hour_and_a_half_before_midnight = midnight - utils::Duration({10, 1, 30, 1, 20, 20}); ASSERT_EQ(almost_an_hour_and_a_half_before_midnight, utils::LocalTime({22, 29, 58, 979, 980})); ASSERT_NO_THROW(midnight - (utils::Duration(std::numeric_limits<int64_t>::max()))); - ASSERT_EQ(midnight - (utils::Duration(std::numeric_limits<int64_t>::max())), utils::LocalTime({0, 7, 29, 224, 193})); + ASSERT_EQ(midnight - (utils::Duration(std::numeric_limits<int64_t>::max())), utils::LocalTime({19, 59, 5, 224, 193})); ASSERT_THROW(midnight - utils::Duration(std::numeric_limits<int64_t>::min()), utils::BasicException); }