|
23 | 23 |
|
24 | 24 | from rdflib.plugins.sparql.parserutils import CompValue, Expr |
25 | 25 | from rdflib.plugins.sparql.datatypes import XSD_DTs, type_promotion |
| 26 | +from rdflib.plugins.sparql.datatypes import XSD_DateTime_DTs, XSD_Duration_DTs |
26 | 27 | from rdflib import URIRef, BNode, Variable, Literal, XSD, RDF |
27 | 28 | from rdflib.term import Node |
28 | 29 |
|
@@ -760,25 +761,67 @@ def AdditiveExpression(e, ctx): |
760 | 761 | if other is None: |
761 | 762 | return expr |
762 | 763 |
|
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) |
764 | 807 |
|
765 | | - dt = expr.datatype |
| 808 | + dt = expr.datatype |
766 | 809 |
|
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) |
773 | 816 |
|
774 | | - dt = type_promotion(dt, term.datatype) |
| 817 | + dt = type_promotion(dt, term.datatype) |
775 | 818 |
|
776 | | - if op == "+": |
777 | | - res += n |
778 | | - else: |
779 | | - res -= n |
| 819 | + if op == "+": |
| 820 | + res += n |
| 821 | + else: |
| 822 | + res -= n |
780 | 823 |
|
781 | | - return Literal(res, datatype=dt) |
| 824 | + return Literal(res, datatype=dt) |
782 | 825 |
|
783 | 826 |
|
784 | 827 | def RelationalExpression(e, ctx): |
@@ -1001,6 +1044,79 @@ def numeric(expr): |
1001 | 1044 | return expr.toPython() |
1002 | 1045 |
|
1003 | 1046 |
|
| 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 | + |
1004 | 1120 | def EBV(rt): |
1005 | 1121 | """ |
1006 | 1122 | * If the argument is a typed literal with a datatype of xsd:boolean, |
|
0 commit comments