Remove support for iso duration and fix bolt (#235)

This commit is contained in:
Kostas Kyrimis 2021-09-17 17:40:57 +03:00 committed by Antonio Andelic
parent da68f86fc9
commit 8ea281649a
12 changed files with 93 additions and 213 deletions

View File

@ -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());
}

View File

@ -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) {

View File

@ -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},

View File

@ -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);

View File

@ -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 &parameters) {
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 &parameters) {
.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 {

View File

@ -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;

View File

@ -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],

View File

@ -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],

View File

@ -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());
}
{

View File

@ -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);

View File

@ -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\")]");
}

View File

@ -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);
}