Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
92488ee
Date-Date operator overload
Jan 1, 2026
50b6665
ValueGetter for NumericOrDate and extended Subtract Expression
Jan 7, 2026
c6dbba5
ValueGetter for NumericOrDate and extended Subtract Expression
Jan 7, 2026
df9d950
Merge branch 'DateTimeSubtraction' of https://github.com/yarox-1/qlev…
yarox-1 Jan 7, 2026
b831b91
Deleted MakeNumericOrDateExpression & now returning ValueId in Subtra…
Jan 10, 2026
59a5d84
working subtraction for Date - Date; Time not supported yet
Jan 13, 2026
38dad7b
Test and DateTime - DateTime operator overload
Jan 16, 2026
ab0b2ff
Merge branch 'ad-freiburg:master' into DateTimeSubtraction
yarox-1 Jan 16, 2026
3698083
some small changes in comments
Jan 16, 2026
5f4a885
debug output
Jan 19, 2026
435c214
Subtraction is now working for date1 - date2 with date1.year >= date2…
Jan 19, 2026
6aa7bc3
Merge branch 'ad-freiburg:master' into DateTimeSubtraction
yarox-1 Jan 23, 2026
598c0fa
removed debug logs
Jan 23, 2026
f40809d
fixed bug, where date subtraction gave duration negative arguments
Jan 23, 2026
4b5d95c
fixed sonar issues
Jan 23, 2026
6e9b66d
renaming for NumericOrDateValueGetter
Jan 27, 2026
c934689
fixed bug in Date Subtraction
Jan 28, 2026
6c1b058
Added Tests for DateSubtraction to ExpressionTests
Jan 28, 2026
eadb522
added Tests for NumericOrDateValueGetter | needed to add operator== t…
yarox-1 Jan 28, 2026
0fd36fd
Merge branch 'ad-freiburg:master' into DateTimeSubtraction
yarox-1 Jan 28, 2026
7a7cb15
Update src/engine/sparqlExpressions/SparqlExpressionValueGetters.h
yarox-1 Jan 30, 2026
0cd0d3f
Update src/util/DateYearDuration.h
yarox-1 Jan 30, 2026
4c1d239
Update src/util/DateYearDuration.h
yarox-1 Jan 30, 2026
792d808
ValueGetterTested fixed
yarox-1 Jan 30, 2026
18efa1a
grouped DateYearDuration Subtraction Tests
yarox-1 Jan 30, 2026
6c1c4bb
Added lambda function to remove redundand Code from SparqlExpressionTest
yarox-1 Jan 30, 2026
0f78720
added 'isLongYear()'; Date operator- now returns std::optional; shoul…
yarox-1 Feb 3, 2026
a0cf10f
Merge branch 'ad-freiburg:master' into DateTimeSubtraction
yarox-1 Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions src/engine/sparqlExpressions/NumericBinaryExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,51 @@ NARY_EXPRESSION(DivideExpressionByZeroIsNan, 2,
using Add = MakeNumericExpression<std::plus<>>;
NARY_EXPRESSION(AddExpression, 2, FV<Add, NumericValueGetter>);

using Subtract = MakeNumericExpression<std::minus<>>;
NARY_EXPRESSION(SubtractExpression, 2, FV<Subtract, NumericValueGetter>);
// _____________________________________________________________________________
// Subtract.
struct SubtractImpl {
ValueId operator()(NumericOrDateValue lhs, NumericOrDateValue rhs) const {
return std::visit(SubtractImpl{}, lhs, rhs);
}

CPP_template(typename L, typename R)(
requires(!std::is_same_v<L, NumericOrDateValue>
CPP_and !std::is_same_v<R, NumericOrDateValue>)) ValueId
operator()(L lhs, R rhs) const {
using T1 = std::decay_t<decltype(lhs)>;
using T2 = std::decay_t<decltype(rhs)>;

if constexpr (std::is_same_v<T1, double>) {
if constexpr (std::is_same_v<T2, double>) {
return Id::makeFromDouble(lhs - rhs);
} else if constexpr (std::is_same_v<T2, int64_t>) {
return Id::makeFromDouble(lhs - static_cast<double>(rhs));
}
} else if constexpr (std::is_same_v<T1, int64_t>) {
if constexpr (std::is_same_v<T2, double>) {
return Id::makeFromDouble(static_cast<double>(lhs) - rhs);
} else if constexpr (std::is_same_v<T2, int64_t>) {
return Id::makeFromInt(lhs - rhs);
}
} else if constexpr (std::is_same_v<T1, DateYearOrDuration> &&
std::is_same_v<T2, DateYearOrDuration>) {
#ifndef REDUCED_FEATURE_SET_FOR_CPP17
// Using - operator implementation in DateYearOrDuration.
auto difference = lhs - rhs;
if (difference.has_value()) {
return Id::makeFromDate(difference.value());
} else {
return Id::makeUndefined();
}
#endif
}
// For all other operations returning Undefined
// It is not allowed to use subtractionn between Date and NumericValue
return Id::makeUndefined();
}
};
NARY_EXPRESSION(SubtractExpression, 2,
FV<SubtractImpl, NumericOrDateValueGetter>);

// _____________________________________________________________________________
// Power.
Expand Down
29 changes: 29 additions & 0 deletions src/engine/sparqlExpressions/SparqlExpressionValueGetters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,35 @@ NumericValue NumericValueGetter::operator()(
AD_FAIL();
}

// _____________________________________________________________________________
NumericOrDateValue NumericOrDateValueGetter::operator()(
ValueId id, const sparqlExpression::EvaluationContext*) const {
switch (id.getDatatype()) {
case Datatype::Double:
return id.getDouble();
case Datatype::Int:
return id.getInt();
case Datatype::Bool:
// TODO<joka921> Check in the specification what the correct behavior is
// here. They probably should be UNDEF as soon as we have conversion
// functions.
return static_cast<int64_t>(id.getBool());
case Datatype::Undefined:
case Datatype::EncodedVal:
case Datatype::VocabIndex:
case Datatype::LocalVocabIndex:
case Datatype::TextRecordIndex:
case Datatype::WordVocabIndex:
return NotNumeric{};
case Datatype::Date:
return id.getDate();
case Datatype::GeoPoint:
case Datatype::BlankNodeIndex:
return NotNumeric{};
}
AD_FAIL();
}

// _____________________________________________________________________________
auto EffectiveBooleanValueGetter::operator()(
ValueId id, const EvaluationContext* context) const -> Result {
Expand Down
22 changes: 21 additions & 1 deletion src/engine/sparqlExpressions/SparqlExpressionValueGetters.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@ using Iri = ad_utility::triple_component::Iri;

// An empty struct to represent a non-numeric value in a context where only
// numeric values make sense.
struct NotNumeric {};
struct NotNumeric {
bool operator==(const NotNumeric&) const noexcept = default;
};
// The input to an expression that expects a numeric value.
using NumericValue = std::variant<NotNumeric, double, int64_t>;
using IntOrDouble = std::variant<double, int64_t>;
// The input to an expression that expects a numeric value or a date.
// Will be used in `NumericBinaryExpressions.cpp` to allow for subtraction of
// Dates.
using NumericOrDateValue =
std::variant<NotNumeric, double, int64_t, DateYearOrDuration>;

// Return type for `DatatypeValueGetter`.
using LiteralOrString =
Expand Down Expand Up @@ -102,6 +109,19 @@ struct NumericValueGetter : Mixin<NumericValueGetter> {
NumericValue operator()(ValueId id, const EvaluationContext*) const;
};

// Return `NumericOrDateValue` which is then used as the input to numeric
// expressions.
struct NumericOrDateValueGetter : Mixin<NumericOrDateValueGetter> {
using Mixin<NumericOrDateValueGetter>::operator();
// same as in `NumericValueGetter`
NumericOrDateValue operator()(const LiteralOrIri&,
const EvaluationContext*) const {
return NotNumeric{};
}

NumericOrDateValue operator()(ValueId id, const EvaluationContext*) const;
};

/// Return the type exactly as it was passed in.
/// This class is needed for the distinct calculation in the aggregates.
struct ActualValueGetter {
Expand Down
133 changes: 133 additions & 0 deletions src/util/DateYearDuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,136 @@ std::optional<DateYearOrDuration> DateYearOrDuration::convertToXsdDate(
return DateYearOrDuration(
Date(date.getYear(), date.getMonth(), date.getDay()));
}

// _____________________________________________________________________________
#ifndef REDUCED_FEATURE_SET_FOR_CPP17
void updatePassedTimes(const Date& date1, const Date& date2, long& daysPassed,
int& hoursPassed, int& minutesPassed,
double& secondsPassed) {
// helper function for Subtraction
// this function allows to swap the dates, if daysPassed was negative before
// applying abs. updating daysPassed, hoursPassed, minutesPassed,
// secondsPassed accordingly
if (date1.hasTime()) {
int hour1 = date1.getHour();
int minute1 = date1.getMinute();
double second1 = date1.getSecond();
if (date2.hasTime()) {
int hour2 = date2.getHour();
int minute2 = date2.getMinute();
double second2 = date2.getSecond();
if (hour1 <= hour2) {
if (daysPassed > 0) {
daysPassed--; // counted one day to much
hoursPassed =
24 - (hour2 - hour1); // total hours of a day - difference
} else {
hoursPassed = (hour2 - hour1);
}
} else {
hoursPassed = (hour1 - hour2);
}
if (minute1 <= minute2) {
if (hoursPassed > 0) {
hoursPassed--; // same as above just one level down
minutesPassed = 60 - (minute2 - minute1);
} else {
minutesPassed = (minute2 - minute1);
}
} else {
minutesPassed = (minute1 - minute2);
}
if (second1 <= second2) {
if (minutesPassed > 0) {
minutesPassed--;
secondsPassed = 60 - (second2 - second1);
} else {
secondsPassed = (second2 - second1);
}
} else {
secondsPassed = (second1 - second2);
}
} else {
// if there is no time given, assume 00:00h 0seconds
hoursPassed = hour1;
minutesPassed = minute1;
secondsPassed = second1;
}
} else {
// date1 has no time, therefore we are assuming time 00:00:00
if (date2.hasTime()) {
int hour2 = date2.getHour();
int minute2 = date2.getMinute();
double second2 = date2.getSecond();
daysPassed--;
secondsPassed = 60.0 - second2;
minutesPassed =
60 -
(minute2 + (secondsPassed > 0.0
? 1
: 0)); // we add 1 because the seconds added a minute
hoursPassed = 24 - (hour2 + (minutesPassed > 0 ? 1 : 0));
}
}
}

std::optional<DateYearOrDuration> DateYearOrDuration::operator-(
const DateYearOrDuration& rhs) const {
if (isDate() && rhs.isDate()) {
// Date - Date => Duration | getting time between the two Dates
const Date& ownDate = getDateUnchecked();
const Date& otherDate = rhs.getDateUnchecked();

// Calculate number of days between the two Dates
auto date1 =
std::chrono::year_month_day{std::chrono::year(ownDate.getYear()) /
ownDate.getMonth() / ownDate.getDay()};
auto date2 =
std::chrono::year_month_day{std::chrono::year(otherDate.getYear()) /
otherDate.getMonth() / otherDate.getDay()};

long daysPassed =
(std::chrono::sys_days{date1} - std::chrono::sys_days{date2}).count();
int hoursPassed = 0;
int minutesPassed = 0;
double secondsPassed = 0.0;

bool isDaysPassedPos = true;

if (daysPassed < 0) {
isDaysPassedPos = false;
daysPassed = abs(daysPassed);
}
// Calculate time passed between the two Dates if at least one of them has a
// Time.
if (isDaysPassedPos) {
updatePassedTimes(ownDate, otherDate, daysPassed, hoursPassed,
minutesPassed, secondsPassed);
} else {
updatePassedTimes(otherDate, ownDate, daysPassed, hoursPassed,
minutesPassed, secondsPassed);
}
return DateYearOrDuration(DayTimeDuration(DayTimeDuration::Type::Positive,
daysPassed, hoursPassed,
minutesPassed, secondsPassed));
}

if (isDayTimeDuration() && rhs.isDayTimeDuration()) {
// Duration - Duration => Duration | getting new duration that is
// rhs.duration-time smaller return;
// TODO: can be implemented
}

if (isDate() && rhs.isDayTimeDuration()) {
// Date - Duration => Date | getting new Date from rhs.duration-time earlier
// TODO: can be implemented
}

// TODO: subtraction with large year can also be implemented

// Duration - Date is not implemented

// no viable subtraction
return std::nullopt;
}
#endif
13 changes: 13 additions & 0 deletions src/util/DateYearDuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class DateYearOrDuration {
// True iff a complete `Date` is stored and not only a large year.
bool isDate() const { return bits_ >> numPayloadDateBits == datetime; }

// True iff a large year is stored.
bool isLongYear() const {
return (bits_ >> numPayloadDateBits == negativeYear) ||
(bits_ >> numPayloadDateBits == positiveYear);
}

// True iff constructed with `DayTimeDuration`.
bool isDayTimeDuration() const {
return bits_ >> numPayloadDurationBits == daytimeDuration;
Expand Down Expand Up @@ -205,6 +211,13 @@ class DateYearOrDuration {
// std::nullopt.
static std::optional<DateYearOrDuration> convertToXsdDate(
const DateYearOrDuration& dateValue);

#ifndef REDUCED_FEATURE_SET_FOR_CPP17
// Subtraction of two DateYearOrDuration Objects.
// For undefined subtractions `std::nullopt` is returned.
[[nodiscard]] std::optional<DateYearOrDuration> operator-(
const DateYearOrDuration& rhs) const;
#endif
};
#ifdef QLEVER_CPP_17
static_assert(std::is_default_constructible_v<DateYearOrDuration>);
Expand Down
Loading
Loading