Add temporal types to TypedValue (#176)
This commit is contained in:
parent
5da32c1bff
commit
e628b5ba6e
@ -46,7 +46,7 @@ inline nlohmann::json PropertyValueToJson(const storage::PropertyValue &pv) {
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
ret = nlohmann::json::object();
|
||||
const auto temporal_data = pv.ValueTemporalData();
|
||||
// TODO (antonio2368): Maybe we want to have custom format for each type
|
||||
// TODO(antonio2368): Maybe we want to have custom format for each type
|
||||
ret.emplace("type", storage::TemporalTypeTostring(temporal_data.type));
|
||||
ret.emplace("microseconds", temporal_data.microseconds);
|
||||
break;
|
||||
|
@ -99,6 +99,13 @@ storage::Result<Value> ToBoltValue(const query::TypedValue &value, const storage
|
||||
if (maybe_path.HasError()) return maybe_path.GetError();
|
||||
return Value(std::move(*maybe_path));
|
||||
}
|
||||
case query::TypedValue::Type::Date:
|
||||
case query::TypedValue::Type::LocalTime:
|
||||
case query::TypedValue::Type::LocalDateTime:
|
||||
case query::TypedValue::Type::Duration:
|
||||
// TODO(antonio2368): Change this when Bolt value for temporal types
|
||||
// are implemented
|
||||
LOG_FATAL("Temporal types not yet supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ set(mg_query_sources
|
||||
procedure/py_module.cpp
|
||||
serialization/property_value.cpp
|
||||
streams.cpp
|
||||
temporal.cpp
|
||||
trigger.cpp
|
||||
trigger_context.cpp
|
||||
typed_value.cpp)
|
||||
|
@ -142,7 +142,7 @@ void PrintObject(std::ostream *out, const storage::PropertyValue &value) {
|
||||
PrintObject(out, value.ValueMap());
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// TODO (antonio2368): Print out the temporal data based on the type
|
||||
// TODO(antonio2368): Print out the temporal data based on the type
|
||||
LOG_FATAL("Not implemented!");
|
||||
}
|
||||
}
|
||||
|
@ -542,6 +542,14 @@ TypedValue ValueType(const TypedValue *args, int64_t nargs, const FunctionContex
|
||||
return TypedValue("RELATIONSHIP", ctx.memory);
|
||||
case TypedValue::Type::Path:
|
||||
return TypedValue("PATH", ctx.memory);
|
||||
case TypedValue::Type::Date:
|
||||
return TypedValue("DATE", ctx.memory);
|
||||
case TypedValue::Type::LocalTime:
|
||||
return TypedValue("LOCAL_TIME", ctx.memory);
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
return TypedValue("LOCAL_DATE_TIME", ctx.memory);
|
||||
case TypedValue::Type::Duration:
|
||||
return TypedValue("DURATION", ctx.memory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,6 +262,9 @@ mgp_value_type FromTypedValueType(query::TypedValue::Type type) {
|
||||
return MGP_VALUE_TYPE_EDGE;
|
||||
case query::TypedValue::Type::Path:
|
||||
return MGP_VALUE_TYPE_PATH;
|
||||
default:
|
||||
// TODO(antonio2368): Implement this when we add mgp temporal types
|
||||
LOG_FATAL("Unsupported value in the procedures");
|
||||
}
|
||||
}
|
||||
|
||||
@ -478,7 +481,7 @@ mgp_value::mgp_value(const storage::PropertyValue &pv, utils::MemoryResource *m)
|
||||
break;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
// TODO (antonio2368): Add support for temporala data types
|
||||
// TODO(antonio2368): Add support for temporala data types
|
||||
LOG_FATAL("Unsupported type");
|
||||
}
|
||||
}
|
||||
@ -1942,6 +1945,12 @@ std::ostream &PrintValue(const TypedValue &value, std::ostream *stream) {
|
||||
case TypedValue::Type::Edge:
|
||||
case TypedValue::Type::Path:
|
||||
LOG_FATAL("value must not be a graph element");
|
||||
case TypedValue::Type::Date:
|
||||
case TypedValue::Type::LocalTime:
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
case TypedValue::Type::Duration:
|
||||
// TODO(antonio2368): Check how to print out nicely temporal types
|
||||
LOG_FATAL("Temporal types not imlemented yet");
|
||||
}
|
||||
}
|
||||
|
||||
|
198
src/query/temporal.cpp
Normal file
198
src/query/temporal.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
#include "query/temporal.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/fnv.hpp"
|
||||
|
||||
namespace query {
|
||||
namespace {
|
||||
template <typename T>
|
||||
concept Chrono = requires(T) {
|
||||
typename T::rep;
|
||||
typename T::period;
|
||||
};
|
||||
|
||||
template <Chrono TFirst, Chrono TSecond>
|
||||
constexpr auto GetAndSubtractDuration(TSecond &base_duration) {
|
||||
const auto duration = std::chrono::duration_cast<TFirst>(base_duration);
|
||||
base_duration -= duration;
|
||||
return duration.count();
|
||||
}
|
||||
|
||||
constexpr std::chrono::microseconds epoch{std::chrono::years{1970} + std::chrono::months{1} + std::chrono::days{1}};
|
||||
|
||||
constexpr bool IsInBounds(const auto low, const auto high, const auto value) { return low <= value && value <= high; }
|
||||
|
||||
constexpr bool IsValidDay(const auto day, const auto month, const auto year) {
|
||||
constexpr std::array<uint8_t, 12> days{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
MG_ASSERT(IsInBounds(1, 12, month), "Invalid month!");
|
||||
|
||||
if (day <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto is_leap_year = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||
|
||||
uint8_t leap_day = month == 2 && is_leap_year;
|
||||
|
||||
return day <= (days[month - 1] + leap_day);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Date::Date(const int64_t microseconds) {
|
||||
auto chrono_microseconds = std::chrono::microseconds(microseconds);
|
||||
chrono_microseconds += epoch;
|
||||
MG_ASSERT(chrono_microseconds.count() >= 0, "Invalid Date specified in microseconds");
|
||||
years = GetAndSubtractDuration<std::chrono::years>(chrono_microseconds);
|
||||
months = GetAndSubtractDuration<std::chrono::months>(chrono_microseconds);
|
||||
days = GetAndSubtractDuration<std::chrono::days>(chrono_microseconds);
|
||||
}
|
||||
|
||||
Date::Date(const DateParameters &date_parameters) {
|
||||
if (!IsInBounds(0, 9999, date_parameters.years)) {
|
||||
throw utils::BasicException("Creating a Date with invalid year parameter.");
|
||||
}
|
||||
|
||||
// TODO(antonio2368): Replace with year_month_day when it's implemented
|
||||
// https://en.cppreference.com/w/cpp/chrono/year_month_day/ok
|
||||
if (!IsInBounds(1, 12, date_parameters.months)) {
|
||||
throw utils::BasicException("Creating a Date with invalid month parameter.");
|
||||
}
|
||||
|
||||
if (!IsValidDay(date_parameters.days, date_parameters.months, date_parameters.years)) {
|
||||
throw utils::BasicException("Creating a Date with invalid day parameter.");
|
||||
}
|
||||
|
||||
years = date_parameters.years;
|
||||
months = date_parameters.months;
|
||||
days = date_parameters.days;
|
||||
}
|
||||
|
||||
int64_t Date::MicrosecondsSinceEpoch() const {
|
||||
auto result = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::years{years} + std::chrono::months{months} + std::chrono::days{days});
|
||||
|
||||
result -= epoch;
|
||||
return result.count();
|
||||
}
|
||||
|
||||
size_t DateHash::operator()(const Date &date) const {
|
||||
utils::HashCombine<uint64_t, uint64_t> hasher;
|
||||
size_t result = hasher(0, date.years);
|
||||
result = hasher(result, date.months);
|
||||
result = hasher(result, date.days);
|
||||
return result;
|
||||
}
|
||||
|
||||
LocalTime::LocalTime(const int64_t microseconds) {
|
||||
auto chrono_microseconds = std::chrono::microseconds(microseconds);
|
||||
MG_ASSERT(chrono_microseconds.count() >= 0, "Negative LocalTime specified in microseconds");
|
||||
|
||||
const auto parsed_hours = GetAndSubtractDuration<std::chrono::hours>(chrono_microseconds);
|
||||
MG_ASSERT(parsed_hours <= 23, "Invalid LocalTime specified in microseconds");
|
||||
|
||||
hours = parsed_hours;
|
||||
minutes = GetAndSubtractDuration<std::chrono::minutes>(chrono_microseconds);
|
||||
seconds = GetAndSubtractDuration<std::chrono::seconds>(chrono_microseconds);
|
||||
milliseconds = GetAndSubtractDuration<std::chrono::milliseconds>(chrono_microseconds);
|
||||
this->microseconds = chrono_microseconds.count();
|
||||
}
|
||||
|
||||
LocalTime::LocalTime(const LocalTimeParameters &local_time_parameters) {
|
||||
if (!IsInBounds(0, 23, local_time_parameters.hours)) {
|
||||
throw utils::BasicException("Creating a LocalTime with invalid hour parameter.");
|
||||
}
|
||||
|
||||
if (!IsInBounds(0, 59, local_time_parameters.minutes)) {
|
||||
throw utils::BasicException("Creating a LocalTime with invalid minutes parameter.");
|
||||
}
|
||||
|
||||
if (!IsInBounds(0, 59, local_time_parameters.seconds)) {
|
||||
throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
|
||||
}
|
||||
|
||||
if (!IsInBounds(0, 999, local_time_parameters.milliseconds)) {
|
||||
throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
|
||||
}
|
||||
|
||||
if (!IsInBounds(0, 999, local_time_parameters.microseconds)) {
|
||||
throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
|
||||
}
|
||||
|
||||
hours = local_time_parameters.hours;
|
||||
minutes = local_time_parameters.minutes;
|
||||
seconds = local_time_parameters.seconds;
|
||||
milliseconds = local_time_parameters.milliseconds;
|
||||
microseconds = local_time_parameters.microseconds;
|
||||
}
|
||||
|
||||
int64_t LocalTime::MicrosecondsSinceEpoch() const {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::hours{hours} + std::chrono::minutes{minutes} + std::chrono::seconds{seconds} +
|
||||
std::chrono::milliseconds{milliseconds} + std::chrono::microseconds{microseconds})
|
||||
.count();
|
||||
}
|
||||
|
||||
size_t LocalTimeHash::operator()(const LocalTime &local_time) const {
|
||||
utils::HashCombine<uint64_t, uint64_t> hasher;
|
||||
size_t result = hasher(0, local_time.hours);
|
||||
result = hasher(result, local_time.minutes);
|
||||
result = hasher(result, local_time.seconds);
|
||||
result = hasher(result, local_time.milliseconds);
|
||||
result = hasher(result, local_time.microseconds);
|
||||
return result;
|
||||
}
|
||||
|
||||
LocalDateTime::LocalDateTime(const int64_t microseconds) {
|
||||
auto chrono_microseconds = std::chrono::microseconds(microseconds);
|
||||
date = Date(chrono_microseconds.count());
|
||||
chrono_microseconds -= std::chrono::microseconds{date.MicrosecondsSinceEpoch()};
|
||||
local_time = LocalTime(chrono_microseconds.count());
|
||||
}
|
||||
|
||||
// return microseconds normilized with regard to epoch time point
|
||||
int64_t LocalDateTime::MicrosecondsSinceEpoch() const {
|
||||
return date.MicrosecondsSinceEpoch() + local_time.MicrosecondsSinceEpoch();
|
||||
}
|
||||
|
||||
LocalDateTime::LocalDateTime(const DateParameters date_parameters, const LocalTimeParameters &local_time_parameters)
|
||||
: date(date_parameters), local_time(local_time_parameters) {}
|
||||
|
||||
size_t LocalDateTimeHash::operator()(const LocalDateTime &local_date_time) const {
|
||||
utils::HashCombine<uint64_t, uint64_t> hasher;
|
||||
size_t result = hasher(0, LocalTimeHash{}(local_date_time.local_time));
|
||||
result = hasher(result, DateHash{}(local_date_time.date));
|
||||
return result;
|
||||
}
|
||||
|
||||
Duration::Duration(int64_t microseconds) { this->microseconds = microseconds; }
|
||||
|
||||
namespace {
|
||||
template <Chrono From, Chrono To>
|
||||
constexpr To CastChronoDouble(const double value) {
|
||||
return std::chrono::duration_cast<To>(std::chrono::duration<double, typename From::period>(value));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
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) +
|
||||
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))
|
||||
.count();
|
||||
}
|
||||
|
||||
Duration Duration::operator-() const {
|
||||
Duration result{-microseconds};
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t DurationHash::operator()(const Duration &duration) const { return std::hash<int64_t>{}(duration.microseconds); }
|
||||
|
||||
} // namespace query
|
105
src/query/temporal.hpp
Normal file
105
src/query/temporal.hpp
Normal file
@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace query {
|
||||
|
||||
struct DateParameters {
|
||||
int64_t years{0};
|
||||
int64_t months{1};
|
||||
int64_t days{1};
|
||||
};
|
||||
|
||||
struct Date {
|
||||
explicit Date() : Date{DateParameters{}} {}
|
||||
// we assume we accepted date in microseconds which was normilized using the epoch time point
|
||||
explicit Date(int64_t microseconds);
|
||||
explicit Date(const DateParameters &date_parameters);
|
||||
|
||||
int64_t MicrosecondsSinceEpoch() const;
|
||||
|
||||
auto operator<=>(const Date &) const = default;
|
||||
|
||||
uint16_t years;
|
||||
uint8_t months;
|
||||
uint8_t days;
|
||||
};
|
||||
|
||||
struct DateHash {
|
||||
size_t operator()(const Date &date) const;
|
||||
};
|
||||
|
||||
struct LocalTimeParameters {
|
||||
int64_t hours{0};
|
||||
int64_t minutes{0};
|
||||
int64_t seconds{0};
|
||||
int64_t milliseconds{0};
|
||||
int64_t microseconds{0};
|
||||
};
|
||||
|
||||
struct LocalTime {
|
||||
explicit LocalTime() : LocalTime{LocalTimeParameters{}} {}
|
||||
explicit LocalTime(int64_t microseconds);
|
||||
explicit LocalTime(const LocalTimeParameters &local_time_parameters);
|
||||
|
||||
int64_t MicrosecondsSinceEpoch() const;
|
||||
|
||||
auto operator<=>(const LocalTime &) const = default;
|
||||
|
||||
uint8_t hours;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
uint16_t milliseconds;
|
||||
uint16_t microseconds;
|
||||
};
|
||||
|
||||
struct LocalTimeHash {
|
||||
size_t operator()(const LocalTime &local_time) const;
|
||||
};
|
||||
|
||||
struct LocalDateTime {
|
||||
explicit LocalDateTime(int64_t microseconds);
|
||||
explicit LocalDateTime(DateParameters date, const LocalTimeParameters &local_time);
|
||||
|
||||
int64_t MicrosecondsSinceEpoch() const;
|
||||
|
||||
auto operator<=>(const LocalDateTime &) const = default;
|
||||
|
||||
Date date;
|
||||
LocalTime local_time;
|
||||
};
|
||||
|
||||
struct LocalDateTimeHash {
|
||||
size_t operator()(const LocalDateTime &local_date_time) const;
|
||||
};
|
||||
|
||||
struct DurationParameters {
|
||||
double years{0};
|
||||
double months{0};
|
||||
double days{0};
|
||||
double hours{0};
|
||||
double minutes{0};
|
||||
double seconds{0};
|
||||
// TODO(antonio2368): Check how to include milliseconds/microseconds
|
||||
// ISO 8601 does not specify string format for them
|
||||
};
|
||||
|
||||
struct Duration {
|
||||
explicit Duration(int64_t microseconds);
|
||||
explicit Duration(const DurationParameters ¶meters);
|
||||
|
||||
auto operator<=>(const Duration &) const = default;
|
||||
|
||||
Duration operator-() const;
|
||||
|
||||
int64_t microseconds;
|
||||
};
|
||||
|
||||
struct DurationHash {
|
||||
size_t operator()(const Duration &duration) const;
|
||||
};
|
||||
|
||||
} // namespace query
|
@ -1,12 +1,15 @@
|
||||
#include "query/typed_value.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "query/temporal.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/fnv.hpp"
|
||||
|
||||
@ -52,9 +55,32 @@ TypedValue::TypedValue(const storage::PropertyValue &value, utils::MemoryResourc
|
||||
for (const auto &kv : map) map_v.emplace(kv.first, kv.second);
|
||||
return;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// TODO (antonio2368): Add support for Temporal types in TypedValues
|
||||
break;
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
const auto &temporal_data = value.ValueTemporalData();
|
||||
switch (temporal_data.type) {
|
||||
case storage::TemporalType::Date: {
|
||||
type_ = Type::Date;
|
||||
new (&date_v) Date(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
case storage::TemporalType::LocalTime: {
|
||||
type_ = Type::LocalTime;
|
||||
new (&local_time_v) LocalTime(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
case storage::TemporalType::LocalDateTime: {
|
||||
type_ = Type::LocalDateTime;
|
||||
new (&local_date_time_v) LocalDateTime(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
case storage::TemporalType::Duration: {
|
||||
type_ = Type::Duration;
|
||||
new (&duration_v) Duration(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG_FATAL("Unsupported type");
|
||||
}
|
||||
@ -99,9 +125,32 @@ TypedValue::TypedValue(storage::PropertyValue &&other, utils::MemoryResource *me
|
||||
for (auto &kv : map) map_v.emplace(kv.first, std::move(kv.second));
|
||||
break;
|
||||
}
|
||||
case storage::PropertyValue::Type::TemporalData:
|
||||
// TODO (antonio2368): Add support for Temporal types in TypedValues
|
||||
LOG_FATAL("Unsupported type");
|
||||
case storage::PropertyValue::Type::TemporalData: {
|
||||
const auto &temporal_data = other.ValueTemporalData();
|
||||
switch (temporal_data.type) {
|
||||
case storage::TemporalType::Date: {
|
||||
type_ = Type::Date;
|
||||
new (&date_v) Date(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
case storage::TemporalType::LocalTime: {
|
||||
type_ = Type::LocalTime;
|
||||
new (&local_time_v) LocalTime(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
case storage::TemporalType::LocalDateTime: {
|
||||
type_ = Type::LocalDateTime;
|
||||
new (&local_date_time_v) LocalDateTime(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
case storage::TemporalType::Duration: {
|
||||
type_ = Type::Duration;
|
||||
new (&duration_v) Duration(temporal_data.microseconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
other = storage::PropertyValue();
|
||||
@ -143,6 +192,18 @@ TypedValue::TypedValue(const TypedValue &other, utils::MemoryResource *memory) :
|
||||
case Type::Path:
|
||||
new (&path_v) Path(other.path_v, memory_);
|
||||
return;
|
||||
case Type::Date:
|
||||
new (&date_v) Date(other.date_v);
|
||||
return;
|
||||
case Type::LocalTime:
|
||||
new (&local_time_v) LocalTime(other.local_time_v);
|
||||
return;
|
||||
case Type::LocalDateTime:
|
||||
new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
|
||||
return;
|
||||
case Type::Duration:
|
||||
new (&duration_v) Duration(other.duration_v);
|
||||
return;
|
||||
}
|
||||
LOG_FATAL("Unsupported TypedValue::Type");
|
||||
}
|
||||
@ -180,6 +241,18 @@ TypedValue::TypedValue(TypedValue &&other, utils::MemoryResource *memory) : memo
|
||||
case Type::Path:
|
||||
new (&path_v) Path(std::move(other.path_v), memory_);
|
||||
break;
|
||||
case Type::Date:
|
||||
new (&date_v) Date(other.date_v);
|
||||
break;
|
||||
case Type::LocalTime:
|
||||
new (&local_time_v) LocalTime(other.local_time_v);
|
||||
break;
|
||||
case Type::LocalDateTime:
|
||||
new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
|
||||
break;
|
||||
case Type::Duration:
|
||||
new (&duration_v) Duration(other.duration_v);
|
||||
break;
|
||||
}
|
||||
other.DestroyValue();
|
||||
}
|
||||
@ -203,6 +276,17 @@ TypedValue::operator storage::PropertyValue() const {
|
||||
for (const auto &kv : map_v) map.emplace(kv.first, kv.second);
|
||||
return storage::PropertyValue(std::move(map));
|
||||
}
|
||||
case Type::Date:
|
||||
return storage::PropertyValue(
|
||||
storage::TemporalData{storage::TemporalType::Date, date_v.MicrosecondsSinceEpoch()});
|
||||
case Type::LocalTime:
|
||||
return storage::PropertyValue(
|
||||
storage::TemporalData{storage::TemporalType::LocalTime, local_time_v.MicrosecondsSinceEpoch()});
|
||||
case Type::LocalDateTime:
|
||||
return storage::PropertyValue(
|
||||
storage::TemporalData{storage::TemporalType::LocalDateTime, local_date_time_v.MicrosecondsSinceEpoch()});
|
||||
case Type::Duration:
|
||||
return storage::PropertyValue(storage::TemporalData{storage::TemporalType::Duration, duration_v.microseconds});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -233,6 +317,10 @@ DEFINE_VALUE_AND_TYPE_GETTERS(TypedValue::TMap, Map, map_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(VertexAccessor, Vertex, vertex_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge, edge_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(Path, Path, path_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(Date, Date, date_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(LocalTime, LocalTime, local_time_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(LocalDateTime, LocalDateTime, local_date_time_v)
|
||||
DEFINE_VALUE_AND_TYPE_GETTERS(Duration, Duration, duration_v)
|
||||
|
||||
#undef DEFINE_VALUE_AND_TYPE_GETTERS
|
||||
|
||||
@ -249,6 +337,10 @@ bool TypedValue::IsPropertyValue() const {
|
||||
case Type::String:
|
||||
case Type::List:
|
||||
case Type::Map:
|
||||
case Type::Date:
|
||||
case Type::LocalTime:
|
||||
case Type::LocalDateTime:
|
||||
case Type::Duration:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -277,6 +369,14 @@ std::ostream &operator<<(std::ostream &os, const TypedValue::Type &type) {
|
||||
return os << "edge";
|
||||
case TypedValue::Type::Path:
|
||||
return os << "path";
|
||||
case TypedValue::Type::Date:
|
||||
return os << "date";
|
||||
case TypedValue::Type::LocalTime:
|
||||
return os << "local_time";
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
return os << "local_date_time";
|
||||
case TypedValue::Type::Duration:
|
||||
return os << "duration";
|
||||
}
|
||||
LOG_FATAL("Unsupported TypedValue::Type");
|
||||
}
|
||||
@ -325,6 +425,10 @@ TypedValue &TypedValue::operator=(const std::map<std::string, TypedValue> &other
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const VertexAccessor &, Vertex, vertex_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const EdgeAccessor &, Edge, edge_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Path &, Path, path_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Date &, Date, date_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const LocalTime &, LocalTime, local_time_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const LocalDateTime &, LocalDateTime, local_date_time_v)
|
||||
DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Duration &, Duration, duration_v)
|
||||
|
||||
#undef DEFINE_TYPED_VALUE_COPY_ASSIGNMENT
|
||||
|
||||
@ -408,6 +512,18 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
|
||||
case TypedValue::Type::Path:
|
||||
new (&path_v) Path(other.path_v, memory_);
|
||||
return *this;
|
||||
case Type::Date:
|
||||
new (&date_v) Date(other.date_v);
|
||||
return *this;
|
||||
case Type::LocalTime:
|
||||
new (&local_time_v) LocalTime(other.local_time_v);
|
||||
return *this;
|
||||
case Type::LocalDateTime:
|
||||
new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
|
||||
return *this;
|
||||
case Type::Duration:
|
||||
new (&duration_v) Duration(other.duration_v);
|
||||
return *this;
|
||||
}
|
||||
LOG_FATAL("Unsupported TypedValue::Type");
|
||||
}
|
||||
@ -455,6 +571,18 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept(false) {
|
||||
case TypedValue::Type::Path:
|
||||
new (&path_v) Path(std::move(other.path_v), memory_);
|
||||
break;
|
||||
case Type::Date:
|
||||
new (&date_v) Date(other.date_v);
|
||||
break;
|
||||
case Type::LocalTime:
|
||||
new (&local_time_v) LocalTime(other.local_time_v);
|
||||
break;
|
||||
case Type::LocalDateTime:
|
||||
new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
|
||||
break;
|
||||
case Type::Duration:
|
||||
new (&duration_v) Duration(other.duration_v);
|
||||
break;
|
||||
}
|
||||
other.DestroyValue();
|
||||
}
|
||||
@ -490,6 +618,11 @@ void TypedValue::DestroyValue() {
|
||||
case Type::Path:
|
||||
path_v.~Path();
|
||||
break;
|
||||
case Type::Date:
|
||||
case Type::LocalTime:
|
||||
case Type::LocalDateTime:
|
||||
case Type::Duration:
|
||||
break;
|
||||
}
|
||||
|
||||
type_ = TypedValue::Type::Null;
|
||||
@ -515,6 +648,15 @@ double ToDouble(const TypedValue &value) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool IsTemporalType(const TypedValue::Type type) {
|
||||
constexpr std::array temporal_types{TypedValue::Type::Date, TypedValue::Type::LocalTime,
|
||||
TypedValue::Type::LocalDateTime, TypedValue::Type::Duration};
|
||||
return std::any_of(temporal_types.begin(), temporal_types.end(),
|
||||
[type](const auto temporal_type) { return temporal_type == type; });
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TypedValue operator<(const TypedValue &a, const TypedValue &b) {
|
||||
auto is_legal = [](TypedValue::Type type) {
|
||||
switch (type) {
|
||||
@ -522,6 +664,10 @@ TypedValue operator<(const TypedValue &a, const TypedValue &b) {
|
||||
case TypedValue::Type::Int:
|
||||
case TypedValue::Type::Double:
|
||||
case TypedValue::Type::String:
|
||||
case TypedValue::Type::Date:
|
||||
case TypedValue::Type::LocalTime:
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
case TypedValue::Type::Duration:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -540,6 +686,29 @@ TypedValue operator<(const TypedValue &a, const TypedValue &b) {
|
||||
}
|
||||
}
|
||||
|
||||
if (IsTemporalType(a.type()) || IsTemporalType(b.type())) {
|
||||
if (a.type() != b.type()) {
|
||||
throw TypedValueException("Invalid 'less' operand types({} + {})", a.type(), b.type());
|
||||
}
|
||||
|
||||
switch (a.type()) {
|
||||
case TypedValue::Type::Date:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return TypedValue(a.ValueDate() < b.ValueDate(), a.GetMemoryResource());
|
||||
case TypedValue::Type::LocalTime:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return TypedValue(a.ValueLocalTime() < b.ValueLocalTime(), a.GetMemoryResource());
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return TypedValue(a.ValueLocalDateTime() < b.ValueLocalDateTime(), a.GetMemoryResource());
|
||||
case TypedValue::Type::Duration:
|
||||
// NOLINTNEXTLINE(modernize-use-nullptr)
|
||||
return TypedValue(a.ValueDuration() < b.ValueDuration(), a.GetMemoryResource());
|
||||
default:
|
||||
LOG_FATAL("Invalid temporal type");
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we only have int and double
|
||||
if (a.IsDouble() || b.IsDouble()) {
|
||||
return TypedValue(ToDouble(a) < ToDouble(b), a.GetMemoryResource());
|
||||
@ -605,6 +774,14 @@ TypedValue operator==(const TypedValue &a, const TypedValue &b) {
|
||||
}
|
||||
case TypedValue::Type::Path:
|
||||
return TypedValue(a.ValuePath() == b.ValuePath(), a.GetMemoryResource());
|
||||
case TypedValue::Type::Date:
|
||||
return TypedValue(a.ValueDate() == b.ValueDate(), a.GetMemoryResource());
|
||||
case TypedValue::Type::LocalTime:
|
||||
return TypedValue(a.ValueLocalTime() == b.ValueLocalTime(), a.GetMemoryResource());
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
return TypedValue(a.ValueLocalDateTime() == b.ValueLocalDateTime(), a.GetMemoryResource());
|
||||
case TypedValue::Type::Duration:
|
||||
return TypedValue(a.ValueDuration() == b.ValueDuration(), a.GetMemoryResource());
|
||||
default:
|
||||
LOG_FATAL("Unhandled comparison for types");
|
||||
}
|
||||
@ -635,6 +812,7 @@ TypedValue operator-(const TypedValue &a) {
|
||||
if (a.IsNull()) return TypedValue(a.GetMemoryResource());
|
||||
if (a.IsInt()) return TypedValue(-a.ValueInt(), a.GetMemoryResource());
|
||||
if (a.IsDouble()) return TypedValue(-a.ValueDouble(), a.GetMemoryResource());
|
||||
if (a.IsDuration()) return TypedValue(-a.ValueDuration(), a.GetMemoryResource());
|
||||
throw TypedValueException("Invalid unary minus operand type (-{})", a.type());
|
||||
}
|
||||
|
||||
@ -666,6 +844,7 @@ inline void EnsureArithmeticallyOk(const TypedValue &a, const TypedValue &b, boo
|
||||
// checked here because they are handled before this check is performed in
|
||||
// arithmetic op implementations.
|
||||
|
||||
// TODO(antonio2368): Introduce typed value arithmetic
|
||||
if (!is_legal(a) || !is_legal(b))
|
||||
throw TypedValueException("Invalid {} operand types {}, {}", op_name, a.type(), b.type());
|
||||
}
|
||||
@ -699,6 +878,7 @@ TypedValue operator+(const TypedValue &a, const TypedValue &b) {
|
||||
} else {
|
||||
return TypedValue(a.ValueInt() + b.ValueInt(), a.GetMemoryResource());
|
||||
}
|
||||
// TODO(antonio2368): Introduce typed value arithmetic
|
||||
}
|
||||
|
||||
TypedValue operator-(const TypedValue &a, const TypedValue &b) {
|
||||
@ -711,6 +891,7 @@ TypedValue operator-(const TypedValue &a, const TypedValue &b) {
|
||||
} else {
|
||||
return TypedValue(a.ValueInt() - b.ValueInt(), a.GetMemoryResource());
|
||||
}
|
||||
// TODO(antonio2368): Introduce typed value arithmetic
|
||||
}
|
||||
|
||||
TypedValue operator/(const TypedValue &a, const TypedValue &b) {
|
||||
@ -838,6 +1019,15 @@ size_t TypedValue::Hash::operator()(const TypedValue &value) const {
|
||||
return utils::FnvCollection<decltype(vertices), VertexAccessor>{}(vertices) ^
|
||||
utils::FnvCollection<decltype(edges), EdgeAccessor>{}(edges);
|
||||
}
|
||||
case TypedValue::Type::Date:
|
||||
return DateHash{}(value.ValueDate());
|
||||
case TypedValue::Type::LocalTime:
|
||||
return LocalTimeHash{}(value.ValueLocalTime());
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
return LocalDateTimeHash{}(value.ValueLocalDateTime());
|
||||
case TypedValue::Type::Duration:
|
||||
return DurationHash{}(value.ValueDuration());
|
||||
break;
|
||||
}
|
||||
LOG_FATAL("Unhandled TypedValue.type() in hash function");
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "query/db_accessor.hpp"
|
||||
#include "query/path.hpp"
|
||||
#include "query/temporal.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
#include "utils/pmr/map.hpp"
|
||||
@ -56,7 +57,22 @@ class TypedValue {
|
||||
};
|
||||
|
||||
/** A value type. Each type corresponds to exactly one C++ type */
|
||||
enum class Type : unsigned { Null, Bool, Int, Double, String, List, Map, Vertex, Edge, Path };
|
||||
enum class Type : unsigned {
|
||||
Null,
|
||||
Bool,
|
||||
Int,
|
||||
Double,
|
||||
String,
|
||||
List,
|
||||
Map,
|
||||
Vertex,
|
||||
Edge,
|
||||
Path,
|
||||
Date,
|
||||
LocalTime,
|
||||
LocalDateTime,
|
||||
Duration
|
||||
};
|
||||
|
||||
// TypedValue at this exact moment of compilation is an incomplete type, and
|
||||
// the standard says that instantiating a container with an incomplete type
|
||||
@ -124,6 +140,26 @@ class TypedValue {
|
||||
double_v = value;
|
||||
}
|
||||
|
||||
explicit TypedValue(const Date &value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: memory_(memory), type_(Type::Date) {
|
||||
date_v = value;
|
||||
}
|
||||
|
||||
explicit TypedValue(const LocalTime &value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: memory_(memory), type_(Type::LocalTime) {
|
||||
local_time_v = value;
|
||||
}
|
||||
|
||||
explicit TypedValue(const LocalDateTime &value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: memory_(memory), type_(Type::LocalDateTime) {
|
||||
local_date_time_v = value;
|
||||
}
|
||||
|
||||
explicit TypedValue(const Duration &value, utils::MemoryResource *memory = utils::NewDeleteResource())
|
||||
: memory_(memory), type_(Type::Duration) {
|
||||
duration_v = value;
|
||||
}
|
||||
|
||||
// conversion function to storage::PropertyValue
|
||||
explicit operator storage::PropertyValue() const;
|
||||
|
||||
@ -381,6 +417,10 @@ class TypedValue {
|
||||
TypedValue &operator=(const VertexAccessor &);
|
||||
TypedValue &operator=(const EdgeAccessor &);
|
||||
TypedValue &operator=(const Path &);
|
||||
TypedValue &operator=(const Date &);
|
||||
TypedValue &operator=(const LocalTime &);
|
||||
TypedValue &operator=(const LocalDateTime &);
|
||||
TypedValue &operator=(const Duration &);
|
||||
|
||||
/** Copy assign other, utils::MemoryResource of `this` is used */
|
||||
TypedValue &operator=(const TypedValue &other);
|
||||
@ -431,6 +471,11 @@ class TypedValue {
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(Path, Path)
|
||||
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(Date, Date)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(LocalTime, LocalTime)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(LocalDateTime, LocalDateTime)
|
||||
DECLARE_VALUE_AND_TYPE_GETTERS(Duration, Duration)
|
||||
|
||||
#undef DECLARE_VALUE_AND_TYPE_GETTERS
|
||||
|
||||
/** Checks if value is a TypedValue::Null. */
|
||||
@ -468,6 +513,10 @@ class TypedValue {
|
||||
VertexAccessor vertex_v;
|
||||
EdgeAccessor edge_v;
|
||||
Path path_v;
|
||||
Date date_v;
|
||||
LocalTime local_time_v;
|
||||
LocalDateTime local_date_time_v;
|
||||
Duration duration_v;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -125,6 +125,8 @@ target_link_libraries(${test_prefix}query_serialization_property_value mg-query)
|
||||
add_unit_test(query_streams.cpp)
|
||||
target_link_libraries(${test_prefix}query_streams mg-query kafka-mock)
|
||||
|
||||
add_unit_test(query_temporal query_temporal.cpp)
|
||||
target_link_libraries(${test_prefix}query_temporal mg-query)
|
||||
|
||||
# Test query/procedure
|
||||
add_unit_test(query_procedure_mgp_type.cpp)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "query/temporal.hpp"
|
||||
#include "query/typed_value.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
|
||||
@ -61,6 +62,15 @@ inline std::string ToString(const query::Path &path, const TAccessor &acc) {
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// TODO(antonio2368): Define printing of dates
|
||||
inline std::string ToString(const query::Date) { return ""; }
|
||||
|
||||
inline std::string ToString(const query::LocalTime) { return ""; }
|
||||
|
||||
inline std::string ToString(const query::LocalDateTime) { return ""; }
|
||||
|
||||
inline std::string ToString(const query::Duration) { return ""; }
|
||||
|
||||
template <class TAccessor>
|
||||
inline std::string ToString(const query::TypedValue &value, const TAccessor &acc) {
|
||||
std::ostringstream os;
|
||||
@ -102,6 +112,18 @@ inline std::string ToString(const query::TypedValue &value, const TAccessor &acc
|
||||
case query::TypedValue::Type::Path:
|
||||
os << ToString(value.ValuePath(), acc);
|
||||
break;
|
||||
case query::TypedValue::Type::Date:
|
||||
os << ToString(value.ValueDate());
|
||||
break;
|
||||
case query::TypedValue::Type::LocalTime:
|
||||
os << ToString(value.ValueLocalTime());
|
||||
break;
|
||||
case query::TypedValue::Type::LocalDateTime:
|
||||
os << ToString(value.ValueLocalDateTime());
|
||||
break;
|
||||
case query::TypedValue::Type::Duration:
|
||||
os << ToString(value.ValueDuration());
|
||||
break;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
161
tests/unit/query_temporal.cpp
Normal file
161
tests/unit/query_temporal.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include <optional>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "query/temporal.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
namespace {
|
||||
struct TestDateParameters {
|
||||
query::DateParameters date_parameters;
|
||||
bool should_throw;
|
||||
};
|
||||
|
||||
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 {
|
||||
query::LocalTimeParameters local_time_parameters;
|
||||
bool should_throw;
|
||||
};
|
||||
|
||||
constexpr std::array test_local_times{
|
||||
TestLocalTimeParameters{{.hours = 24}, true}, TestLocalTimeParameters{{.hours = -1}, true},
|
||||
TestLocalTimeParameters{{.minutes = -1}, true}, TestLocalTimeParameters{{.minutes = 60}, true},
|
||||
TestLocalTimeParameters{{.seconds = -1}, true}, TestLocalTimeParameters{{.minutes = 60}, true},
|
||||
TestLocalTimeParameters{{.milliseconds = -1}, true}, TestLocalTimeParameters{{.milliseconds = 1000}, true},
|
||||
TestLocalTimeParameters{{.microseconds = -1}, true}, TestLocalTimeParameters{{.microseconds = 1000}, true},
|
||||
TestLocalTimeParameters{{23, 59, 59, 999, 999}, false}, TestLocalTimeParameters{{0, 0, 0, 0, 0}, false}};
|
||||
} // namespace
|
||||
|
||||
TEST(TemporalTest, DateConstruction) {
|
||||
std::optional<query::Date> test_date;
|
||||
|
||||
for (const auto [date_parameters, should_throw] : test_dates) {
|
||||
if (should_throw) {
|
||||
ASSERT_THROW(test_date.emplace(date_parameters), utils::BasicException);
|
||||
} else {
|
||||
ASSERT_NO_THROW(test_date.emplace(date_parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DateMicrosecondsSinceEpochConversion) {
|
||||
const auto check_microseconds = [](const auto date_parameters) {
|
||||
query::Date initial_date{date_parameters};
|
||||
const auto microseconds = initial_date.MicrosecondsSinceEpoch();
|
||||
query::Date new_date{microseconds};
|
||||
ASSERT_EQ(initial_date, new_date);
|
||||
};
|
||||
|
||||
check_microseconds(query::DateParameters{2020, 11, 22});
|
||||
check_microseconds(query::DateParameters{1900, 2, 22});
|
||||
check_microseconds(query::DateParameters{0, 1, 1});
|
||||
|
||||
ASSERT_THROW(check_microseconds(query::DateParameters{-10, 1, 1}), utils::BasicException);
|
||||
|
||||
{
|
||||
query::Date date{query::DateParameters{1970, 1, 1}};
|
||||
ASSERT_EQ(date.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
{
|
||||
query::Date date{query::DateParameters{1910, 1, 1}};
|
||||
ASSERT_LT(date.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
{
|
||||
query::Date date{query::DateParameters{2021, 1, 1}};
|
||||
ASSERT_GT(date.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalTimeConstruction) {
|
||||
std::optional<query::LocalTime> 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), utils::BasicException);
|
||||
} else {
|
||||
ASSERT_NO_THROW(test_local_time.emplace(local_time_parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalTimeMicrosecondsSinceEpochConversion) {
|
||||
const auto check_microseconds = [](const query::LocalTimeParameters ¶meters) {
|
||||
query::LocalTime initial_local_time{parameters};
|
||||
const auto microseconds = initial_local_time.MicrosecondsSinceEpoch();
|
||||
query::LocalTime new_local_time{microseconds};
|
||||
ASSERT_EQ(initial_local_time, new_local_time);
|
||||
};
|
||||
|
||||
check_microseconds(query::LocalTimeParameters{23, 59, 59, 999, 999});
|
||||
check_microseconds(query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
check_microseconds(query::LocalTimeParameters{14, 8, 55, 321, 452});
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalDateTimeMicrosecondsSinceEpochConversion) {
|
||||
const auto check_microseconds = [](const query::DateParameters date_parameters,
|
||||
const query::LocalTimeParameters &local_time_parameters) {
|
||||
query::LocalDateTime initial_local_date_time{date_parameters, local_time_parameters};
|
||||
const auto microseconds = initial_local_date_time.MicrosecondsSinceEpoch();
|
||||
query::LocalDateTime new_local_date_time{microseconds};
|
||||
ASSERT_EQ(initial_local_date_time, new_local_date_time);
|
||||
};
|
||||
|
||||
check_microseconds(query::DateParameters{2020, 11, 22}, query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
check_microseconds(query::DateParameters{1900, 2, 22}, query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
check_microseconds(query::DateParameters{0, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
{
|
||||
query::LocalDateTime local_date_time(query::DateParameters{1970, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
{
|
||||
query::LocalDateTime local_date_time(query::DateParameters{1970, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 1});
|
||||
ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
{
|
||||
query::LocalTimeParameters local_time_parameters{12, 10, 40, 42, 42};
|
||||
query::LocalDateTime local_date_time{query::DateParameters{1970, 1, 1}, local_time_parameters};
|
||||
ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(),
|
||||
query::LocalTime{local_time_parameters}.MicrosecondsSinceEpoch());
|
||||
}
|
||||
{
|
||||
query::LocalDateTime local_date_time(query::DateParameters{1910, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
ASSERT_LT(local_date_time.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
{
|
||||
query::LocalDateTime local_date_time(query::DateParameters{2021, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
|
||||
ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DurationConversion) {
|
||||
{
|
||||
query::Duration duration{{.years = 2.5}};
|
||||
const auto microseconds = duration.microseconds;
|
||||
query::LocalDateTime local_date_time{microseconds};
|
||||
ASSERT_EQ(local_date_time.date.years, 2 + 1970);
|
||||
ASSERT_EQ(local_date_time.date.months, 6 + 1);
|
||||
};
|
||||
{
|
||||
query::Duration duration{{.months = 26}};
|
||||
const auto microseconds = duration.microseconds;
|
||||
query::LocalDateTime local_date_time{microseconds};
|
||||
ASSERT_EQ(local_date_time.date.years, 2 + 1970);
|
||||
ASSERT_EQ(local_date_time.date.months, 2 + 1);
|
||||
};
|
||||
{
|
||||
query::Duration duration{{.minutes = 123.25}};
|
||||
const auto microseconds = duration.microseconds;
|
||||
query::LocalDateTime local_date_time{microseconds};
|
||||
ASSERT_EQ(local_date_time.date.years, 1970);
|
||||
ASSERT_EQ(local_date_time.date.months, 1);
|
||||
ASSERT_EQ(local_date_time.date.days, 1);
|
||||
ASSERT_EQ(local_date_time.local_time.hours, 2);
|
||||
ASSERT_EQ(local_date_time.local_time.minutes, 3);
|
||||
ASSERT_EQ(local_date_time.local_time.seconds, 15);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user