From c09dd511809f2eef7ca631d9e00c3f51f612e185 Mon Sep 17 00:00:00 2001 From: lipanpan03 <656461146@qq.com> Date: Thu, 5 Dec 2024 17:28:00 +0800 Subject: [PATCH 1/3] fix temporal 1 - 3 --- src/bolt/pack_stream.h | 2 +- src/bolt/to_string.h | 2 +- src/common/temporal/date.cpp | 162 ++++++++++++++++-- src/common/temporal/date.h | 5 + src/common/temporal/datetime.cpp | 21 ++- src/common/temporal/localdatetime.cpp | 19 +- src/common/temporal/temporal_pattern.h | 3 +- src/common/temporal/time.cpp | 36 +++- src/common/temporal/time.h | 2 +- src/common/value.cpp | 18 +- .../arithmetic/arithmetic_expression.cpp | 7 +- 11 files changed, 237 insertions(+), 40 deletions(-) diff --git a/src/bolt/pack_stream.h b/src/bolt/pack_stream.h index 5a2aef6e2f..0d56c47750 100644 --- a/src/bolt/pack_stream.h +++ b/src/bolt/pack_stream.h @@ -90,7 +90,7 @@ class PackStream { } void PackDateTime(const bolt::DateTime& m) { packer_.StructHeader('I', 3); - packer_.Int64(m.seconds); + packer_.Int64(m.seconds - m.tz_offset_seconds); packer_.Int64(m.nanoseconds); packer_.Int64(m.tz_offset_seconds); } diff --git a/src/bolt/to_string.h b/src/bolt/to_string.h index 6168f53059..7d9d1763b6 100644 --- a/src/bolt/to_string.h +++ b/src/bolt/to_string.h @@ -62,7 +62,7 @@ nlohmann::json ToJsonObj(const std::any& item) { } else if (item.type() == typeid(bolt::DateTime)) { const auto& dateTime = std::any_cast(item); common::DateTime dt( - dateTime.seconds * 1000000000 + dateTime.nanoseconds, + (dateTime.seconds + dateTime.tz_offset_seconds) * 1000000000 + dateTime.nanoseconds, dateTime.tz_offset_seconds); return ToJsonObj(dt.ToString()); } else if (item.type() == typeid(bolt::LocalDateTime)) { diff --git a/src/common/temporal/date.cpp b/src/common/temporal/date.cpp index 7a474c7f67..950613e867 100644 --- a/src/common/temporal/date.cpp +++ b/src/common/temporal/date.cpp @@ -61,10 +61,10 @@ void validateDateParams( // handle field conflict for (const auto& [param, _] : params_map) { if (fieldConflictMap.count(param)) { - const auto& fieldConflit = fieldConflictMap.at(param); - for (const auto& rhs : fieldConflit.first) { + const auto& fieldConflict = fieldConflictMap.at(param); + for (const auto& rhs : fieldConflict.first) { if (params_map.count(rhs)) { - THROW_CODE(InputError, fieldConflit.second.c_str(), rhs); + THROW_CODE(InputError, fieldConflict.second.c_str(), rhs); } } } @@ -74,16 +74,19 @@ void validateDateParams( if (params_map.count(DATE_YEAR) == 0 && (params_map.count(DATE_ORDINAL) == 0 && params_map.count(DATE_DATE) == 0 && + params_map.count(DATE_DATETIME) == 0 && params_map.count(DATE_TIMEZONE) == 0)) { THROW_CODE(InputError, "year must be specified"); } // handle field group - for (const auto& [param, _] : params_map) { - if (fieldGroup.count(param)) { - if (params_map.count(fieldGroup.at(param)) == 0) { - THROW_CODE(InputError, "{} can not be specified without {}", - param, fieldGroup.at(param)); + if (params_map.count(DATE_DATE) != 0 && params_map.count(DATE_DATETIME) != 0) { + for (const auto& [param, _] : params_map) { + if (fieldGroup.count(param)) { + if (params_map.count(fieldGroup.at(param)) == 0) { + THROW_CODE(InputError, "{} can not be specified without {}", + param, fieldGroup.at(param)); + } } } } @@ -106,6 +109,25 @@ void Date::fromYearMonthDay(int year, unsigned month, unsigned day) { } } +void Date::fromYearMonthDay(const Date& base_date, std::optional year, + std::optional month, std::optional day) { + date::year_month_day ymd = date::year_month_day{date::local_days(date::days(base_date.days_since_epoch_))}; + int target_year = year.has_value() ? year.value() : (int)ymd.year(); + unsigned target_month = month.has_value() ? month.value() : (unsigned)ymd.month(); + unsigned target_day = day.has_value() ? day.value() : (unsigned)ymd.day(); + + ymd = date::year_month_day( + date::year{target_year}, date::month{target_month},date::day{date::day{target_day}}); + if (ymd.ok()) { + auto ld = date::local_days(ymd); + days_since_epoch_ = ld.time_since_epoch().count(); + } else { + std::ostringstream oss; + oss << ymd; + THROW_CODE(InputError, "{}", oss.str()); + } +} + void Date::fromYearWeekDow(int year, unsigned week, unsigned dow) { iso_week::year_weeknum_weekday yww(iso_week::year{year}, iso_week::weeknum{week}, @@ -146,6 +168,54 @@ void Date::fromYearWeekDow(const Date& base_date, std::optional year, } } +void Date::fromYearQuarterDoq(const Date& base_date, std::optional year, + std::optional quarter, std::optional doq) { + date::year_month_day ymd = date::year_month_day{date::local_days(date::days(base_date.days_since_epoch_))}; + int target_year = year.has_value() ? year.value() : (int)ymd.year(); + if (!doq.has_value()) { + unsigned target_month = quarter.has_value() ? (quarter.value() - 1) * 3 + ((unsigned)ymd.month() - 1) % 3 + 1 : + (unsigned)ymd.month(); + ymd = date::year_month_day(date::year(target_year), date::month(target_month), ymd.day()); + } else { + unsigned target_quarter = quarter.has_value() ? quarter.value() : ((unsigned)ymd.month() - 1) / 3 + 1; + switch (target_quarter) { + case 1: { + ymd = date::year{target_year} / 1 / 1; + break; + } + case 2: { + ymd = date::year{target_year} / 4 / 1; + break; + } + case 3: { + ymd = date::year{target_year} / 7 / 1; + break; + } + case 4: { + ymd = date::year{target_year} / 10 / 1; + break; + } + default: + THROW_CODE( + InputError, + "Invalid value for QuarterOfYear (valid values 1 - 4): {}", + target_quarter); + } + auto days = (date::local_days)ymd; + days += date::days(doq.value() - 1); + ymd = date::year_month_day(days); + } + + if (ymd.ok()) { + date::local_days ld = (date::local_days)ymd; + days_since_epoch_ = ld.time_since_epoch().count(); + } else { + std::ostringstream oss; + oss << ymd; + THROW_CODE(InputError, "{}", oss.str()); + } +} + void Date::fromYearQuarterDoq(int year, int quarter, int doq) { date::year_month_day ymd; switch (quarter) { @@ -186,6 +256,28 @@ void Date::fromYearQuarterDoq(int year, int quarter, int doq) { } } +void Date::fromYearDoy(const Date& base_date, std::optional year, std::optional doy) { + date::year_month_day ymd = date::year_month_day{date::local_days(date::days(base_date.days_since_epoch_))}; + int target_year = year.has_value() ? year.value() : (int)ymd.year(); + if (doy.has_value()) { + ymd = date::year_month_day{date::year{target_year}, date::month{1}, date::day{1}}; + auto days = (date::local_days)ymd; + days += date::days(doy.value() - 1); + ymd = date::year_month_day(days); + } else { + ymd = date::year_month_day{date::year{target_year}, ymd.month(), ymd.day()}; + } + + if (ymd.ok()) { + date::local_days ld = (date::local_days)ymd; + days_since_epoch_ = ld.time_since_epoch().count(); + } else { + std::ostringstream oss; + oss << ymd; + THROW_CODE(InputError, "{}", oss.str()); + } +} + void Date::fromYearDoy(int year, int doy) { date::year_month_day ymd = date::year_month_day{date::year{year}, date::month{1}, date::day{1}}; @@ -317,12 +409,26 @@ Date::Date(const std::string& str) { } Date::Date(const Value& params) { + if (params.IsDate()) { + days_since_epoch_ = params.AsDate().GetStorage(); + return; + } + if (params.IsDateTime()) { + days_since_epoch_ = std::get<0>(params.AsDateTime().GetStorage()) / (NANOS_PER_SECOND * SECONDS_PER_DAY); + return; + } + if (params.IsLocalDateTime()) { + days_since_epoch_ = params.AsLocalDateTime().GetStorage() / (NANOS_PER_SECOND * SECONDS_PER_DAY); + return; + } + std::optional year; std::optional week; std::optional dow; - int doy = 1; - unsigned month = 1, day = 1; - int quarter = 1, doq = 1; + + std::optional doy; + std::optional month, day; + std::optional quarter, doq; std::string timezone; std::optional base_date = std::nullopt; @@ -342,6 +448,17 @@ Date::Date(const Value& params) { DateType dt = DateType::CALENDER_DATE; try { + if (params_map.count(DATE_DATE)) { + if (params_map.at(DATE_DATE).IsDate()) { + base_date = params_map.at(DATE_DATE).AsDate(); + } else if (params_map.at(DATE_DATE).IsDateTime()) { + base_date = Date(std::get<0>(params_map.at(DATE_DATE).AsDateTime().GetStorage()) / (NANOS_PER_SECOND * SECONDS_PER_DAY)); + } else if (params_map.at(DATE_DATE).IsLocalDateTime()) { + base_date = Date(params_map.at(DATE_DATE).AsLocalDateTime().GetStorage() / (NANOS_PER_SECOND * SECONDS_PER_DAY)); + } else { + THROW_CODE(InvalidParameter, "Cannot get the date of: {}", params_map.at(DATE_DATE).ToString()); + } + } if (params_map.count(std::string(DATE_YEAR))) { year = (int)params_map.at(DATE_YEAR).AsInteger(); } @@ -357,6 +474,7 @@ Date::Date(const Value& params) { } if (params_map.count(DATE_DOW)) { dow = (unsigned)params_map.at(DATE_DOW).AsInteger(); + dt = DateType::WEEK_DATE; } if (params_map.count(DATE_QUARTER)) { quarter = (int)params_map.at(DATE_QUARTER).AsInteger(); @@ -364,14 +482,12 @@ Date::Date(const Value& params) { } if (params_map.count(DATE_DOQ)) { doq = (int)params_map.at(DATE_DOQ).AsInteger(); + dt = DateType::QUATER_DATE; } if (params_map.count(DATE_ORDINAL)) { doy = (int)params_map.at(DATE_ORDINAL).AsInteger(); dt = DateType::ORDINAL_DATE; } - if (params_map.count(DATE_DATE)) { - base_date = params_map.at(DATE_DATE).AsDate(); - } if (params_map.count(DATE_TIMEZONE)) { timezone = params_map.at(DATE_TIMEZONE).AsString(); dt = DateType::TIMEZONE_DATE; @@ -383,7 +499,11 @@ Date::Date(const Value& params) { switch (dt) { case DateType::CALENDER_DATE: - fromYearMonthDay(year.value(), month, day); + if (base_date.has_value()) { + fromYearMonthDay(base_date.value(), year, month, day); + } else { + fromYearMonthDay(year.value(), month.value_or(1), day.value_or(1)); + } break; case DateType::WEEK_DATE: { if (base_date.has_value()) { @@ -394,10 +514,18 @@ Date::Date(const Value& params) { break; } case DateType::QUATER_DATE: - fromYearQuarterDoq(year.value(), quarter, doq); + if (base_date.has_value()) { + fromYearQuarterDoq(base_date.value(), year, quarter, doq); + } else { + fromYearQuarterDoq(year.value(), quarter.value_or(1), doq.value_or(1)); + } break; case DateType::ORDINAL_DATE: - fromYearDoy(year.value(), doy); + if (base_date.has_value()) { + fromYearDoy(base_date.value(), year, doy); + } else { + fromYearDoy(year.value(), doy.value_or(1)); + } break; case DateType::TIMEZONE_DATE: fromTimeZone(timezone); diff --git a/src/common/temporal/date.h b/src/common/temporal/date.h index e53832a5ab..3e77acad8e 100644 --- a/src/common/temporal/date.h +++ b/src/common/temporal/date.h @@ -30,12 +30,17 @@ class Date { int64_t days_since_epoch_ = 0; void fromYearMonthDay(int year, unsigned month, unsigned day); + void fromYearMonthDay(const Date& base_date, std::optional year, + std::optional month, std::optional day); void fromYearWeekDow(int year, unsigned week, unsigned dow); void fromYearWeekDow(const Date& base_date, std::optional year, std::optional week, std::optional dow); void fromYearQuarterDoq(int year, int quarter, int doq); + void fromYearQuarterDoq(const Date& base_date, std::optional year, + std::optional quarter, std::optional doq); void fromYearDoy(int year, int doy); + void fromYearDoy(const Date& base_date, std::optional year, std::optional doy); void fromTimeZone(std::string timezone); public: diff --git a/src/common/temporal/datetime.cpp b/src/common/temporal/datetime.cpp index 6ccf36cb3e..45de70e4f0 100644 --- a/src/common/temporal/datetime.cpp +++ b/src/common/temporal/datetime.cpp @@ -40,7 +40,7 @@ DateTime::DateTime(const std::string& str) { days_since_epoch = Date(str).GetStorage(); } else { days_since_epoch = Date(str.substr(0, pos)).GetStorage(); - auto t = Time(str.substr(pos + 1)); + auto t = Time(str.substr(pos + 1), days_since_epoch); nanoseconds_since_begin_of_day = std::get<0>(t.GetStorage()); tz_offset_seconds_ = std::get<1>(t.GetStorage()); timezone_name_ = t.GetTimezoneName(); @@ -56,6 +56,15 @@ std::string DateTime::GetTimezoneName() const { } DateTime::DateTime(const Value& params) { + if (params.IsDateTime()) { + nanoseconds_since_epoch_ = std::get<0>(params.AsDateTime().GetStorage()); + tz_offset_seconds_ = std::get<1>(params.AsDateTime().GetStorage()); + return; + } + if (params.IsLocalDateTime()) { + nanoseconds_since_epoch_ = params.AsLocalDateTime().GetStorage(); + return; + } std::unordered_map dateParams; std::unordered_map timeParams; for (const auto& [key, v] : params.AsMap()) { @@ -63,7 +72,12 @@ DateTime::DateTime(const Value& params) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); // handle "timezone" in Time if (s != DATE_TIMEZONE && validDateFields.count(s)) { - dateParams.emplace(s, v); + if (s == DATE_DATETIME) { + dateParams.emplace("date", v); + timeParams.emplace("time", v); + } else { + dateParams.emplace(s, v); + } } else { timeParams.emplace(s, v); } @@ -74,6 +88,9 @@ DateTime::DateTime(const Value& params) { days_since_epoch = Date(Value(std::move(dateParams))).GetStorage(); if (!timeParams.empty()) { timeParams.emplace("days_for_timezone", days_since_epoch); + if (!timeParams.count("hour") && !timeParams.count("time")) { + timeParams["hour"] = Value::Integer(0); + } auto t = Time(Value(std::move(timeParams))); nanoseconds_since_begin_of_day = std::get<0>(t.GetStorage()); tz_offset_seconds_ = std::get<1>(t.GetStorage()); diff --git a/src/common/temporal/localdatetime.cpp b/src/common/temporal/localdatetime.cpp index f8b9eb4e51..02b4568457 100644 --- a/src/common/temporal/localdatetime.cpp +++ b/src/common/temporal/localdatetime.cpp @@ -50,13 +50,27 @@ LocalDateTime::LocalDateTime(const std::string& str) { } LocalDateTime::LocalDateTime(const Value& params) { + if (params.IsDateTime()) { + nanoseconds_since_epoch_ = std::get<0>(params.AsDateTime().GetStorage()); + return; + } + if (params.IsLocalDateTime()) { + nanoseconds_since_epoch_ = params.AsLocalDateTime().GetStorage(); + return; + } + std::unordered_map dateParams; std::unordered_map timeParams; for (const auto& [key, v] : params.AsMap()) { auto s = key; std::transform(s.begin(), s.end(), s.begin(), ::tolower); if (validDateFields.count(s)) { - dateParams.emplace(s, v); + if (s == DATE_DATETIME) { + dateParams.emplace("date", v); + timeParams.emplace("time", v); + } else { + dateParams.emplace(s, v); + } } else { timeParams.emplace(s, v); } @@ -66,6 +80,9 @@ LocalDateTime::LocalDateTime(const Value& params) { int64_t nanoseconds_since_begin_of_day = 0; days_since_epoch = Date(Value(std::move(dateParams))).GetStorage(); if (!timeParams.empty()) { + if (!timeParams.count("hour") && !timeParams.count("time")) { + timeParams["hour"] = Value::Integer(0); + } nanoseconds_since_begin_of_day = LocalTime(Value(std::move(timeParams))).GetStorage(); } diff --git a/src/common/temporal/temporal_pattern.h b/src/common/temporal/temporal_pattern.h index 0f6a63bcc9..d1434b9282 100644 --- a/src/common/temporal/temporal_pattern.h +++ b/src/common/temporal/temporal_pattern.h @@ -53,6 +53,7 @@ enum DATE_PATTERN_GROUP { const std::string DATE_TIMEZONE = "timezone"; const std::string DATE_DATE = "date"; +const std::string DATE_DATETIME = "datetime"; const std::string DATE_YEAR = "year"; const std::string DATE_MONTH = "month"; const std::string DATE_DAY = "day"; @@ -63,7 +64,7 @@ const std::string DATE_DOQ = "dayofquarter"; const std::string DATE_ORDINAL = "ordinalday"; const std::unordered_set validDateFields{ - DATE_TIMEZONE, DATE_DATE, DATE_YEAR, DATE_MONTH, DATE_DAY, + DATE_TIMEZONE, DATE_DATE, DATE_DATETIME, DATE_YEAR, DATE_MONTH, DATE_DAY, DATE_WEEK, DATE_DOW, DATE_QUARTER, DATE_DOQ, DATE_ORDINAL}; const std::string OFFSET_PATTERN = "(Z|[+-]([0-9]{2})(?::?([0-9]{2}))?(?::?([0-9]{2}))?)"; diff --git a/src/common/temporal/time.cpp b/src/common/temporal/time.cpp index c2fcaf49c5..65a0b103ce 100644 --- a/src/common/temporal/time.cpp +++ b/src/common/temporal/time.cpp @@ -30,7 +30,7 @@ Time::Time() { nanoseconds_since_today_with_of_ = t.get_local_time().time_since_epoch().count(); } -Time::Time(const std::string& str) { +Time::Time(const std::string& str, std::optional days) { int64_t hour = 0, minute = 0, second = 0, nanoseconds = 0; int64_t zoneHour = 0, zoneMinute = 0, zoneSecond = 0; std::smatch match; @@ -82,11 +82,6 @@ Time::Time(const std::string& str) { if (match[TIME_ZONE].matched) { auto tmp = match[TIME_ZONE].str(); if (tmp != "Z" && tmp != "z") { - if (match[TIME_ZONE_NAME].matched) { - THROW_CODE(InvalidParameter, "Using a named time zone e.g. " - "[UTC] is not valid for a time without a date. Instead, " - "use a specific time zone string e.g. +00:00."); - } if (match[TIME_ZONE_HOUR].matched) { zoneHour = std::stoi(match[TIME_ZONE_HOUR].str()); } @@ -102,6 +97,17 @@ Time::Time(const std::string& str) { } } } + if (match[TIME_ZONE_NAME].matched) { + auto tmp = match[TIME_ZONE_NAME].str(); + std::replace(tmp.begin(), tmp.end(), ' ', '_'); + timezone_name_ = tmp; + if (!days.has_value()) { + tz_offset_seconds_ = date::zoned_time(tmp).get_info().offset.count(); + } else { + tz_offset_seconds_ = date::zoned_time(tmp, date::local_time( + std::chrono::nanoseconds(NANOS_PER_SECOND * SECONDS_PER_DAY * days.value()))).get_info().offset.count(); + } + } std::chrono::nanoseconds ns{hour * 60 * 60 * 1000000000 + minute * 60 * 1000000000 + second * 1000000000 + nanoseconds}; date::local_time tp{ns}; auto t = make_zoned(date::current_zone(), tp); @@ -151,15 +157,27 @@ Time::Time(const Value& params, int64_t truncate) { } else if (parse_params_map["time"].IsTime()) { has_time_param = true; v = std::get<0>(parse_params_map["time"].AsTime().GetStorage()); - tz_offset_seconds_ = time_param_offset_second = std::get<1>(parse_params_map["time"].AsTime().GetStorage()); timezone_name_ = parse_params_map["time"].AsTime().GetTimezoneName(); + if (parse_params_map.count("days_for_timezone") && timezone_name_[0] != '+' && timezone_name_[0] != '-') { + tz_offset_seconds_ = time_param_offset_second = date::zoned_time(timezone_name_, date::local_time( + std::chrono::nanoseconds(NANOS_PER_SECOND * SECONDS_PER_DAY * parse_params_map["days_for_timezone"].AsInteger()))).get_info().offset.count(); + } else { + tz_offset_seconds_ = time_param_offset_second = std::get<1>(parse_params_map["time"].AsTime().GetStorage()); + } } else if (parse_params_map["time"].IsLocalDateTime()) { v = parse_params_map["time"].AsLocalDateTime().GetStorage(); } else if (parse_params_map["time"].IsDateTime()) { has_time_param = true; v = std::get<0>(parse_params_map["time"].AsDateTime().GetStorage()); - tz_offset_seconds_ = time_param_offset_second = std::get<1>(parse_params_map["time"].AsDateTime().GetStorage()); timezone_name_ = parse_params_map["time"].AsDateTime().GetTimezoneName(); + if (parse_params_map.count("days_for_timezone") && timezone_name_[0] != '+' && timezone_name_[0] != '-') { + tz_offset_seconds_ = time_param_offset_second = date::zoned_time(timezone_name_, date::local_time( + std::chrono::nanoseconds(NANOS_PER_SECOND * SECONDS_PER_DAY * parse_params_map["days_for_timezone"].AsInteger()))).get_info().offset.count(); + } else { + tz_offset_seconds_ = time_param_offset_second = std::get<1>(parse_params_map["time"].AsDateTime().GetStorage()); + } + } else { + THROW_CODE(InvalidParameter, "Cannot get the time of: {}", parse_params_map["time"].ToString()); } nanosecond = v % 1000; microsecond = v / 1000 % 1000; @@ -205,7 +223,7 @@ Time::Time(const Value& params, int64_t truncate) { zoneMinute = std::stoi(match[3].str()); } if (match[4].matched) { - zoneSecond = std::stoi(match[3].str()); + zoneSecond = std::stoi(match[4].str()); } tz_offset_seconds_ = zoneHour * 60 * 60 + zoneMinute * 60 + zoneSecond; if (tmp[0] == '-') { diff --git a/src/common/temporal/time.h b/src/common/temporal/time.h index 01b5494c9f..e7b6885340 100644 --- a/src/common/temporal/time.h +++ b/src/common/temporal/time.h @@ -32,7 +32,7 @@ class Time { public: Time(); - explicit Time(const std::string& str); + explicit Time(const std::string& str, std::optional days = {}); explicit Time(const Value& params, int64_t truncate = 0); explicit Time(int64_t nanoseconds, int64_t tz_offset_seconds_ = 0) : nanoseconds_since_today_with_of_(nanoseconds), tz_offset_seconds_(tz_offset_seconds_){}; diff --git a/src/common/value.cpp b/src/common/value.cpp index 28503229e1..154710d24f 100644 --- a/src/common/value.cpp +++ b/src/common/value.cpp @@ -398,13 +398,23 @@ std::any Value::ToBolt() const { } case ValueType::DATETIME: { auto s = std::any_cast(data); - return bolt::DateTime{std::get<0>(s.GetStorage()) / 1000000000, - std::get<0>(s.GetStorage()) % 1000000000, - std::get<1>(s.GetStorage())}; + if (std::get<0>(s.GetStorage()) % 1000000000 >= 0) { + return bolt::DateTime{std::get<0>(s.GetStorage()) / 1000000000, + std::get<0>(s.GetStorage()) % 1000000000, + std::get<1>(s.GetStorage())}; + } else { + return bolt::DateTime{std::get<0>(s.GetStorage()) / 1000000000 - 1, + std::get<0>(s.GetStorage()) % 1000000000 + 1000000000, + std::get<1>(s.GetStorage())}; + } } case ValueType::LOCALDATETIME: { auto s = std::any_cast(data).GetStorage(); - return bolt::LocalDateTime{s / 1000000000, s % 1000000000}; + if (s % 1000000000 >= 0) { + return bolt::LocalDateTime{s / 1000000000, s % 1000000000}; + } else { + return bolt::LocalDateTime{s / 1000000000 - 1, s % 1000000000 + 1000000000}; + } } case ValueType::LOCALTIME: { return bolt::LocalTime{std::any_cast(data).GetStorage()}; diff --git a/src/cypher/arithmetic/arithmetic_expression.cpp b/src/cypher/arithmetic/arithmetic_expression.cpp index a29f396a0a..77fe406807 100644 --- a/src/cypher/arithmetic/arithmetic_expression.cpp +++ b/src/cypher/arithmetic/arithmetic_expression.cpp @@ -683,7 +683,8 @@ Value BuiltinFunction::Date(RTContext *ctx, const Record &record, CYPHER_THROW_ASSERT(args.size() == 2); // date(string) Returns a Date by parsing a string. auto r = args[1].Evaluate(ctx, record); - if (r.IsMap()) { + if (r.IsMap() || (r.IsConstant() && (r.constant.IsDate() || r.constant.IsDateTime() + || r.constant.IsLocalDateTime()))) { auto dt = common::Date(r.constant); return Value(dt); } else if (r.IsString()) { @@ -705,7 +706,7 @@ Value BuiltinFunction::LocalDateTime(RTContext *ctx, const Record &record, CYPHER_THROW_ASSERT(args.size() == 2); // datetime(string) Returns a DateTime by parsing a string. auto r = args[1].Evaluate(ctx, record); - if (r.IsMap()) { + if (r.IsMap() || (r.IsConstant() && (r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { auto dt = common::LocalDateTime(r.constant); return Value(dt); } else if (r.IsString()) { @@ -727,7 +728,7 @@ Value BuiltinFunction::DateTime(RTContext *ctx, const Record &record, CYPHER_THROW_ASSERT(args.size() == 2); // datetime(string) Returns a DateTime by parsing a string. auto r = args[1].Evaluate(ctx, record); - if (r.IsMap()) { + if (r.IsMap() || (r.IsConstant() && (r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { auto dt = common::DateTime(r.constant); return Value(dt); } else if (r.IsString()) { From fdb306dca16e09a6a991b2bb9214e51a0d7f8bce Mon Sep 17 00:00:00 2001 From: lipanpan03 <656461146@qq.com> Date: Thu, 5 Dec 2024 19:14:36 +0800 Subject: [PATCH 2/3] fix null --- src/common/value.cpp | 17 +++++++++-- .../arithmetic/arithmetic_expression.cpp | 28 ++++++++++++++++--- src/cypher/arithmetic/arithmetic_expression.h | 6 ++-- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/common/value.cpp b/src/common/value.cpp index 154710d24f..4086dad142 100644 --- a/src/common/value.cpp +++ b/src/common/value.cpp @@ -150,6 +150,14 @@ std::string Value::Serialize() const { buffer.append((const char*)&offset, sizeof(offset)); break; } + case ValueType::DATETIME: { + auto& v = std::any_cast(data); + int64_t nanosecond = std::get<0>(v.GetStorage()); + int64_t offset = std::get<1>(v.GetStorage()); + buffer.append((const char*)&nanosecond, sizeof(nanosecond)); + buffer.append((const char*)&offset, sizeof(offset)); + break; + } case ValueType::DURATION: { auto& v = std::any_cast(data); int64_t months = v.months; @@ -284,12 +292,17 @@ void Value::Deserialize(const char* p, size_t size) { } case ValueType::TIME: { assert(size == sizeof(int64_t) * 2); - data = common::Time(*(int64_t*)p, *(int64_t*)(p+1)); + data = common::Time(*(int64_t*)p, *(int64_t*)(p+8)); + break; + } + case ValueType::DATETIME: { + assert(size == sizeof(int64_t) * 2); + data = common::DateTime(*(int64_t*)p, *(int64_t*)(p+8)); break; } case ValueType::DURATION: { assert(size == sizeof(int64_t) * 4); - data = common::Duration(*(int64_t*)p, *(int64_t*)(p+1), *(int64_t*)(p+2), *(int64_t*)(p+3)); + data = common::Duration(*(int64_t*)p, *(int64_t*)(p+8), *(int64_t*)(p+16), *(int64_t*)(p+24)); break; } default: { diff --git a/src/cypher/arithmetic/arithmetic_expression.cpp b/src/cypher/arithmetic/arithmetic_expression.cpp index b176c9a4b5..dda014da7d 100644 --- a/src/cypher/arithmetic/arithmetic_expression.cpp +++ b/src/cypher/arithmetic/arithmetic_expression.cpp @@ -703,7 +703,9 @@ Value BuiltinFunction::Date(RTContext *ctx, const Record &record, CYPHER_THROW_ASSERT(args.size() == 2); // date(string) Returns a Date by parsing a string. auto r = args[1].Evaluate(ctx, record); - if (r.IsMap() || (r.IsConstant() && (r.constant.IsDate() || r.constant.IsDateTime() + if (r.IsNull()) { + return {}; + } else if (r.IsMap() || (r.IsConstant() && (r.constant.IsDate() || r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { auto dt = common::Date(r.constant); return Value(dt); @@ -726,7 +728,9 @@ Value BuiltinFunction::LocalDateTime(RTContext *ctx, const Record &record, CYPHER_THROW_ASSERT(args.size() == 2); // datetime(string) Returns a DateTime by parsing a string. auto r = args[1].Evaluate(ctx, record); - if (r.IsMap() || (r.IsConstant() && (r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { + if (r.IsNull()) { + return {}; + } else if (r.IsMap() || (r.IsConstant() && (r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { auto dt = common::LocalDateTime(r.constant); return Value(dt); } else if (r.IsString()) { @@ -748,7 +752,9 @@ Value BuiltinFunction::DateTime(RTContext *ctx, const Record &record, CYPHER_THROW_ASSERT(args.size() == 2); // datetime(string) Returns a DateTime by parsing a string. auto r = args[1].Evaluate(ctx, record); - if (r.IsMap() || (r.IsConstant() && (r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { + if (r.IsNull()) { + return {}; + } else if (r.IsMap() || (r.IsConstant() && (r.constant.IsDateTime() || r.constant.IsLocalDateTime()))) { auto dt = common::DateTime(r.constant); return Value(dt); } else if (r.IsString()) { @@ -859,31 +865,45 @@ Value BuiltinFunction::TimeTruncate(RTContext *ctx, const Record &record, const Value BuiltinFunction::DurationBetween(RTContext *ctx, const Record &record, const std::vector &args) { CYPHER_THROW_ASSERT(args.size() == 3); auto from = args[1].Evaluate(ctx, record), to = args[2].Evaluate(ctx, record); + if (from.IsNull() || to.IsNull()) { + return {}; + } return Value::Duration(common::Duration::between(from.constant, to.constant)); } Value BuiltinFunction::DurationInSeconds(RTContext *ctx, const Record &record, const std::vector &args) { CYPHER_THROW_ASSERT(args.size() == 3); auto from = args[1].Evaluate(ctx, record), to = args[2].Evaluate(ctx, record); + if (from.IsNull() || to.IsNull()) { + return {}; + } return Value::Duration(common::Duration::between(from.constant, to.constant, "SECOND")); } Value BuiltinFunction::DurationInDays(RTContext *ctx, const Record &record, const std::vector &args) { CYPHER_THROW_ASSERT(args.size() == 3); auto from = args[1].Evaluate(ctx, record), to = args[2].Evaluate(ctx, record); + if (from.IsNull() || to.IsNull()) { + return {}; + } return Value::Duration(common::Duration::between(from.constant, to.constant, "DAY")); } Value BuiltinFunction::DurationInMonths(RTContext *ctx, const Record &record, const std::vector &args) { CYPHER_THROW_ASSERT(args.size() == 3); auto from = args[1].Evaluate(ctx, record), to = args[2].Evaluate(ctx, record); + if (from.IsNull() || to.IsNull()) { + return {}; + } return Value::Duration(common::Duration::between(from.constant, to.constant, "MONTH")); } Value BuiltinFunction::Duration(RTContext *ctx, const Record &record, const std::vector &args) { CYPHER_THROW_ASSERT(args.size() == 2); auto r = args[1].Evaluate(ctx, record); - if (r.IsMap()) { + if (r.IsNull()) { + return {}; + } else if (r.IsMap()) { auto dt = common::Duration(r.constant); return Value(dt); } else if (r.IsString()) { diff --git a/src/cypher/arithmetic/arithmetic_expression.h b/src/cypher/arithmetic/arithmetic_expression.h index 381bd3e4d2..ca2e2776f4 100644 --- a/src/cypher/arithmetic/arithmetic_expression.h +++ b/src/cypher/arithmetic/arithmetic_expression.h @@ -545,9 +545,9 @@ struct ArithOpNode { ae_registered_funcs.emplace("localtime.truncate", BuiltinFunction::LocalTimeTruncate); ae_registered_funcs.emplace("time.truncate", BuiltinFunction::TimeTruncate); ae_registered_funcs.emplace("duration.between", BuiltinFunction::DurationBetween); - ae_registered_funcs.emplace("duration.inSeconds", BuiltinFunction::DurationInSeconds); - ae_registered_funcs.emplace("duration.inDays", BuiltinFunction::DurationInDays); - ae_registered_funcs.emplace("duration.inMonths", BuiltinFunction::DurationInMonths); + ae_registered_funcs.emplace("duration.inseconds", BuiltinFunction::DurationInSeconds); + ae_registered_funcs.emplace("duration.indays", BuiltinFunction::DurationInDays); + ae_registered_funcs.emplace("duration.inmonths", BuiltinFunction::DurationInMonths); ae_registered_funcs.emplace("duration.between", BuiltinFunction::TimeTruncate); ae_registered_funcs.emplace("datetime.fromepoch", BuiltinFunction::DateTimeFromEpoch); From 9f22ac44af621450c2a39ef68e7a342af2895d4a Mon Sep 17 00:00:00 2001 From: lipanpan03 <656461146@qq.com> Date: Fri, 6 Dec 2024 10:20:34 +0800 Subject: [PATCH 3/3] fix time compare --- src/common/temporal/time.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/temporal/time.cpp b/src/common/temporal/time.cpp index 65a0b103ce..e7e826002f 100644 --- a/src/common/temporal/time.cpp +++ b/src/common/temporal/time.cpp @@ -372,27 +372,27 @@ Value Time::GetUnit(std::string unit) const { } bool Time::operator<(const Time& rhs) const noexcept { - return nanoseconds_since_today_with_of_ < rhs.nanoseconds_since_today_with_of_; + return nanoseconds_since_today_with_of_ - tz_offset_seconds_ * NANOS_PER_SECOND < rhs.nanoseconds_since_today_with_of_ - rhs.tz_offset_seconds_ * NANOS_PER_SECOND; } bool Time::operator<=(const Time& rhs) const noexcept { - return nanoseconds_since_today_with_of_ <= rhs.nanoseconds_since_today_with_of_; + return nanoseconds_since_today_with_of_ - tz_offset_seconds_ * NANOS_PER_SECOND <= rhs.nanoseconds_since_today_with_of_ - rhs.tz_offset_seconds_ * NANOS_PER_SECOND; } bool Time::operator>(const Time& rhs) const noexcept { - return nanoseconds_since_today_with_of_ > rhs.nanoseconds_since_today_with_of_; + return nanoseconds_since_today_with_of_ - tz_offset_seconds_ * NANOS_PER_SECOND > rhs.nanoseconds_since_today_with_of_ - rhs.tz_offset_seconds_ * NANOS_PER_SECOND; } bool Time::operator>=(const Time& rhs) const noexcept { - return nanoseconds_since_today_with_of_ >= rhs.nanoseconds_since_today_with_of_; + return nanoseconds_since_today_with_of_ - tz_offset_seconds_ * NANOS_PER_SECOND >= rhs.nanoseconds_since_today_with_of_ - rhs.tz_offset_seconds_ * NANOS_PER_SECOND; } bool Time::operator==(const Time& rhs) const noexcept { - return nanoseconds_since_today_with_of_ == rhs.nanoseconds_since_today_with_of_; + return nanoseconds_since_today_with_of_ - tz_offset_seconds_ * NANOS_PER_SECOND == rhs.nanoseconds_since_today_with_of_ - rhs.tz_offset_seconds_ * NANOS_PER_SECOND; } bool Time::operator!=(const Time& rhs) const noexcept { - return nanoseconds_since_today_with_of_ != rhs.nanoseconds_since_today_with_of_; + return nanoseconds_since_today_with_of_ - tz_offset_seconds_ * NANOS_PER_SECOND != rhs.nanoseconds_since_today_with_of_ - rhs.tz_offset_seconds_ * NANOS_PER_SECOND; } Time Time::operator-(const Duration& duration) const {