Skip to content

Commit ea10f71

Browse files
authored
Merge pull request #1061 from Akash-Sharma-1/master
Fix for issue #629 - Arithmetic Operations of DateTime in SPARQL
2 parents 7289f68 + f5b7b56 commit ea10f71

File tree

3 files changed

+359
-14
lines changed

3 files changed

+359
-14
lines changed

rdflib/plugins/sparql/datatypes.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
)
3030
)
3131

32+
### adding dateTime datatypes
33+
34+
XSD_DateTime_DTs = set(
35+
(XSD.dateTime, XSD.date, XSD.time)
36+
)
37+
38+
XSD_Duration_DTs = set(
39+
(XSD.duration, XSD.dayTimeDuration, XSD.yearMonthDuration)
40+
)
41+
3242
_sub_types = {
3343
XSD.integer: [
3444
XSD.nonPositiveInteger,

rdflib/plugins/sparql/operators.py

Lines changed: 130 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from rdflib.plugins.sparql.parserutils import CompValue, Expr
2525
from rdflib.plugins.sparql.datatypes import XSD_DTs, type_promotion
26+
from rdflib.plugins.sparql.datatypes import XSD_DateTime_DTs, XSD_Duration_DTs
2627
from rdflib import URIRef, BNode, Variable, Literal, XSD, RDF
2728
from rdflib.term import Node
2829

@@ -760,25 +761,67 @@ def AdditiveExpression(e, ctx):
760761
if other is None:
761762
return expr
762763

763-
res = numeric(expr)
764+
# handling arithmetic(addition/subtraction) of dateTime, date, time
765+
# and duration datatypes (if any)
766+
if hasattr(expr, 'datatype') and (expr.datatype in XSD_DateTime_DTs or expr.datatype in XSD_Duration_DTs):
767+
768+
res = dateTimeObjects(expr)
769+
dt = expr.datatype
770+
771+
for op, term in zip(e.op, other):
772+
773+
# check if operation is datetime,date,time operation over
774+
# another datetime,date,time datatype
775+
if dt in XSD_DateTime_DTs and dt == term.datatype and op == '-':
776+
# checking if there are more than one datetime operands -
777+
# in that case it doesn't make sense for example
778+
# ( dateTime1 - dateTime2 - dateTime3 ) is an invalid operation
779+
if len(other) > 1:
780+
error_message = "Can't evaluate multiple %r arguments"
781+
raise SPARQLError(error_message, dt.datatype)
782+
else:
783+
n = dateTimeObjects(term)
784+
res = calculateDuration(res, n)
785+
return res
786+
787+
# datetime,date,time +/- duration,dayTimeDuration,yearMonthDuration
788+
elif (dt in XSD_DateTime_DTs and term.datatype in XSD_Duration_DTs):
789+
n = dateTimeObjects(term)
790+
res = calculateFinalDateTime(res, dt, n, term.datatype, op)
791+
return res
792+
793+
# duration,dayTimeDuration,yearMonthDuration + datetime,date,time
794+
elif dt in XSD_Duration_DTs and term.datatype in XSD_DateTime_DTs:
795+
if op == "+":
796+
n = dateTimeObjects(term)
797+
res = calculateFinalDateTime(res, dt, n, term.datatype, op)
798+
return res
799+
800+
# rest are invalid types
801+
else:
802+
raise SPARQLError('Invalid DateTime Operations')
803+
804+
# handling arithmetic(addition/subtraction) of numeric datatypes (if any)
805+
else:
806+
res = numeric(expr)
764807

765-
dt = expr.datatype
808+
dt = expr.datatype
766809

767-
for op, term in zip(e.op, other):
768-
n = numeric(term)
769-
if isinstance(n, Decimal) and isinstance(res, float):
770-
n = float(n)
771-
if isinstance(n, float) and isinstance(res, Decimal):
772-
res = float(res)
810+
for op, term in zip(e.op, other):
811+
n = numeric(term)
812+
if isinstance(n, Decimal) and isinstance(res, float):
813+
n = float(n)
814+
if isinstance(n, float) and isinstance(res, Decimal):
815+
res = float(res)
773816

774-
dt = type_promotion(dt, term.datatype)
817+
dt = type_promotion(dt, term.datatype)
775818

776-
if op == "+":
777-
res += n
778-
else:
779-
res -= n
819+
if op == "+":
820+
res += n
821+
else:
822+
res -= n
780823

781-
return Literal(res, datatype=dt)
824+
return Literal(res, datatype=dt)
782825

783826

784827
def RelationalExpression(e, ctx):
@@ -1001,6 +1044,79 @@ def numeric(expr):
10011044
return expr.toPython()
10021045

10031046

1047+
def dateTimeObjects(expr):
1048+
"""
1049+
return a dataTime/date/time/duration/dayTimeDuration/yearMonthDuration python objects from a literal
1050+
1051+
"""
1052+
return expr.toPython()
1053+
1054+
1055+
def isCompatibleDateTimeDatatype(obj1, dt1, obj2, dt2):
1056+
"""
1057+
returns a boolean indicating if first object is compatible
1058+
with operation(+/-) over second object.
1059+
1060+
"""
1061+
if(dt1 == XSD.date):
1062+
if(dt2 == XSD.yearMonthDuration):
1063+
return True
1064+
elif(dt2 == XSD.dayTimeDuration or dt2 == XSD.Duration):
1065+
# checking if the dayTimeDuration has no Time Component
1066+
# else it wont be compatible with Date Literal
1067+
if("T" in str(obj2)):
1068+
return False
1069+
else:
1070+
return True
1071+
1072+
if(dt1 == XSD.time):
1073+
if(dt2 == XSD.yearMonthDuration):
1074+
return False
1075+
elif(dt2 == XSD.dayTimeDuration or dt2 == XSD.Duration):
1076+
# checking if the dayTimeDuration has no Date Component
1077+
# (by checking if the format is "PT...." )
1078+
# else it wont be compatible with Time Literal
1079+
if("T" == str(obj2)[1]):
1080+
return True
1081+
else:
1082+
return False
1083+
1084+
if(dt1 == XSD.dateTime):
1085+
# compatible with all
1086+
return True
1087+
1088+
1089+
def calculateDuration(obj1, obj2):
1090+
"""
1091+
returns the duration Literal between two datetime
1092+
1093+
"""
1094+
date1 = obj1
1095+
date2 = obj2
1096+
difference = date1 - date2
1097+
return Literal(difference, datatype=XSD.duration)
1098+
1099+
1100+
def calculateFinalDateTime(obj1, dt1, obj2, dt2, operation):
1101+
"""
1102+
Calculates the final dateTime/date/time resultant after addition/
1103+
subtraction of duration/dayTimeDuration/yearMonthDuration
1104+
"""
1105+
1106+
# checking compatibility of datatypes (duration types and date/time/dateTime)
1107+
if(isCompatibleDateTimeDatatype(obj1, dt1, obj2, dt2)):
1108+
# proceed
1109+
if(operation == "-"):
1110+
ans = obj1 - obj2
1111+
return Literal(ans, datatype=dt1)
1112+
else:
1113+
ans = obj1 + obj2
1114+
return Literal(ans, datatype=dt1)
1115+
1116+
else:
1117+
raise SPARQLError('Incompatible Data types to DateTime Operations')
1118+
1119+
10041120
def EBV(rt):
10051121
"""
10061122
* If the argument is a typed literal with a datatype of xsd:boolean,

0 commit comments

Comments
 (0)