This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit a2dd5b0acf165d98f128cf1c0f43fb9ec004f3b5
Author: Andy Seaborne <[email protected]>
AuthorDate: Sat Feb 17 17:25:54 2024 +0000

    GH-2273: Implement XSD duration divide operations
---
 .../jena/sparql/expr/nodevalue/NodeValueOps.java   | 169 ++++++------
 .../apache/jena/sparql/expr/TestNodeValueOps.java  | 300 +++++++++++++++------
 2 files changed, 300 insertions(+), 169 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
index 88d6b86378..3a36dc9040 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueOps.java
@@ -18,88 +18,38 @@
 
 package org.apache.jena.sparql.expr.nodevalue;
 
-import static javax.xml.datatype.DatatypeConstants.FIELD_UNDEFINED ;
-import static org.apache.jena.datatypes.xsd.XSDDatatype.XSD ;
-import static org.apache.jena.sparql.expr.ValueSpace.* ;
+import static javax.xml.datatype.DatatypeConstants.FIELD_UNDEFINED;
+import static org.apache.jena.sparql.expr.ValueSpace.*;
 
-import java.math.BigDecimal ;
-import java.util.GregorianCalendar ;
+import java.math.BigDecimal;
+import java.util.GregorianCalendar;
 
-import javax.xml.datatype.Duration ;
-import javax.xml.datatype.XMLGregorianCalendar ;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
 
-import org.apache.jena.datatypes.xsd.XSDDatatype ;
-import org.apache.jena.graph.Node ;
-import org.apache.jena.sparql.expr.ExprEvalTypeException ;
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.ValueSpace ;
-import org.apache.jena.sparql.util.NodeFactoryExtra ;
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.expr.ExprEvalTypeException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.ValueSpace;
 
 /** The code parts of arithmetic operations on {@link NodeValue}s.
  */
 public class NodeValueOps
 {
-    /* Date/time/duration coverage
-     * add, subtract (duration, duration)
-     *
-(xsd:duration, xsd:duration) -> xsd:duration
-        10.6.1 op:add-yearMonthDurations
-        10.6.2 op:subtract-yearMonthDurations
-(xsd:duration, xsd:duration) -> xsd:duration
-        10.6.6 op:add-dayTimeDurations
-        10.6.7 op:subtract-dayTimeDurations
-** Java has general duration subtract
-
-    * Subtract two date/times
-
-(xsd:dateTime, xsd:dateTime) -> xsd:duration
-        10.8.1 op:subtract-dateTimes -> xsd:dayTimeDuration
-(xsd:date, xsd:date) -> xsd:duration
-        10.8.2 op:subtract-dates
-(xsd:time, xsd:time) -> xsd:duration
-        10.8.3 op:subtract-times
-
-    * Date/time and duration
-** Java has general duration subtract (error needed?)
-
-(xsd:dateTime, xsd:duration) -> xsd:dateTime
-        10.8.4 op:add-yearMonthDuration-to-dateTime
-        10.8.5 op:add-dayTimeDuration-to-dateTime
-        10.8.6 op:subtract-yearMonthDuration-from-dateTime
-        10.8.7 op:subtract-dayTimeDuration-from-dateTime
-
-(xsd:date, xsd:duration) -> xsd:date
-        10.8.8 op:add-yearMonthDuration-to-date
-        10.8.9 op:add-dayTimeDuration-to-date
-        10.8.10 op:subtract-yearMonthDuration-from-date
-        10.8.11 op:subtract-dayTimeDuration-from-date
-
-(xsd:time, xsd:duration) -> xsd:time
-        10.8.12 op:add-dayTimeDuration-to-time
-        10.8.13 op:subtract-dayTimeDuration-from-time
-
-(xsd:duration, xsd:double) -> xsd:duration
-        10.6.8 op:multiply-dayTimeDuration
-        */
-
-        /* Missing:
-(xsd:duration, xsd:double) -> xsd:duration
-        10.6.9 op:divide-dayTimeDuration
-
-(xsd:duration, xsd:duration) -> xsd:decimal
-        10.6.10 op:divide-dayTimeDuration-by-dayTimeDuration
-         */
-    /* Notes:
-     * Does not consider whether a duration is dayTime or yearMonth
-     * for addition and subtraction.
+    /*
+     * Notes:
+     * Does not consider whether a duration is dayTime or yearMonth for
+     * multiplication and division where the operation can changes the result 
type.
      * As per the Java (Xerces) implementation, it just work on durations.
      */
 
-    // Until part of Jena datatypes.
-    private static final String dtXSDdateTimeStamp      = XSD+"#dateTimeStamp" 
;
-    private static final String dtXSDdayTimeDuration    = 
XSD+"#dayTimeDuration" ;
-    private static final String dtXSDyearMonthDuration  = 
XSD+"#yearMonthDuration" ;
+    //private static final String dtXSDdateTimeStamp      = 
XSDDatatype.XSDdateTimeStamp.getURI();
+    private static final String dtXSDdayTimeDuration    = 
XSDDatatype.XSDdayTimeDuration.getURI();
+    private static final String dtXSDyearMonthDuration  = 
XSDDatatype.XSDyearMonthDuration.getURI();
 
+    /** Add two {@link NodeValue NodeValues}, with all the extra datatypes and 
extensions supported. */
     public static NodeValue additionNV(NodeValue nv1, NodeValue nv2) {
         ValueSpace vs1 = nv1.getValueSpace();
         ValueSpace vs2 = nv2.getValueSpace();
@@ -117,11 +67,11 @@ public class NodeValueOps
             String lex = d3.toString();
             Node n;
             if ( isDTDur )
-                n = NodeFactoryExtra.createLiteralNode(lex, null, 
dtXSDdayTimeDuration);
+                n = NodeFactory.createLiteral(lex, 
XSDDatatype.XSDdayTimeDuration);
             else if ( isYMDur )
-                n = NodeFactoryExtra.createLiteralNode(lex, null, 
dtXSDyearMonthDuration);
+                n = NodeFactory.createLiteral(lex, 
XSDDatatype.XSDyearMonthDuration);
             else
-                n = org.apache.jena.graph.NodeFactory.createLiteral(lex, 
XSDDatatype.XSDduration);
+                n = NodeFactory.createLiteral(lex, XSDDatatype.XSDduration);
             return NodeValue.makeNodeDuration(d3, n);
         }
 
@@ -147,14 +97,10 @@ public class NodeValueOps
             NodeValue r = NodeValue.makeNode(result.toXMLFormat(), 
XSDDatatype.XSDtime);
             return r;
         }
-
-        if ( isDT(vs2) && vs1.equals(VSPACE_DURATION) )
-            // Carefully ...
-            return additionNV(nv2, nv1);
         throw new ExprEvalTypeException("Operator '+' : Undefined addition: " 
+ nv1 + " and " + nv2);
     }
 
-    // NodeFunctions
+    /** Subtract two {@link NodeValue NodeValues}, with all the extra 
datatypes and extensions supported. */
     public static NodeValue subtractionNV(NodeValue nv1, NodeValue nv2) {
         ValueSpace vs1 = nv1.getValueSpace();
         ValueSpace vs2 = nv2.getValueSpace();
@@ -170,9 +116,9 @@ public class NodeValueOps
             String lex = d3.toString();
             Node n;
             if ( isDTDur )
-                n = NodeFactoryExtra.createLiteralNode(lex, null, 
dtXSDdayTimeDuration);
+                n = NodeFactory.createLiteral(lex, 
XSDDatatype.XSDdayTimeDuration);
             else if ( isYMDur )
-                n = NodeFactoryExtra.createLiteralNode(lex, null, 
dtXSDyearMonthDuration);
+                n = NodeFactory.createLiteral(lex, 
XSDDatatype.XSDyearMonthDuration);
             else
                 n = org.apache.jena.graph.NodeFactory.createLiteral(lex, 
XSDDatatype.XSDduration);
             return NodeValue.makeNodeDuration(d3, n);
@@ -215,6 +161,7 @@ public class NodeValueOps
         throw new ExprEvalTypeException("Operator '-' : Undefined subtraction: 
" + nv1 + " and " + nv2);
     }
 
+    /** Multiple two {@link NodeValue NodeValues}, with all the extra 
datatypes and extensions supported. */
     public static NodeValue multiplicationNV(NodeValue nv1, NodeValue nv2) {
         ValueSpace vs1 = nv1.getValueSpace();
         ValueSpace vs2 = nv2.getValueSpace();
@@ -230,12 +177,13 @@ public class NodeValueOps
                 throw new ExprEvalTypeException("Operator '*': only dayTime 
duration.  Got: " + nv1);
             BigDecimal dec = nv2.getDecimal();
             Duration r = dur.multiply(dec);
-            Node n = NodeFactoryExtra.createLiteralNode(r.toString(), null, 
dtXSDdayTimeDuration);
+            Node n = NodeFactory.createLiteral(r.toString(), 
XSDDatatype.XSDduration);
             return NodeValue.makeNodeDuration(r, n);
         }
         throw new ExprEvalTypeException("Operator '*' : Undefined multiply: " 
+ nv1 + " and " + nv2);
     }
 
+    /** Divide two {@link NodeValue NodeValues}, with all the extra datatypes 
and extensions supported. */
     public static NodeValue divisionNV(NodeValue nv1, NodeValue nv2) {
         ValueSpace vs1 = nv1.getValueSpace();
         ValueSpace vs2 = nv2.getValueSpace();
@@ -243,9 +191,64 @@ public class NodeValueOps
         if ( vs1.equals(VSPACE_NUM) && vs2.equals(VSPACE_NUM) )
             return XSDFuncOp.numDivide(nv1, nv2);
 
+        // Duration divided by number
+        if ( vs1.equals(VSPACE_DURATION) && vs2.equals(VSPACE_NUM) ) {
+            Duration dur = nv1.getDuration();
+            // Multiply by 1/number.
+            BigDecimal dec = nv2.getDecimal();
+            if ( dec.equals(BigDecimal.ZERO) )
+                throw new ExprEvalTypeException("Divide by zero in duration 
division");
+
+            BigDecimal dec1 = BigDecimal.ONE.divide(dec);
+            Duration r = dur.multiply(dec1);
+            // Should normalize but not Duration.normalizeWith for a general 
duration.
+            // DT or YM specific normalization could be done. e.g. days can go 
over 31.
+            Node n = NodeFactory.createLiteral(r.toString(), 
XSDDatatype.XSDduration);
+            return NodeValue.makeNodeDuration(r, n);
+        }
+
+        // Duration divided by duration
+        if ( vs1.equals(VSPACE_DURATION) && vs2.equals(VSPACE_DURATION) ) {
+            // Ratio as a BigDecimal
+            Duration dur1 = nv1.getDuration();
+            Duration dur2 = nv2.getDuration();
+            if ( XSDFuncOp.isDayTime(dur1) && XSDFuncOp.isDayTime(dur2) ) {
+                // In seconds. Ignores fractional seconds.
+                double x1 = durationDayTimeAsSeconds(dur1);
+                double x2 = durationDayTimeAsSeconds(dur2);
+                if ( x2 == 0 )
+                    throw new ExprEvalTypeException("Divide by zero duration 
in xsd:dayTimeDuration division");
+                return NodeValue.makeDecimal(x1/x2);
+            }
+            if ( XSDFuncOp.isYearMonth(dur1) && XSDFuncOp.isYearMonth(dur2) ) {
+                // In months
+                double x1 = durationYearMonthAsMonths(dur1);
+                double x2 = durationYearMonthAsMonths(dur2);
+                if ( x2 == 0 )
+                    throw new ExprEvalTypeException("Divide by zero duration 
in xsd:YearMonthDuration division");
+                return NodeValue.makeDecimal(x1/x2);
+            }
+            throw new ExprEvalTypeException("Durations not both day-time nor 
year-month: " + nv1 + " and " + nv2);
+        }
+
         throw new ExprEvalTypeException("Operator '/' : Undefined division: " 
+ nv1 + " and " + nv2);
     }
 
+    private static double durationYearMonthAsMonths(Duration dur) {
+        return 12*dur.getYears() + dur.getMonths();
+    }
+
+    private static double durationDayTimeAsSeconds(Duration dur) {
+        double x = dur.getDays();
+        x = x * 24;
+        x = x + dur.getHours();
+        x = x * 60;
+        x = x + dur.getMinutes();
+        x = x * 60;
+        x = x + dur.getSeconds();
+        return x;
+    }
+
     private static boolean isDT(ValueSpace vs) {
         switch (vs) {
             case VSPACE_DATETIME:
@@ -256,9 +259,9 @@ public class NodeValueOps
 //            case VSPACE_G_MONTHDAY:
 //            case VSPACE_G_MONTH:
 //            case VSPACE_G_DAY:
-                return true ;
+                return true;
             default:
-                return false ;
+                return false;
         }
     }
 
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java
index d74d9f9d27..76b9b9347b 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeValueOps.java
@@ -20,124 +20,252 @@ package org.apache.jena.sparql.expr;
 
 import static org.junit.Assert.assertEquals;
 
-import org.apache.jena.atlas.logging.Log;
 import org.apache.jena.sparql.expr.nodevalue.NodeValueOps;
 import org.junit.Test;
 
-public class TestNodeValueOps
-{
+public class TestNodeValueOps {
     // ** Addition
     // Numerics
-    @Test public void nv_add_1() { testAdd("12", "13", "'25'^^xsd:integer" ); }
-    @Test public void nv_add_2() { testAdd("'12'^^xsd:decimal", "13", 
"'25.0'^^xsd:decimal" ); }
-    @Test public void nv_add_3() { testAdd("'12.0'^^xsd:decimal", "13", 
"'25.0'^^xsd:decimal" ); }
-    @Test public void nv_add_4() { testAdd("12e0", "13", "25.0e0" ); }
+    @Test
+    public void nv_add_1() {
+        testAdd("12", "13", "'25'^^xsd:integer");
+    }
+
+    @Test
+    public void nv_add_2() {
+        testAdd("'12'^^xsd:decimal", "13", "'25.0'^^xsd:decimal");
+    }
+
+    @Test
+    public void nv_add_3() {
+        testAdd("'12.0'^^xsd:decimal", "13", "'25.0'^^xsd:decimal");
+    }
+
+    @Test
+    public void nv_add_4() {
+        testAdd("12e0", "13", "25.0e0");
+    }
 
     // Strings
-    @Test public void nv_add_10() { testAdd("'12'", "'13'", "'1213'" ); }
+    @Test
+    public void nv_add_10() {
+        testAdd("'12'", "'13'", "'1213'");
+    }
 
-    //Durations (need to test the wiring, not whether the calculation is right)
-    @Test public void nv_add_20() { testAdd("'PT1H'^^xsd:duration", 
"'PT1H'^^xsd:duration", "'PT2H'^^xsd:duration" ); }
+    // Durations (need to test the wiring, not whether the calculation is 
right)
+    @Test
+    public void nv_add_20() {
+        testAdd("'PT1H'^^xsd:duration", "'PT1H'^^xsd:duration", 
"'PT2H'^^xsd:duration");
+    }
 
-    @Test public void nv_add_21() { testAdd("'PT1H'^^xsd:dayTimeDuration", 
"'PT1H'^^xsd:dayTimeDuration", "'PT2H'^^xsd:dayTimeDuration" ); }
+    @Test
+    public void nv_add_21() {
+        testAdd("'PT1H'^^xsd:dayTimeDuration", "'PT1H'^^xsd:dayTimeDuration", 
"'PT2H'^^xsd:dayTimeDuration");
+    }
 
     // Outside the XSD spec.
-    //@Test public void nv_add_22() { testAdd("'P1Y'^^xsd:yearMonthDuration", 
"'PT4H'^^xsd:dayTimeDuration", "'P1YT4H'^^xsd:duration" ); }
-    @Test public void nv_add_22() {
-        try {
-            testAdd("'P1Y'^^xsd:yearMonthDuration", 
"'PT4H'^^xsd:dayTimeDuration", "'P1YT4H'^^xsd:duration" );
-        } catch (NullPointerException ex) {
-            if ( isProbablyIBMJVM() )
-                // IBM JDK causes NPE on this one.
-                // IllegalStateException is acceptable; NullPointerException 
is not.
-                Log.warn(this, "TestNodeValueOps.nv_add_22 - IBM JVM - 
reported issue in the support for 
xsd:xsd:yearMonthDuration/xsd:dayTimeDuration");
-            else
-                throw ex;
-        }
-        catch (IllegalStateException ex) {}
-    }
-
-    private static boolean isProbablyIBMJVM()
-    {
-        return System.getProperty("java.vm.name", "").contains("IBM");
+    @Test
+    public void nv_add_22() {
+        testAdd("'P1Y'^^xsd:yearMonthDuration", "'PT4H'^^xsd:dayTimeDuration", 
"'P1YT4H'^^xsd:duration");
     }
 
     // Date/time + duration
-    @Test public void nv_add_23() { testAdd("'2000-01-01'^^xsd:date", 
"'P1Y'^^xsd:duration", "'2001-01-01'^^xsd:date"); }
-    @Test public void nv_add_24() { 
testAdd("'2000-01-01T00:00:00Z'^^xsd:dateTime",
-                                            "'P1Y1M'^^xsd:yearMonthDuration",
-                                            
"'2001-02-01T00:00:00Z'^^xsd:dateTime"); }
-    @Test public void nv_add_25() { 
testAdd("'2000-01-01T00:00:00Z'^^xsd:dateTime",
-                                            "'P1Y1M1DT1H1M1.1S'^^xsd:duration",
-                                            
"'2001-02-02T01:01:01.1Z'^^xsd:dateTime"); }
-    @Test public void nv_add_26() { testAdd("'00:00:00'^^xsd:time",
-                                            "'PT1H2M3.4S'^^xsd:duration",
-                                            "'01:02:03.4'^^xsd:time"); }
+    @Test
+    public void nv_add_23() {
+        testAdd("'2000-01-01'^^xsd:date", "'P1Y'^^xsd:duration", 
"'2001-01-01'^^xsd:date");
+    }
+
+    @Test
+    public void nv_add_24() {
+        testAdd("'2000-01-01T00:00:00Z'^^xsd:dateTime", 
"'P1Y1M'^^xsd:yearMonthDuration", "'2001-02-01T00:00:00Z'^^xsd:dateTime");
+    }
+
+    @Test
+    public void nv_add_25() {
+        testAdd("'2000-01-01T00:00:00Z'^^xsd:dateTime", 
"'P1Y1M1DT1H1M1.1S'^^xsd:duration", "'2001-02-02T01:01:01.1Z'^^xsd:dateTime");
+    }
+
+    @Test
+    public void nv_add_26() {
+        testAdd("'00:00:00'^^xsd:time", "'PT1H2M3.4S'^^xsd:duration", 
"'01:02:03.4'^^xsd:time");
+    }
 
     // Bad mixes
-    @Test(expected=ExprEvalException.class)
-    public void nv_add_50() { testAdd("'12'", "13"); }
+    @Test(expected = ExprEvalException.class)
+    public void nv_add_50() {
+        testAdd("'12'", "13");
+    }
 
-    @Test(expected=ExprEvalException.class)
-    public void nv_add_51() { testAdd("'12'", "'PT1H'^^xsd:duration"); }
+    @Test(expected = ExprEvalException.class)
+    public void nv_add_51() {
+        testAdd("'12'", "'PT1H'^^xsd:duration");
+    }
 
-    @Test(expected=ExprEvalException.class)
-    public void nv_add_52() { testAdd("'2012-04-05'^^xsd:date", 
"'2012-04-05'^^xsd:date"); }
+    @Test(expected = ExprEvalException.class)
+    public void nv_add_52() {
+        testAdd("'2012-04-05'^^xsd:date", "'2012-04-05'^^xsd:date");
+    }
 
     // ** Subtraction
     // Numerics
-    @Test public void nv_sub_1() { testSub("12", "13", "-1" ); }
-    @Test public void nv_sub_2() { testSub("12", "13.0", "-1.0" ); }
-    @Test public void nv_sub_3() { testSub("12e0", "13.0", "-1.0e0" ); }
+    @Test
+    public void nv_sub_1() {
+        testSub("12", "13", "-1");
+    }
+
+    @Test
+    public void nv_sub_2() {
+        testSub("12", "13.0", "-1.0");
+    }
+
+    @Test
+    public void nv_sub_3() {
+        testSub("12e0", "13.0", "-1.0e0");
+    }
 
     // Durations
-    //Durations (need to test the wiring, not whether the calculation is right)
-    @Test public void nv_sub_20() { testSub("'PT2H'^^xsd:duration", 
"'PT1H'^^xsd:duration", "'PT1H'^^xsd:duration" ); }
-    @Test public void nv_sub_21() { testSub("'PT2H'^^xsd:dayTimeDuration", 
"'PT1H'^^xsd:dayTimeDuration", "'PT1H'^^xsd:dayTimeDuration" ); }
-
-    //@Test public void nv_sub_22() { testSub("'P2Y'^^xsd:yearMonthDuration", 
"'P1Y'^^xsd:yearMonthDuration", "'P1Y'^^xsd:yearMonthDuration" ); }
-    @Test public void nv_sub_22() {
-        try {
-            testSub("'P2Y'^^xsd:yearMonthDuration", 
"'P1Y'^^xsd:yearMonthDuration", "'P1Y'^^xsd:yearMonthDuration" );
-        } catch (NullPointerException ex) {
-            if ( isProbablyIBMJVM() )
-                // IBM JDK causes NPE on this one.
-                Log.warn(this, "TestNodeValueOps.nv_sub_22 - IBM JVM - 
reported issue in the support for 
xsd:xsd:yearMonthDuration/xsd:dayTimeDuration");
-            else
-                throw ex;
-        }
-        catch (IllegalStateException ex) {}
-    }
-
-    @Test public void nv_sub_23() { testSub("'P3D'^^xsd:dayTimeDuration", 
"'PT4H'^^xsd:dayTimeDuration", "'P2DT20H'^^xsd:dayTimeDuration" ); }
+    @Test
+    public void nv_sub_20() {
+        testSub("'PT2H'^^xsd:duration", "'PT1H'^^xsd:duration", 
"'PT1H'^^xsd:duration");
+    }
+
+    @Test
+    public void nv_sub_21() {
+        testSub("'PT2H'^^xsd:dayTimeDuration", "'PT1H'^^xsd:dayTimeDuration", 
"'PT1H'^^xsd:dayTimeDuration");
+    }
+
+    @Test
+    public void nv_sub_22() {
+        testSub("'P2Y'^^xsd:yearMonthDuration", 
"'P1Y'^^xsd:yearMonthDuration", "'P1Y'^^xsd:yearMonthDuration");
+    }
+
+    @Test
+    public void nv_sub_23() {
+        testSub("'P3D'^^xsd:dayTimeDuration", "'PT4H'^^xsd:dayTimeDuration", 
"'P2DT20H'^^xsd:dayTimeDuration");
+    }
 
     // Date/time - duration
-    @Test public void nv_sub_30() { testSub("'2000-01-01'^^xsd:date", 
"'P1Y'^^xsd:duration", "'1999-01-01'^^xsd:date"); }
-    @Test public void nv_sub_31() { 
testSub("'2000-01-01T00:00:00Z'^^xsd:dateTime",
-                                            "'P1Y1M'^^xsd:yearMonthDuration",
-                                            
"'1998-12-01T00:00:00Z'^^xsd:dateTime"); }
-    @Test public void nv_sub_32() { 
testSub("'2000-01-01T00:00:00Z'^^xsd:dateTime",
-                                            "'P1Y1M1DT1H1M1.1S'^^xsd:duration",
-                                            
"'1998-11-29T22:58:58.9Z'^^xsd:dateTime"); }
-
-    @Test public void nv_sub_33() { testSub("'10:11:12'^^xsd:time",
-                                            "'PT1H2M3.4S'^^xsd:duration",
-                                            "'09:09:08.6'^^xsd:time"); }
+    @Test
+    public void nv_sub_30() {
+        testSub("'2000-01-01'^^xsd:date", "'P1Y'^^xsd:duration", 
"'1999-01-01'^^xsd:date");
+    }
+
+    @Test
+    public void nv_sub_31() {
+        testSub("'2000-01-01T00:00:00Z'^^xsd:dateTime", 
"'P1Y1M'^^xsd:yearMonthDuration", "'1998-12-01T00:00:00Z'^^xsd:dateTime");
+    }
+
+    @Test
+    public void nv_sub_32() {
+        testSub("'2000-01-01T00:00:00Z'^^xsd:dateTime", 
"'P1Y1M1DT1H1M1.1S'^^xsd:duration", "'1998-11-29T22:58:58.9Z'^^xsd:dateTime");
+    }
+
+    @Test
+    public void nv_sub_33() {
+        testSub("'10:11:12'^^xsd:time", "'PT1H2M3.4S'^^xsd:duration", 
"'09:09:08.6'^^xsd:time");
+    }
     // Date/time - date/time
 
-    // Bad
-    @Test(expected=ExprEvalException.class)
-    public void nv_sub_50() { testSub("'12'", "'13'" ); }
+    @Test(expected = ExprEvalException.class)
+    public void nv_sub_50() {
+        testSub("'12'", "'13'");
+    }
 
     // ** Multiplication
 
-    @Test public void nv_mult_1() { testMult("12", "13", "156" ); }
-    @Test public void nv_mult_2() { testMult("-12", "13.0", "-156.0" ); }
-    @Test public void nv_mult_3() { testMult("'PT1H2M'^^xsd:duration", "2", 
"'PT2H4M'^^xsd:dayTimeDuration"); }
+    @Test
+    public void nv_mult_1() {
+        testMult("12", "13", "156");
+    }
+
+    @Test
+    public void nv_mult_2() {
+        testMult("-12", "13.0", "-156.0");
+    }
+
+    @Test
+    public void nv_mult_3() {
+        testMult("'PT1H2M'^^xsd:duration", "2", "'PT2H4M'^^xsd:duration");
+    }
 
     // ** Division
-    @Test public void nv_div_1() { testDiv("12", "2", "6.0" ); }
-    @Test public void nv_div_2() { testDiv("12", "2e0", "6.0e0" ); }
+    @Test
+    public void nv_div_num_1() {
+        testDiv("12", "2", "6.0");
+    }
+
+    @Test
+    public void nv_div_num_2() {
+        testDiv("12", "2e0", "6.0e0");
+    }
+
+    @Test
+    public void nv_div_dur_3() {
+        testDiv("'PT24H20M'^^xsd:duration", "2", "'PT12H10M'^^xsd:duration");
+    }
+
+    @Test
+    public void nv_div_dur_4() {
+        testDiv("'PT24H20M'^^xsd:dayTimeDuration", "2", 
"'PT12H10M'^^xsd:duration");
+    }
+
+    // Note - not normalized.
+    @Test
+    public void nv_div_dur_5() {
+        testDiv("'PT24H20M'^^xsd:dayTimeDuration", "0.1", 
"'PT240H200M'^^xsd:duration");
+    }
+
+    @Test
+    public void nv_div_dur_6() {
+        testDiv("'P2Y8M'^^xsd:duration", "2", "'P1Y4M'^^xsd:duration");
+    }
+
+    @Test
+    public void nv_div_dur_7() {
+        testDiv("'P2Y8M'^^xsd:yearMonthDuration", "2", 
"'P1Y4M'^^xsd:duration");
+    }
+
+    // Note - not normalized.
+    @Test
+    public void nv_div_dur_8() {
+        testDiv("'P2Y8M'^^xsd:yearMonthDuration", "0.1", 
"'P20Y80M'^^xsd:duration");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void nv_div_dur_bad_1() {
+        // Year-month divided by day-time
+        testDiv("'P2Y8M'^^xsd:yearMonthDuration", "'PT12H10M'^^xsd:duration");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void nv_div_dur_bad_2() {
+        // day-time divided by year-month.
+        testDiv("'PT12H10M'^^xsd:duration", "'P2Y8M'^^xsd:duration");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void nv_div_dur_bad_3() {
+        // day-time divided by year-month.
+        testDiv("'PT12H10M'^^xsd:duration", "0");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void nv_div_dur_bad_4() {
+        // day-time divided by year-month.
+        testDiv("'P1Y'^^xsd:duration", "0");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void nv_div_dur_bad_5() {
+        // day-time divided by zero interval.
+        testDiv("'PT1H'^^xsd:duration", "'PT0S'^^xsd:duration");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void nv_div_dur_bad_6() {
+        // year-month divided by zero interval.
+        testDiv("'P1Y'^^xsd:duration", "'P0Y'^^xsd:duration");
+    }
 
     // == Workers
 

Reply via email to