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

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


The following commit(s) were added to refs/heads/master by this push:
     new e7b7afa  JENA-1845: Complete collection of "fn:*-from-*" functions
     new 7b5b9b5  Merge pull request #693 from afs/more-fn-functions
e7b7afa is described below

commit e7b7afae8063695b5de47d3eaa58f1d98817de55
Author: Andy Seaborne <[email protected]>
AuthorDate: Wed Feb 19 23:31:41 2020 +0000

    JENA-1845: Complete collection of "fn:*-from-*" functions
---
 .../jena/sparql/expr/nodevalue/XSDFuncOp.java      |   5 +-
 .../apache/jena/sparql/function/FunctionBase.java  |  17 +-
 .../jena/sparql/function/StandardFunctions.java    |  20 +-
 ...oTimezone.java => FN_AdjustDateToTimezone.java} |  11 +-
 .../library/FN_AdjustDatetimeToTimezone.java       |   7 +-
 ...oTimezone.java => FN_AdjustTimeToTimezone.java} |  11 +-
 ...N_DayFromDateTime.java => FN_DaysFromDate.java} |  19 +-
 ...yFromDateTime.java => FN_DaysFromDateTime.java} |   6 +-
 .../function/library/FN_HoursFromDateTime.java     |  19 +-
 .../function/library/FN_HoursFromDuration.java     |   4 +
 ..._DayFromDateTime.java => FN_HoursFromTime.java} |  19 +-
 .../function/library/FN_MinutesFromDateTime.java   |  19 +-
 .../function/library/FN_MinutesFromDuration.java   |  19 +-
 ...ayFromDateTime.java => FN_MinutesFromTime.java} |  19 +-
 .../function/library/FN_MonthFromDateTime.java     |  33 ---
 ...DayFromDateTime.java => FN_MonthsFromDate.java} |  19 +-
 ...romDateTime.java => FN_MonthsFromDateTime.java} |  19 +-
 .../function/library/FN_SecondsFromDateTime.java   |  19 +-
 .../function/library/FN_SecondsFromDuration.java   |  19 +-
 ...ayFromDateTime.java => FN_SecondsFromTime.java} |  19 +-
 ...yFromDateTime.java => FN_TimezoneFromDate.java} |  19 +-
 .../function/library/FN_TimezoneFromDateTime.java  |  19 +-
 ...yFromDateTime.java => FN_TimezoneFromTime.java} |  19 +-
 ..._DayFromDateTime.java => FN_YearsFromDate.java} |  19 +-
 ...FromDateTime.java => FN_YearsFromDateTime.java} |   6 +-
 .../jena/sparql/function/library/execTime.java     |   2 +-
 .../org/apache/jena/sparql/expr/LibTestExpr.java   | 116 ++++----
 .../org/apache/jena/sparql/expr/TestFunctions.java | 298 +--------------------
 .../jena/sparql/function/library/LibTest.java      |  30 ++-
 .../function/library/TS_LibraryFunctions.java      |  34 ++-
 .../function/library/TestFnFunctionsBoolean.java   |  44 +++
 ...ollation.java => TestFnFunctionsCollation.java} |  40 ++-
 .../library/TestFnFunctionsDateTimeDuration.java   | 221 +++++++++++++++
 .../function/library/TestFnFunctionsFormat.java    |  86 ++++++
 .../function/library/TestFnFunctionsNumeric.java   |  53 ++++
 ...tFnFunctions.java => TestFnFunctionsOther.java} |  65 +----
 .../function/library/TestFnFunctionsString.java    | 174 ++++++++++++
 37 files changed, 940 insertions(+), 628 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
index 95cc787..210ae58 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/XSDFuncOp.java
@@ -1364,7 +1364,6 @@ public class XSDFuncOp
         return dtGetSeconds(nv) ;
     }
 
-    private static int F_UNDEF = DatatypeConstants.FIELD_UNDEFINED; 
     public static NodeValue dtDateTime(NodeValue nv1, NodeValue nv2) {
         if ( ! nv1.isDate() )
             throw new ExprEvalException("fn:dateTime: arg1: Not an xsd:date: 
"+nv1) ;
@@ -1390,7 +1389,7 @@ public class XSDFuncOp
         return NodeValue.makeDateTime(lex);
     }
     
-    // Datetime accessors
+    // Accessors: datetime, date, Gregorian.
     public static NodeValue dtGetYear(NodeValue nv) {
         if ( nv.isDateTime() || nv.isDate() || nv.isGYear() || 
nv.isGYearMonth() ) {
             DateTimeStruct dts = parseAnyDT(nv) ;
@@ -1412,7 +1411,7 @@ public class XSDFuncOp
             DateTimeStruct dts = parseAnyDT(nv) ;
             return NodeValue.makeNode(dts.day, XSDDatatype.XSDinteger) ;
         }
-        throw new ExprEvalException("Not a month datatype") ;
+        throw new ExprEvalException("Not a day datatype") ;
     }
 
     private static DateTimeStruct parseAnyDT(NodeValue nv) {
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
index 4672f9a..e2e5e2d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
@@ -27,7 +27,7 @@ import org.apache.jena.sparql.expr.Expr ;
 import org.apache.jena.sparql.expr.ExprList ;
 import org.apache.jena.sparql.expr.NodeValue ;
 
-/** Implementation root for custom function evaluation. */  
+/** Implementation root for custom function evaluation. */
 public abstract class FunctionBase implements Function {
 
     @Override
@@ -35,14 +35,15 @@ public abstract class FunctionBase implements Function {
         // Rename for legacy reasons.
         checkBuild(uri, args) ;
     }
-    
+
     @Override
     public NodeValue exec(Binding binding, ExprList args, String uri, 
FunctionEnv env) {
         if ( args == null )
             // The contract on the function interface is that this should not 
happen.
             throw new ARQInternalErrorException("FunctionBase: Null args 
list") ;
-        
+
         List<NodeValue> evalArgs = evalArgs(binding, args, env);
+
         return exec(evalArgs, env) ;
     }
 
@@ -55,15 +56,15 @@ public abstract class FunctionBase implements Function {
         return evalArgs;
     }
 
-    /** Evaluation with access to the environment. 
-     * Special and careful use only! 
+    /** Evaluation with access to the environment.
+     * Special and careful use only!
      * Arity will have been checked if using a fixed-count FunctionBase 
subclass.
-     */ 
+     */
     protected NodeValue exec(List<NodeValue> args, FunctionEnv env) {
         return exec(args);
     }
-    
-    /** Function call to a list of evaluated argument values */ 
+
+    /** Function call to a list of evaluated argument values */
     public abstract NodeValue exec(List<NodeValue> args) ;
 
     public abstract void checkBuild(String uri, ExprList args) ;
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
index 393c753..bd7037e 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
@@ -166,10 +166,20 @@ public class StandardFunctions
         
         add(registry, xfn+"encode-for-uri", FN_StrEncodeForURI.class) ;
 
+        add(registry, xfn+"years-from-date",        FN_YearsFromDate.class) ;
+        add(registry, xfn+"months-from-date",       FN_MonthsFromDate.class) ;
+        add(registry, xfn+"days-from-date",         FN_DaysFromDate.class) ;
+        add(registry, xfn+"timezone-from-date",     FN_TimezoneFromDate.class) 
;
+
+        add(registry, xfn+"hours-from-time",        FN_HoursFromTime.class) ;
+        add(registry, xfn+"minutes-from-time",      FN_MinutesFromTime.class) ;
+        add(registry, xfn+"seconds-from-time",      FN_SecondsFromTime.class) ;
+        add(registry, xfn+"timezone-from-time",     FN_TimezoneFromTime.class) 
;
+        
         add(registry, xfn+"dateTime",               FN_DateTime.class) ;
-        add(registry, xfn+"year-from-dateTime",     FN_YearFromDateTime.class) 
;
-        add(registry, xfn+"month-from-dateTime",    
FN_MonthFromDateTime.class) ;
-        add(registry, xfn+"day-from-dateTime",      FN_DayFromDateTime.class) ;
+        add(registry, xfn+"years-from-dateTime",    
FN_YearsFromDateTime.class) ;
+        add(registry, xfn+"months-from-dateTime",   
FN_MonthsFromDateTime.class) ;
+        add(registry, xfn+"days-from-dateTime",     FN_DaysFromDateTime.class) 
;
         add(registry, xfn+"hours-from-dateTime",    
FN_HoursFromDateTime.class) ;
         add(registry, xfn+"minutes-from-dateTime",  
FN_MinutesFromDateTime.class) ;
         add(registry, xfn+"seconds-from-dateTime",  
FN_SecondsFromDateTime.class) ;
@@ -191,9 +201,9 @@ public class StandardFunctions
 //        9.6.1 fn:adjust-dateTime-to-timezone
         add(registry, xfn+"adjust-dateTime-to-timezone",  
FN_AdjustDatetimeToTimezone.class) ;
 //        9.6.2 fn:adjust-date-to-timezone
-        add(registry, xfn+"adjust-date-to-timezone",  
FN_AdjustDatetimeToTimezone.class) ;
+        add(registry, xfn+"adjust-date-to-timezone",  
FN_AdjustDateToTimezone.class) ;
 //        9.6.3 fn:adjust-time-to-timezone
-        add(registry, xfn+"adjust-time-to-timezone",  
FN_AdjustDatetimeToTimezone.class) ;
+        add(registry, xfn+"adjust-time-to-timezone",  
FN_AdjustTimeToTimezone.class) ;
 //        9.8.1 fn:format-dateTime
 //        9.8.2 fn:format-date
 //        9.8.3 fn:format-time
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDateToTimezone.java
similarity index 82%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDateToTimezone.java
index 7d480dd..5a36b7e 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDateToTimezone.java
@@ -18,6 +18,7 @@
 package org.apache.jena.sparql.function.library;
 
 import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.query.ARQ;
 import org.apache.jena.query.QueryBuildException;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.ExprList;
@@ -27,8 +28,8 @@ import org.apache.jena.sparql.function.FunctionBase;
 
 import java.util.List;
 
-public class FN_AdjustDatetimeToTimezone extends FunctionBase {
-    public FN_AdjustDatetimeToTimezone(){super();}
+public class FN_AdjustDateToTimezone extends FunctionBase {
+    public FN_AdjustDateToTimezone(){super();}
 
     @Override
     public void checkBuild(String uri, ExprList args)
@@ -40,10 +41,12 @@ public class FN_AdjustDatetimeToTimezone extends 
FunctionBase {
     public NodeValue exec(List<NodeValue> args)
     {
         if ( args.size() != 1 && args.size() != 2 )
-            throw new ExprEvalException("FN_StrNormalizeUnicode: Wrong number 
of arguments: "+args.size()+" : [wanted 1 or 2]") ;
+            throw new ExprEvalException("fn:adjustDateToTimezone: Wrong number 
of arguments: "+args.size()+" : [wanted 1 or 2]") ;
 
         NodeValue v1 = args.get(0) ;
-
+        if ( ARQ.isStrictMode() && !v1.isDate() )
+            throw new ExprEvalException("Not an xsd:date : " + v1);
+        
         if ( args.size() == 2 )
         {
             NodeValue v2 = args.get(1) ;
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
index 7d480dd..d46e8a6 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
@@ -18,6 +18,7 @@
 package org.apache.jena.sparql.function.library;
 
 import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.query.ARQ;
 import org.apache.jena.query.QueryBuildException;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.ExprList;
@@ -40,10 +41,12 @@ public class FN_AdjustDatetimeToTimezone extends 
FunctionBase {
     public NodeValue exec(List<NodeValue> args)
     {
         if ( args.size() != 1 && args.size() != 2 )
-            throw new ExprEvalException("FN_StrNormalizeUnicode: Wrong number 
of arguments: "+args.size()+" : [wanted 1 or 2]") ;
+            throw new ExprEvalException("fn:adjustDateTimeToTimezone: Wrong 
number of arguments: "+args.size()+" : [wanted 1 or 2]") ;
 
         NodeValue v1 = args.get(0) ;
-
+        if ( ARQ.isStrictMode() && !v1.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : " + v1);
+        
         if ( args.size() == 2 )
         {
             NodeValue v2 = args.get(1) ;
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustTimeToTimezone.java
similarity index 82%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustTimeToTimezone.java
index 7d480dd..387f162 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustDatetimeToTimezone.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_AdjustTimeToTimezone.java
@@ -18,6 +18,7 @@
 package org.apache.jena.sparql.function.library;
 
 import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.query.ARQ;
 import org.apache.jena.query.QueryBuildException;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.ExprList;
@@ -27,8 +28,8 @@ import org.apache.jena.sparql.function.FunctionBase;
 
 import java.util.List;
 
-public class FN_AdjustDatetimeToTimezone extends FunctionBase {
-    public FN_AdjustDatetimeToTimezone(){super();}
+public class FN_AdjustTimeToTimezone extends FunctionBase {
+    public FN_AdjustTimeToTimezone(){super();}
 
     @Override
     public void checkBuild(String uri, ExprList args)
@@ -40,10 +41,12 @@ public class FN_AdjustDatetimeToTimezone extends 
FunctionBase {
     public NodeValue exec(List<NodeValue> args)
     {
         if ( args.size() != 1 && args.size() != 2 )
-            throw new ExprEvalException("FN_StrNormalizeUnicode: Wrong number 
of arguments: "+args.size()+" : [wanted 1 or 2]") ;
+            throw new ExprEvalException("fn:adjustTimeToTimezone: Wrong number 
of arguments: "+args.size()+" : [wanted 1 or 2]") ;
 
         NodeValue v1 = args.get(0) ;
-
+        if ( ARQ.isStrictMode() && !v1.isTime() )
+            throw new ExprEvalException("Not an xsd:time : " + v1);
+        
         if ( args.size() == 2 )
         {
             NodeValue v2 = args.get(1) ;
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DaysFromDate.java
similarity index 64%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DaysFromDate.java
index 4a18922..05904ba 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DaysFromDate.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_DaysFromDate extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDate() )
+            throw new ExprEvalException("Not an xsd:date : " + v);
+        return XSDFuncOp.dtGetDay(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DaysFromDateTime.java
similarity index 80%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DaysFromDateTime.java
index 4a18922..49fdf40 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DaysFromDateTime.java
@@ -18,15 +18,19 @@
 
 package org.apache.jena.sparql.function.library;
 
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
 import org.apache.jena.sparql.function.FunctionBase1 ;
 
-public class FN_DayFromDateTime extends FunctionBase1
+public class FN_DaysFromDateTime extends FunctionBase1
 {
     @Override
     public NodeValue exec(NodeValue v)
     {
+        if ( ARQ.isStrictMode() && ! v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : "+v);
         return XSDFuncOp.dtGetDay(v) ;
     }
 }
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDateTime.java
index 7455941..a48c7d4 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDateTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_HoursFromDateTime extends FunctionBase1
-{
+public class FN_HoursFromDateTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetHours(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : " + v);
+        return XSDFuncOp.dtGetHours(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDuration.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDuration.java
index 8830d34..3845a04 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDuration.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromDuration.java
@@ -18,6 +18,8 @@
 
 package org.apache.jena.sparql.function.library;
 
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
 import org.apache.jena.sparql.function.FunctionBase1 ;
@@ -27,6 +29,8 @@ public class FN_HoursFromDuration extends FunctionBase1
     @Override
     public NodeValue exec(NodeValue v)
     {
+        if ( ARQ.isStrictMode() && !v.isDuration() )
+            throw new ExprEvalException("Not an xsd:duration : " + v);
         return XSDFuncOp.durGetHours(v) ;
     }
 }
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromTime.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromTime.java
index 4a18922..9805154 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_HoursFromTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_HoursFromTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : " + v);
+        return XSDFuncOp.dtGetHours(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDateTime.java
index 575430e..ed54789 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDateTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_MinutesFromDateTime extends FunctionBase1
-{
+public class FN_MinutesFromDateTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetMinutes(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isTime() )
+            throw new ExprEvalException("Not an xsd:time : " + v);
+        return XSDFuncOp.dtGetMinutes(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDuration.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDuration.java
index cdd91cf..ed3b580 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDuration.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromDuration.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_MinutesFromDuration extends FunctionBase1
-{
+public class FN_MinutesFromDuration extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.durGetMinutes(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDuration() )
+            throw new ExprEvalException("Not an xsd:duration : " + v);
+        return XSDFuncOp.durGetMinutes(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromTime.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromTime.java
index 4a18922..6e4e1cb 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MinutesFromTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_MinutesFromTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isTime() )
+            throw new ExprEvalException("Not an xsd:time : " + v);
+        return XSDFuncOp.dtGetMinutes(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthFromDateTime.java
deleted file mode 100644
index 1d92957..0000000
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthFromDateTime.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.sparql.function.library;
-
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
-
-public class FN_MonthFromDateTime extends FunctionBase1
-{
-    @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetMonth(v) ;
-    }
-}
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthsFromDate.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthsFromDate.java
index 4a18922..f2f54ea 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthsFromDate.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_MonthsFromDate extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDate() )
+            throw new ExprEvalException("Not an xsd:date : " + v);
+        return XSDFuncOp.dtGetMonth(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthsFromDateTime.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthsFromDateTime.java
index 4a18922..5a43a60 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_MonthsFromDateTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_MonthsFromDateTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : " + v);
+        return XSDFuncOp.dtGetMonth(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDateTime.java
index da45265..6d7cdb0 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDateTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_SecondsFromDateTime extends FunctionBase1
-{
+public class FN_SecondsFromDateTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetSeconds(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : " + v);
+        return XSDFuncOp.dtGetSeconds(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDuration.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDuration.java
index 449eaa6..f73623f 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDuration.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromDuration.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_SecondsFromDuration extends FunctionBase1
-{
+public class FN_SecondsFromDuration extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.durGetSeconds(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDuration() )
+            throw new ExprEvalException("Not an xsd:duration : " + v);
+        return XSDFuncOp.durGetSeconds(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromTime.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromTime.java
index 4a18922..7324bd3 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_SecondsFromTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_SecondsFromTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isTime() )
+            throw new ExprEvalException("Not an xsd:time : " + v);
+        return XSDFuncOp.dtGetSeconds(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDate.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDate.java
index 4a18922..48ef05f 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDate.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_TimezoneFromDate extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : " + v);
+        return XSDFuncOp.dtGetTimezone(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDateTime.java
index a1cb608..716cae5 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromDateTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_TimezoneFromDateTime extends FunctionBase1
-{
+public class FN_TimezoneFromDateTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetTimezone(v) ; 
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDate() )
+            throw new ExprEvalException("Not an xsd:date : " + v);
+        return XSDFuncOp.dtGetTimezone(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromTime.java
similarity index 63%
copy from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
copy to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromTime.java
index 4a18922..bb6f23e 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_TimezoneFromTime.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_TimezoneFromTime extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isTime() )
+            throw new ExprEvalException("Not an xsd:time : " + v);
+        return XSDFuncOp.dtGetTimezone(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearsFromDate.java
similarity index 64%
rename from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
rename to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearsFromDate.java
index 4a18922..37118f5 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_DayFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearsFromDate.java
@@ -18,16 +18,17 @@
 
 package org.apache.jena.sparql.function.library;
 
-import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
-import org.apache.jena.sparql.function.FunctionBase1 ;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp;
+import org.apache.jena.sparql.function.FunctionBase1;
 
-public class FN_DayFromDateTime extends FunctionBase1
-{
+public class FN_YearsFromDate extends FunctionBase1 {
     @Override
-    public NodeValue exec(NodeValue v)
-    {
-        return XSDFuncOp.dtGetDay(v) ;
+    public NodeValue exec(NodeValue v) {
+        if ( ARQ.isStrictMode() && !v.isDate() )
+            throw new ExprEvalException("Not an xsd:date : " + v);
+        return XSDFuncOp.dtGetYear(v);
     }
 }
-
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearFromDateTime.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearsFromDateTime.java
similarity index 80%
rename from 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearFromDateTime.java
rename to 
jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearsFromDateTime.java
index 9c8ede8..d2d0d60 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearFromDateTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_YearsFromDateTime.java
@@ -18,15 +18,19 @@
 
 package org.apache.jena.sparql.function.library;
 
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp ;
 import org.apache.jena.sparql.function.FunctionBase1 ;
 
-public class FN_YearFromDateTime extends FunctionBase1
+public class FN_YearsFromDateTime extends FunctionBase1
 {
     @Override
     public NodeValue exec(NodeValue v)
     {
+        if ( ARQ.isStrictMode() && ! v.isDateTime() )
+            throw new ExprEvalException("Not an xsd:dateTime : "+v);
         return XSDFuncOp.dtGetYear(v) ;
     }
 }
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/execTime.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/execTime.java
index d38337a..bfb5a75 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/execTime.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/execTime.java
@@ -25,7 +25,7 @@ import org.apache.jena.sparql.expr.ExprList ;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.sparql.function.FunctionBase ;
 
-/** Function that prints the system time to stderr the point of execution.
+/** Function that prints the system time to stderr at the point of execution.
  *  Returns true.
  *  This is a debugging aid only.
  */
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
index 254b74e..7359f2d 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/LibTestExpr.java
@@ -17,72 +17,88 @@
  */
 
 package org.apache.jena.sparql.expr;
-import static org.junit.Assert.* ;
-import org.apache.jena.graph.Node ;
+import static org.junit.Assert.*;
+
+import java.util.function.Predicate;
+
+import org.apache.jena.graph.Node;
 import org.apache.jena.query.ARQ;
-import org.apache.jena.shared.PrefixMapping ;
-import org.apache.jena.shared.impl.PrefixMappingImpl ;
-import org.apache.jena.sparql.ARQConstants ;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.shared.impl.PrefixMappingImpl;
+import org.apache.jena.sparql.ARQConstants;
 import org.apache.jena.sparql.function.FunctionEnv;
-import org.apache.jena.sparql.function.FunctionEnvBase ;
-import org.apache.jena.sparql.function.library.leviathan.LeviathanConstants ;
+import org.apache.jena.sparql.function.FunctionEnvBase;
+import org.apache.jena.sparql.function.library.leviathan.LeviathanConstants;
 import org.apache.jena.sparql.util.Context;
-import org.apache.jena.sparql.util.ExprUtils ;
-import org.apache.jena.sparql.util.NodeFactoryExtra ;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.apache.jena.sparql.util.NodeFactoryExtra;
 
 public class LibTestExpr {
 
-
     private static PrefixMapping pmap = new PrefixMappingImpl();
     static {
         pmap.setNsPrefixes(ARQConstants.getGlobalPrefixMap());
         pmap.setNsPrefix("lfn", 
LeviathanConstants.LeviathanFunctionLibraryURI);
     }
-    
-    static void testExpr(String exprExpected, String expectedResult) {
-        NodeValue actual = eval(exprExpected) ;
-        NodeValue expected = eval(expectedResult) ;
-        assertEquals(exprExpected, expected, actual) ; 
+
+    public static void testExpr(String exprExpected, String expectedResult) {
+        NodeValue actual = eval(exprExpected);
+        NodeValue expected = eval(expectedResult);
+        assertEquals(exprExpected, expected, actual);
     }
 
-    static Expr parse(String exprString) {
+    public static Expr parse(String exprString) {
         return ExprUtils.parse(exprString, pmap);
     }
-    
-    /** Create an execution environment suitable for testing functions and 
expressions */ 
+
+    /** Create an execution environment suitable for testing functions and 
expressions */
     public static FunctionEnv createTest()
     {
         Context cxt = ARQ.getContext().copy();
-        cxt.set(ARQConstants.sysCurrentTime, NodeFactoryExtra.nowAsDateTime()) 
;
-        return new FunctionEnvBase(cxt) ;
+        cxt.set(ARQConstants.sysCurrentTime, NodeFactoryExtra.nowAsDateTime());
+        return new FunctionEnvBase(cxt);
     }
-        
-    static NodeValue eval(String exprString) {
-        Expr expr = ExprUtils.parse(exprString, pmap);
+
+    public static NodeValue eval(String exprString) {
+        Expr expr = parse(exprString);
         NodeValue result = expr.eval(null, new FunctionEnvBase());
-        return result ;
+        return result;
     }
-        
-    
-    static void test(String exprString, String result) {
-        Node r = NodeFactoryExtra.parseNode(result);
-        test(exprString, r);
+
+    public static void test(String exprStr, String exprStrExpected) {
+        Expr expr = parse(exprStrExpected);
+        NodeValue rExpected = expr.eval(null, LibTestExpr.createTest());
+        test(exprStr, rExpected);
     }
-    
-    static void test(String exprString, Node result) {
-        Expr expr = ExprUtils.parse(exprString, pmap);
-        NodeValue actual = expr.eval(null, new FunctionEnvBase());
+
+    public static void test(String exprString, Node result) {
         NodeValue expected = NodeValue.makeNode(result);
-        assertTrue("Expected = " + expected + " : Actual = " + actual, 
NodeValue.sameAs(expected, actual)) ;
+        test(exprString, expected);
     }
 
-    static void testDouble(String exprString, String result, double delta) {
+    public static void test(String exprStr, NodeValue expected) {
+        Expr expr = parse(exprStr);
+        NodeValue actual = expr.eval(null, LibTestExpr.createTest());
+        assertTrue("Expected = " + expected + " : Actual = " + actual, 
NodeValue.sameAs(expected, actual));
+    }
+
+    public static void test(String exprStr) {
+        test(exprStr, NodeValue.TRUE);
+    }
+
+    public static void test(String exprStr, Predicate<NodeValue> test) {
+        Expr expr = parse(exprStr);
+        NodeValue r = expr.eval(null, LibTestExpr.createTest());
+        assertTrue(exprStr, test.test(r));
+    }
+
+    public static void testDouble(String exprString, String result, double 
delta) {
         Node r = NodeFactoryExtra.parseNode(result);
         testDouble(exprString, r, delta);
     }
 
-    static void testDouble(String exprString, Node result, double delta) {
-        Expr expr = ExprUtils.parse(exprString, pmap);
+    public static void testDouble(String exprString, Node result, double 
delta) {
+        Expr expr = parse(exprString);
         NodeValue actual = expr.eval(null, new FunctionEnvBase());
         NodeValue expected = NodeValue.makeNode(result);
         // Note that we don't test lexical form because we can get mismatches
@@ -90,26 +106,26 @@ public class LibTestExpr {
         if (NodeValue.sameAs(expected, actual))
             return;
 
-        testDouble(exprString, expected.getDouble(), delta); ;
+        testDouble(exprString, expected.getDouble(), delta);;
     }
-    
-    static void testDouble(String exprString, double expected, double delta) {
+
+    public static void testDouble(String exprString, double expected, double 
delta) {
         Expr expr = ExprUtils.parse(exprString, pmap);
         NodeValue actual = expr.eval(null, new FunctionEnvBase());
-        assertTrue("Not a double: "+actual, actual.isDouble() ) ; 
-        double result = actual.getDouble() ;
-        
+        assertTrue("Not a double: "+actual, actual.isDouble() );
+        double result = actual.getDouble();
+
         // Because Java floating point calculations are woefully imprecise we
         // are in many cases simply testing that the differences between the
         // values are within a given delta
         if ( Double.isInfinite(expected) ) {
-            assertTrue("Expected INF: Got "+result, Double.isInfinite(result)) 
;
-            return ;
+            assertTrue("Expected INF: Got "+result, Double.isInfinite(result));
+            return;
         }
-        
+
         if ( Double.isNaN(expected) ) {
-            assertTrue("Expected NaN: Got "+result, Double.isNaN(result)) ;
-            return ;
+            assertTrue("Expected NaN: Got "+result, Double.isNaN(result));
+            return;
         }
 
         double difference = Math.abs(result - expected);
@@ -117,8 +133,8 @@ public class LibTestExpr {
                 difference <= delta);
     }
 
-    
-    static void testError(String exprString) {
+
+    public static void testError(String exprString) {
         Expr expr = ExprUtils.parse(exprString, pmap);
         expr.eval(null, new FunctionEnvBase());
     }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
index e1c1c9a..6ab5fae 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions.java
@@ -23,11 +23,9 @@ import static org.junit.Assert.assertFalse ;
 import static org.junit.Assert.assertTrue ;
 import static org.junit.Assert.fail ;
 
-import java.text.DecimalFormatSymbols;
 import java.text.ParseException ;
 import java.text.SimpleDateFormat ;
 import java.util.Date ;
-import java.util.Locale;
 import java.util.TimeZone ;
 import java.util.function.Predicate;
 
@@ -36,43 +34,17 @@ import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.NodeFactory ;
 import org.apache.jena.sparql.ARQConstants ;
 import org.apache.jena.sparql.util.ExprUtils ;
-import org.junit.Assert ;
 import org.junit.Test ;
 
 public class TestFunctions
 {
-    private static final NodeValue INT_ZERO = NodeValue.makeInteger(0) ;
-    private static final NodeValue INT_ONE  = NodeValue.makeInteger(1) ;
-    //private static final NodeValue INT_TWO  = NodeValue.makeInteger(2) ;
+    // Test of fn;* are in org.apache.jena.sparql.function.library.*
+    
     private static final NodeValue TRUE     = NodeValue.TRUE ;
     private static final NodeValue FALSE    = NodeValue.FALSE ;
     
     @Test public void expr1() { test("1", NodeValue.makeInteger(1)) ; }
 
-    @Test public void exprStrLen1() { test("fn:string-length('')", INT_ZERO) ; 
}
-    @Test public void exprStrLen2() { test("fn:string-length('a')", INT_ONE) ; 
}
-    // Test from JENA-785
-    @Test public void exprStrLen3() { test("fn:string-length('𐐈𐑌𐐻𐐪𐑉𐐿𐐻𐐮𐐿𐐲')", 
NodeValue.makeInteger(10l)) ; }
-
-    // F&O strings are one-based, and substring takes a length
-    @Test public void exprSubstring1() { test("fn:substring('',0)", 
NodeValue.makeString("")) ; }
-    @Test public void exprSubstring2() { test("fn:substring('',1)", 
NodeValue.makeString("")) ; }
-    @Test public void exprSubstring3() { test("fn:substring('',1,0)", 
NodeValue.makeString("")) ; }
-    @Test public void exprSubstring4() { test("fn:substring('',1,1)", 
NodeValue.makeString("")) ; }
-
-    @Test public void exprSubstring5() { test("fn:substring('abc',1)", 
NodeValue.makeString("abc")) ; }
-    @Test public void exprSubstring6() { test("fn:substring('abc',2)", 
NodeValue.makeString("bc")) ; }
-    @Test public void exprSubstring7() { test("fn:substring('a',1,1)", 
NodeValue.makeString("a")) ; }
-    @Test public void exprSubstring8() { test("fn:substring('a',1,2)", 
NodeValue.makeString("a")) ; }
-    @Test public void exprSubstring9() { test("fn:substring('a',0)", 
NodeValue.makeString("")) ; }
-    
-    // Uses round()
-    @Test public void exprSubstring10() { test("fn:substring('abc',1.6,1.33)", 
NodeValue.makeString("b")) ; }
-    // This test was added because the test suite had 1199 tests in. 
-    @Test public void exprSubstring11() { test("fn:substring('abc',-1, 
-15.3)", NodeValue.makeString("")) ; }
-    // Test from JENA-785
-    @Test public void exprSubstring12() { test("fn:substring('𐐈𐑌𐐻𐐪𐑉𐐿𐐻𐐮𐐿𐐲', 1, 
1)", NodeValue.makeString("𐐈")) ; } 
-    
     @Test public void exprJavaSubstring1() { test("afn:substr('abc',0,0)", 
NodeValue.makeString("")) ; }
     @Test public void exprJavaSubstring2() { test("afn:substr('abc',0,1)", 
NodeValue.makeString("a")) ; }
     @Test public void exprJavaSubstring3() { 
test("<"+ARQConstants.ARQFunctionLibrary+"substr>('abc',0,0)", 
NodeValue.makeString("")) ; }
@@ -86,7 +58,6 @@ public class TestFunctions
     @Test public void exprSprintf_03()      { test("afn:sprintf('sometext %s', 
'abcdefghi')",NodeValue.makeString("sometext abcdefghi")) ; }
     @Test public void exprSprintf_04()      { test("afn:sprintf('%1$tm 
%1$te,%1$tY', '2016-03-17'^^xsd:date)",NodeValue.makeString("03 17,2016")) ; }
 
-
     @Test public void exprSprintf_06()      { test("afn:sprintf('this is %s', 
'false'^^xsd:boolean)",NodeValue.makeString("this is false")) ; }
     @Test public void exprSprintf_07()      { test("afn:sprintf('this number 
is equal to %.2f', '11.22'^^xsd:decimal)",NodeValue.makeString("this number is 
equal to "+String.format("%.2f",11.22))) ; }
     @Test public void exprSprintf_08()      { test("afn:sprintf('%.3f', 
'1.23456789'^^xsd:float)",NodeValue.makeString(String.format("%.3f",1.23456789)))
 ; }
@@ -147,12 +118,6 @@ public class TestFunctions
     // Timezone +11:00 can be a day behind
     @Test public void exprSprintf_25() { 
test_exprSprintf_tz_possibilites("2005-10-14T10:09:43+11:00",  "10 13,2005", 
"10 14,2005") ; }
     
-    @Test public void exprStrStart0() { test("fn:starts-with('abc', '')", 
TRUE) ; }
-    @Test public void exprStrStart1() { test("fn:starts-with('abc', 'a')", 
TRUE) ; }
-    @Test public void exprStrStart2() { test("fn:starts-with('abc', 'ab')", 
TRUE) ; }
-    @Test public void exprStrStart3() { test("fn:starts-with('abc', 'abc')", 
TRUE) ; }
-    @Test public void exprStrStart4() { test("fn:starts-with('abc', 'abcd')", 
FALSE) ; }
-    
     @Test public void exprStrStart10() { test("STRSTARTS('abc', 'abcd')", 
FALSE) ; }
     @Test public void exprStrStart11() { test("STRSTARTS('abc'@en, 'ab')", 
TRUE) ; }
     @Test public void exprStrStart12() { test("STRSTARTS('abc'^^xsd:string, 
'ab')", TRUE) ; }
@@ -194,12 +159,6 @@ public class TestFunctions
     // Empty string case
     @Test public void exprStrAfter10() { test("STRAFTER('abc'^^xsd:string, 
'')", NodeValue.makeNode("abc", XSDDatatype.XSDstring)) ; }
 
-    @Test public void exprStrEnds0() { test("fn:ends-with('abc', '')", TRUE) ; 
}
-    @Test public void exprStrEnds1() { test("fn:ends-with('abc', 'c')", TRUE) 
; }
-    @Test public void exprStrEnds2() { test("fn:ends-with('abc', 'bc')", TRUE) 
; }
-    @Test public void exprStrEnds3() { test("fn:ends-with('abc', 'abc')", 
TRUE) ; }
-    @Test public void exprStrEnds4() { test("fn:ends-with('abc', 'zabc')", 
FALSE) ; }
-    
     @Test public void exprStrEnds10() { test("STRENDS('abc', 'abcd')", FALSE) 
; }
     @Test public void exprStrEnds11() { test("STRENDS('abc'@en, 'bc')", TRUE) 
; }
     @Test public void exprStrEnds12() { test("STRENDS('abc'^^xsd:string, 
'c')", TRUE) ; }
@@ -211,23 +170,6 @@ public class TestFunctions
     @Test public void exprStrEnds17() { testEvalException("STRENDS(123, 
'ab'@fr)") ; }
     @Test public void exprStrEnds18() { 
testEvalException("STRENDS('123'^^xsd:string, 12.3)") ; }
 
-    @Test public void exprStrCase1() { test("fn:lower-case('aBc')", 
NodeValue.makeString("abc")) ; }
-    @Test public void exprStrCase2() { test("fn:lower-case('abc')", 
NodeValue.makeString("abc")) ; }
-    @Test public void exprStrCase3() { test("fn:upper-case('abc')", 
NodeValue.makeString("ABC")) ; }
-    @Test public void exprStrCase4() { test("fn:upper-case('ABC')", 
NodeValue.makeString("ABC")) ; }
-    
-
-    @Test public void exprStrContains0() { test("fn:contains('abc', '')", 
TRUE) ; }
-    @Test public void exprStrContains1() { test("fn:contains('abc', 'a')", 
TRUE) ; }
-    @Test public void exprStrContains2() { test("fn:contains('abc', 'b')", 
TRUE) ; }
-    @Test public void exprStrContains3() { test("fn:contains('abc', 'c')", 
TRUE) ; }
-    
-    @Test public void exprStrContains4() { test("fn:contains('abc', 'ab')", 
TRUE) ; }
-    @Test public void exprStrContains5() { test("fn:contains('abc', 'bc')", 
TRUE) ; }
-    @Test public void exprStrContains6() { test("fn:contains('abc', 'abc')", 
TRUE) ; }
-    @Test public void exprStrContains7() { test("fn:contains('abc', 'Xc')", 
FALSE) ; }
-    @Test public void exprStrContains8() { test("fn:contains('abc', 'Xa')", 
FALSE) ; }
-
     @Test public void exprContains10() { test("Contains('abc', 'abcd')", 
FALSE) ; }
     @Test public void exprContains11() { test("Contains('abc'@en, 'bc')", 
TRUE) ; }
     @Test public void exprContains12() { test("Contains('abc'^^xsd:string, 
'c')", TRUE) ; }
@@ -239,61 +181,6 @@ public class TestFunctions
     @Test public void exprContains17() { testEvalException("Contains(123, 
'ab'@fr)") ; }
     @Test public void exprContains18() { 
testEvalException("STRENDS('123'^^xsd:string, 12.3)") ; }
 
-    @Test public void exprStrNormalizeSpace0() { test("fn:normalize-space(' 
The    wealthy curled darlings                                         of    
our    nation. ')",
-            NodeValue.makeString("The wealthy curled darlings of our 
nation.")) ; }
-    @Test public void exprStrNormalizeSpace1() { 
test("fn:normalize-space('')",NodeValue.nvEmptyString) ; }
-    @Test public void exprStrNormalizeSpace2() { test("fn:normalize-space('   
Aaa     ')",NodeValue.makeString("Aaa")) ; }
-    @Test public void exprStrNormalizeSpace3() { test("fn:normalize-space('A a 
  a    a a    ')",NodeValue.makeString("A a a a a")) ; }
-
-    // https://www.w3.org/TR/xpath-functions-30/#func-normalize-unicode
-    // and
-    // from http://www.unicode.org/reports/tr15/
-    //l
-    @Test public void exprStrNormalizeUnicode0() { 
test("fn:normalize-unicode('Äffin','nfd')",NodeValue.makeString("Äffin")) ; }
-    @Test public void exprStrNormalizeUnicode1() { 
test("fn:normalize-unicode('Äffin','nfc')",NodeValue.makeString("Äffin")) ; }
-    //m
-    @Test public void exprStrNormalizeUnicode2() { 
test("fn:normalize-unicode('Ä\\uFB03n','nfd')",NodeValue.makeString("Äffin")) ; }
-    @Test public void exprStrNormalizeUnicode3() { 
test("fn:normalize-unicode('Ä\\uFB03n','nfc')",NodeValue.makeString("Äffin")) ; }
-    //n
-    @Test public void exprStrNormalizeUnicode4() { 
test("fn:normalize-unicode('Henry IV','nfd')",NodeValue.makeString("Henry IV")) 
; }
-    @Test public void exprStrNormalizeUnicode5() { 
test("fn:normalize-unicode('Henry IV','nfc')",NodeValue.makeString("Henry IV")) 
; }
-    //l'
-    @Test public void exprStrNormalizeUnicode6() { 
test("fn:normalize-unicode('Äffin','nfkd')",NodeValue.makeString("Äffin")) ; }
-    @Test public void exprStrNormalizeUnicode7() { 
test("fn:normalize-unicode('Äffin','nfkc')",NodeValue.makeString("Äffin")) ; }
-    // r
-    String hw_ka="\uFF76";
-    String hw_ten="\uFF9F";
-    @Test public void exprStrNormalizeUnicode8() { 
test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfd')",NodeValue.makeString(hw_ka+hw_ten))
 ; }
-    @Test public void exprStrNormalizeUnicode9() {
-        
test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfc')",NodeValue.makeString(hw_ka+hw_ten))
 ;
-    }
-    // Not sure why the following tests are not passing
-    // both examples are taken from the http://www.unicode.org/reports/tr15/ 
(Table 8 r')
-    // the translation of hw_ka,hw_ten,ka and ten are taken from Table 4 of 
the same document
-    // 
-    // I (Alessandro Seganti) took the ga translation by association (it was 
not defined in the unicode report)
-    // and chosen to be: KATAKANA LETTER GA U+30AC
-    // Everything seems ok to me so there are two options in my opinion:
-    // 1) the java implementation of the nfkd has some flaws
-    // 2) the unicode example is wrong (I cannot judge as I do not know 
japanese or unicode enough :))
-    // The test is failing because the expected string has code when looking 
in the debugger (UTF-16?) (12459 | 12442) 
-    // while the Nomalizer.normalize is giving  (12459 | 12441)
-//    @Test public void exprStrNormalizeUnicode10() {
-//        String ka = "\u30AB";
-//        String ten="\u3099";
-//        test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfkd')", 
NodeValue.makeString(ka+ten)) ;
-//    }
-//    @Test public void exprStrNormalizeUnicode11() {
-//        String ga="\u30AC";
-//        
test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfkc')",NodeValue.makeString(ga))
 ;
-//    }
-
-    // empty argument <-> returns the input string
-    @Test public void exprStrNormalizeUnicode12() { 
test("fn:normalize-unicode('some word','')",NodeValue.makeString("some word")) 
; }
-    // one argument <-> NFC
-    @Test public void exprStrNormalizeUnicode13() { 
test("fn:normalize-unicode('Äffin')",NodeValue.makeString("Äffin")) ; }
-
-
     @Test public void exprReplace01()  { test("REPLACE('abc', 'b', 'Z')", 
NodeValue.makeString("aZc")) ; }
     @Test public void exprReplace02()  { test("REPLACE('abc', 'b.', 'Z')", 
NodeValue.makeString("aZ")) ; }
     @Test public void exprReplace03()  { test("REPLACE('abcbd', 'b.', 'Z')", 
NodeValue.makeString("aZZ")) ; }
@@ -312,14 +199,6 @@ public class TestFunctions
     @Test public void exprReplace11()  { test("REPLACE('', '.', 'Z')",      
NodeValue.makeString("")) ; }
     @Test public void exprReplace12()  { test("REPLACE('', '(a|b)?', 'Z')", 
NodeValue.makeString("Z")) ; }
 
-    @Test public void exprFnReplace01()  { test("fn:replace('abc', 'b', 'Z')", 
NodeValue.makeString("aZc")) ; }
-    @Test public void exprFnReplace02()  { test("fn:replace('abc', 'b.', 
'Z')", NodeValue.makeString("aZ")) ; }
-    @Test public void exprFnReplace03()  { test("fn:replace('abcbd', 'b.', 
'Z')", NodeValue.makeString("aZZ")) ; }
-    
-    @Test public void exprFnReplace04()  { 
test("fn:replace('abcbd'^^xsd:string, 'b.', 'Z')", NodeValue.makeNode("aZZ", 
XSDDatatype.XSDstring)) ; }
-    @Test public void exprFnReplace05()  { test("fn:replace('abcbd'@en, 'b.', 
'Z')", NodeValue.makeNode("aZZ", "en", (String)null)) ; }
-    @Test public void exprFnReplace06()  { test("fn:replace('abcbd', 'B.', 
'Z', 'i')", NodeValue.makeString("aZZ")) ; }
-
     // Bad group
     @Test
     public void exprReplace13() {
@@ -331,128 +210,7 @@ public class TestFunctions
     public void exprReplace14() {
         ExprUtils.parse("REPLACE('abc', '^(a){-9}', 'ABC')");
     }
-    
-    // Bad pattern : dynamic (eval time) exception. 
-    // The pattern for fn:replace is not compiled on build - if that changes, 
this test will fail.
-    // See exprReplace14.
-    @Test
-    public void exprReplace15() {
-        testEvalException("fn:replace('abc', '^(a){-9}', 'ABC')");
-    }
-
-    @Test public void exprBoolean1()    { test("fn:boolean('')", FALSE) ; }
-    @Test public void exprBoolean2()    { test("fn:boolean(0)", FALSE) ; }
-    @Test public void exprBoolean3()    { test("fn:boolean(''^^xsd:string)", 
FALSE) ; }
-    
-    @Test public void exprBoolean4()    { test("fn:boolean('X')", TRUE) ; }
-    @Test public void exprBoolean5()    { test("fn:boolean('X'^^xsd:string)", 
TRUE) ; }
-    @Test public void exprBoolean6()    { test("fn:boolean(1)", TRUE) ; }
-
-    @Test public void exprBoolean7()    { test("fn:not('')", TRUE) ; }
-    @Test public void exprBoolean8()    { test("fn:not('X')", FALSE) ; }
-    @Test public void exprBoolean9()    { test("fn:not(1)", FALSE) ; }
-    @Test public void exprBoolean10()   { test("fn:not(0)", TRUE) ; }
-
-    @Test public void exprRound_01()    { test("fn:round(123)",   
NodeValue.makeInteger(123)) ; }
-    @Test public void exprRound_02()    { test("fn:round(123.5)",  
NodeValue.makeDecimal(124)) ; }
-    @Test public void exprRound_03()    { test("fn:round(-0.5e0)", 
NodeValue.makeDouble(0.0e0)) ; }
-    @Test public void exprRound_04()    { test("fn:round(-1.5)",   
NodeValue.makeDecimal(-1)) ; }
-    // !! I don't think that this is working correctly also if the test is 
passing... need to check!
-    @Test public void exprRound_05()    { test("fn:round(-0)",     
NodeValue.makeInteger("-0")) ; }
-    @Test public void exprRound_06()    { test("fn:round(1.125, 2)",     
NodeValue.makeDecimal(1.13)) ; }
-    @Test public void exprRound_07()    { test("fn:round(8452, -2)",     
NodeValue.makeInteger(8500)) ; }
-    @Test public void exprRound_08()    { test("fn:round(3.1415e0, 2)",     
NodeValue.makeDouble(3.14e0)) ; }
-    // counter-intuitive -- would fail if float/double not translated to 
decimal
-    @Test public void exprRound_09()    { test("fn:round(35.425e0, 2)",     
NodeValue.makeDouble(35.42)) ; }
-
-    @Test public void exprRoundHalfEven_01()    { 
test("fn:round-half-to-even(0.5)",   NodeValue.makeDecimal(0)) ; }
-    @Test public void exprRoundHalfEven_02()    { 
test("fn:round-half-to-even(1.5)",  NodeValue.makeDecimal(2)) ; }
-    @Test public void exprRoundHalfEven_03()    { 
test("fn:round-half-to-even(2.5)", NodeValue.makeDecimal(2)) ; }
-    @Test public void exprRoundHalfEven_04()    { 
test("fn:round-half-to-even(3.567812e+3, 2)",   
NodeValue.makeDouble(3567.81e0)) ; }
-    // !! I don't think that this is working correctly also if the test is 
passing... need to check!
-    @Test public void exprRoundHalfEven_05()    { 
test("fn:round-half-to-even(-0)",     NodeValue.makeInteger(-0)) ; }
-    @Test public void exprRoundHalfEven_06()    { 
test("fn:round-half-to-even(4.7564e-3, 2)",     NodeValue.makeDouble(0.0e0)) ; }
-    @Test public void exprRoundHalfEven_07()    { 
test("fn:round-half-to-even(35612.25, -2)",     NodeValue.makeDecimal(35600)) ; 
}
-    // counter-intuitive -- would fail if float/double not translated to 
decimal
-    @Test public void exprRoundHalfEven_08()    { 
test("fn:round-half-to-even('150.015'^^xsd:float, 2)",     
NodeValue.makeFloat((float)150.01)) ; }
-
-    private String getDynamicDurationString(){
-        int tzOffset = TimeZone.getDefault().getOffset(new Date().getTime()) / 
(1000*60);
-        String off = "PT"+Math.abs(tzOffset)+"M";
-        if(tzOffset < 0)
-            off = "-"+off;
-        return off;
-    }
-
-    @Test public void exprAdjustDatetimeToTz_01(){
-        testEqual(
-                
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime)",
-                
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
-    }
-
-    @Test public void exprAdjustDatetimeToTz_02(){
-        testEqual(
-                
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime)",
-                
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
-    }
-
-    @Test public void exprAdjustDatetimeToTz_03() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T10:00:00-10:00"));}
 
-    @Test public void exprAdjustDatetimeToTz_04() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T07:00:00-10:00"));}
-
-    @Test public void exprAdjustDatetimeToTz_05() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-08T03:00:00+10:00"));}
-
-    @Test public void exprAdjustDatetimeToTz_06() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T00:00:00+01:00'^^xsd:dateTime,'-PT8H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-06T15:00:00-08:00"));}
-
-    @Test public void exprAdjustDatetimeToTz_07() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
-
-    @Test public void exprAdjustDatetimeToTz_08() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
-
-    @Test public void exprAdjustDateToTz_01(){
-        testEqual(
-                "fn:adjust-date-to-timezone('2002-03-07'^^xsd:date)",
-                
"fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
-    }
-
-    @Test public void exprAdjustDateToTz_02(){
-        testEqual(
-                "fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date)",
-                
"fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
-    }
-
-    @Test public void exprAdjustDateToTz_03() { 
test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-07-10:00"));}
-
-    @Test public void exprAdjustDateToTz_04() { 
test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-06-10:00"));}
-
-    @Test public void exprAdjustDateToTz_05() { 
test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
-
-    @Test public void exprAdjustDateToTz_06() { 
test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
-
-    @Test public void exprAdjustTimeToTz_01(){
-        testEqual(
-                "fn:adjust-time-to-timezone('10:00:00'^^xsd:time)",
-                
"fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
-    }
-
-    @Test public void exprAdjustTimeToTz_02(){
-        testEqual(
-                "fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time)",
-                
"fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
-    }
-
-    @Test public void exprAdjustTimeToTz_03() { 
test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("10:00:00-10:00",XSDDatatype.XSDtime));}
-
-    @Test public void exprAdjustTimeToTz_04() { 
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("07:00:00-10:00",XSDDatatype.XSDtime));}
-
-    @Test public void exprAdjustTimeToTz_05() { 
test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
-
-    @Test public void exprAdjustTimeToTz_06() { 
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
-
-    @Test public void exprAdjustTimeToTz_07() { 
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("03:00:00+10:00",XSDDatatype.XSDtime));}
-    //@Test public void exprStrJoin()      { test("fn:string-join('a', 'b')", 
NodeValue.makeString("ab")) ; }
-    
-    @Test public void localTimezone_1() { test("fn:implicit-timezone()", 
nv->nv.isDayTimeDuration()); }
-    
     // Better name!
     @Test public void localTimezone_2() { test("afn:timezone()", 
nv->nv.isDayTimeDuration()); }
     
@@ -462,59 +220,7 @@ public class TestFunctions
 
     @Test public void localDateTime_3() { test("afn:nowtz() = NOW()", 
NodeValue.TRUE); }
     
-    private static void testNumberFormat(String expression, String expected) {
-        Expr expr = ExprUtils.parse(expression) ;
-        NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
-        Assert.assertTrue(r.isString());
-        Assert.assertEquals(expected, r.getString()) ;
-    }
-    
-    /* The French grouping separator changed at Java13 because OpenJDK updated
-     * CLDR - Unicode Common Locale Data Repository.
-     * https://bugs.openjdk.java.net/browse/JDK-8225247
-     * 
-     * In the French locale, the grouping separator changes from U+00A0 ==> 
U+202F
-     * Portable tests ...
-     */
-    private static String GroupSepFR = 
Character.toString(DecimalFormatSymbols.getInstance(Locale.FRENCH).getGroupingSeparator());
-    
-    @Test public void formatNumber_01()     { 
testNumberFormat("fn:format-number(0,'#')", "0") ; }
-    @Test public void formatNumber_02()     { 
testNumberFormat("fn:format-number(1234, '#')", "1234") ; }
-    @Test public void formatNumber_03()     { 
testNumberFormat("fn:format-number(1234, '#,###')", "1,234") ; }
-    @Test public void formatNumber_04()     { 
testNumberFormat("fn:format-number(1e3, '#,###,###.#')", "1,000") ; }
-    @Test public void formatNumber_05()     { 
testNumberFormat("fn:format-number(10.5, '##.#')", "10.5") ; }
-    @Test public void formatNumber_06()     { 
testNumberFormat("fn:format-number(-10.5, '##.##')", "-10.5") ; }
-    @Test public void formatNumber_08()     { 
testNumberFormat("fn:format-number(123, 'NotAPattern')", "NotAPattern123") ; }
-    
-    @Test public void formatNumber_11()     { 
testNumberFormat("fn:format-number(0, '#', 'fr')", "0") ; }
-    // No-break space
-    @Test public void formatNumber_12()     { 
testNumberFormat("fn:format-number(1234.5,'#,###.#', 'fr')", 
"1"+GroupSepFR+"234,5") ; }
-    @Test public void formatNumber_13()     { 
testNumberFormat("fn:format-number(1234.5,'#,###.#', 'de')", "1.234,5") ; }
-    
-    @Test public void formatNumber_14()     { 
testNumberFormat("fn:format-number(12, '0,000.0', 'en')", "0,012.0") ; }
-    @Test public void formatNumber_15()     { 
testNumberFormat("fn:format-number(0, '00,000', 'fr')", "00"+GroupSepFR+"000") 
; }
-
-    @Test(expected=ExprEvalException.class)
-    public void formatNumber_20() {
-        // String as number
-        testNumberFormat("fn:format-number('String', '#')", null) ;
-    }
-    @Test(expected=ExprEvalException.class)
-    public void formatNumber_21() {
-        // Pattern is not a string
-        testNumberFormat("fn:format-number(123, <uri>)", null) ; 
-    }
-    @Test(expected=ExprEvalException.class)
-    public void formatNumber_22() {
-        // Locale is not a string
-        testNumberFormat("fn:format-number(123, '###', 123)", null) ; 
-    }
 
-    public void formatNumber_23() {
-        // Not a locale - default to Locale.ROOT
-        testNumberFormat("fn:format-number(123, '###', 'WhereAmI?')", null) ; 
-    }
-    
     @Test public void exprSameTerm1()     { test("sameTerm(1,1)",           
TRUE) ; }
     @Test public void exprSameTerm2()     { test("sameTerm(1,1.0)",         
FALSE) ; }
     @Test public void exprSameTerm3()     { test("sameTerm(1,1e0)",         
FALSE) ; }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java
index cfff012..48de47b 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java
@@ -21,14 +21,14 @@ package org.apache.jena.sparql.function.library;
 import static org.junit.Assert.assertEquals ;
 import static org.junit.Assert.assertTrue ;
 
-import org.apache.jena.graph.Node ;
+import java.util.function.Predicate;
+
 import org.apache.jena.shared.PrefixMapping ;
 import org.apache.jena.sparql.ARQConstants ;
 import org.apache.jena.sparql.expr.Expr ;
+import org.apache.jena.sparql.expr.LibTestExpr;
 import org.apache.jena.sparql.expr.NodeValue ;
-import org.apache.jena.sparql.function.FunctionEnvBase ;
 import org.apache.jena.sparql.util.ExprUtils ;
-import org.apache.jena.sparql.util.NodeFactoryExtra ;
 
 public class LibTest {
     private static PrefixMapping pmap = ARQConstants.getGlobalPrefixMap() ;
@@ -37,13 +37,21 @@ public class LibTest {
         test(string, "true");
     }
 
-    static void test(String string, String result) {
-        Expr expr = ExprUtils.parse(string, pmap) ;
-        NodeValue nv = expr.eval(null, new FunctionEnvBase()) ;
-        Node r = NodeFactoryExtra.parseNode(result) ;
-        NodeValue nvr = NodeValue.makeNode(r) ;
-        assertTrue("Not same value: Expected: " + nvr + " : Actual = " + nv, 
NodeValue.sameAs(nvr, nv)) ;
-        // test result must be lexical form exact.
-        assertEquals(r, nv.asNode()) ;
+    static void test(String exprStr, NodeValue result) {
+        Expr expr = ExprUtils.parse(exprStr) ;
+        NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
+        assertEquals(result, r) ;
+    }
+
+    static void test(String exprStr, String exprStrExpected) {
+        Expr expr = ExprUtils.parse(exprStrExpected) ;
+        NodeValue rExpected = expr.eval(null, LibTestExpr.createTest()) ;
+        test(exprStr, rExpected) ;
+    }
+    
+    static void test(String exprStr, Predicate<NodeValue> test) {
+        Expr expr = ExprUtils.parse(exprStr) ;
+        NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
+        assertTrue(exprStr, test.test(r));
     }
 }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
index 7d0830f..e8699aa 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
@@ -18,14 +18,40 @@
 
 package org.apache.jena.sparql.function.library;
 
+import org.apache.jena.sparql.expr.E_Function;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.runner.RunWith ;
 import org.junit.runners.Suite ;
 import org.junit.runners.Suite.SuiteClasses ;
 
 @RunWith(Suite.class)
 @SuiteClasses( {
-    // A lot of the test are in TestFunctions and TestFunctions2.
-    TestFunctionCollation.class
-    , TestFnFunctions.class
+    TestFnFunctionsBoolean.class
+    , TestFnFunctionsString.class
+    , TestFnFunctionsNumeric.class
+    , TestFnFunctionsDateTimeDuration.class
+    , TestFnFunctionsOther.class
+    , TestFnFunctionsFormat.class
+    , TestFnFunctionsCollation.class
 })
-public class TS_LibraryFunctions {}
+public class TS_LibraryFunctions {
+    // Expected warnings off.
+    private static boolean bVerboseWarnings ;
+    private static boolean bWarnOnUnknownFunction ;
+
+    @BeforeClass public static void beforeClass()
+    {
+        bVerboseWarnings = NodeValue.VerboseWarnings ;
+        bWarnOnUnknownFunction = E_Function.WarnOnUnknownFunction ;
+        NodeValue.VerboseWarnings = false ;
+        E_Function.WarnOnUnknownFunction = false ;
+    }
+
+    @AfterClass public static void afterClass()
+    {
+        NodeValue.VerboseWarnings = bVerboseWarnings ;
+        E_Function.WarnOnUnknownFunction = bWarnOnUnknownFunction ;
+    }
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsBoolean.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsBoolean.java
new file mode 100644
index 0000000..3ff62d7
--- /dev/null
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsBoolean.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import static org.apache.jena.sparql.expr.NodeValue.FALSE;
+import static org.apache.jena.sparql.expr.NodeValue.TRUE;
+import static org.apache.jena.sparql.expr.LibTestExpr.test;
+
+import org.apache.jena.sys.JenaSystem ;
+import org.junit.Test ;
+
+public class TestFnFunctionsBoolean {
+
+    static { JenaSystem.init(); }
+
+    @Test public void exprBoolean1()    { test("fn:boolean('')", FALSE) ; }
+    @Test public void exprBoolean2()    { test("fn:boolean(0)", FALSE) ; }
+    @Test public void exprBoolean3()    { test("fn:boolean(''^^xsd:string)", 
FALSE) ; }
+
+    @Test public void exprBoolean4()    { test("fn:boolean('X')", TRUE) ; }
+    @Test public void exprBoolean5()    { test("fn:boolean('X'^^xsd:string)", 
TRUE) ; }
+    @Test public void exprBoolean6()    { test("fn:boolean(1)", TRUE) ; }
+
+    @Test public void exprBoolean7()    { test("fn:not('')", TRUE) ; }
+    @Test public void exprBoolean8()    { test("fn:not('X')", FALSE) ; }
+    @Test public void exprBoolean9()    { test("fn:not(1)", FALSE) ; }
+    @Test public void exprBoolean10()   { test("fn:not(0)", TRUE) ; }
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java
similarity index 60%
rename from 
jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
rename to 
jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java
index 8efcc78..4b6a2c0 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsCollation.java
@@ -18,39 +18,57 @@
 
 package org.apache.jena.sparql.function.library;
 
+import static org.apache.jena.sparql.expr.LibTestExpr.test;
 import static org.junit.Assert.assertArrayEquals;
 
 import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.junit.Test;
 
 /**
  * Tests for {@link collation}.
  */
-public class TestFunctionCollation {
+public class TestFnFunctionsCollation {
     @Test
     public void testFunctionCollationExec() {
         collation function = new collation();
         NodeValue collation = NodeValue.makeString("fi");
-        
-        final String[] unordered = new String[]
-                {"tšekin kieli", "tulun kieli", "töyhtöhyyppä", "tsahurin 
kieli", "tsahurin kieli", "tulun kieli"};
-        String[] ordered = new String[]
-                {"'tsahurin kieli'", "'tsahurin kieli'", "'tšekin kieli'",
-                        "'tulun kieli'", "'tulun kieli'", "'töyhtöhyyppä'"};
-        // tests collation sort order with Danish words, but New Zealand 
English collation rules
+
+        final String[] unordered = {"tšekin kieli" , "tulun kieli", 
"töyhtöhyyppä",
+                                    "tsahurin kieli", "tsahurin kieli", "tulun 
kieli"};
+        String[] ordered = {"'tsahurin kieli'", "'tsahurin kieli'", "'tšekin 
kieli'",
+                            "'tulun kieli'", "'tulun kieli'", 
"'töyhtöhyyppä'"};
+        // tests collation sort order with Danish words, but New Zealand 
English
+        // collation rules
         List<NodeValue> nodeValues = new LinkedList<>();
-        for (String string : unordered) {
+        for ( String string : unordered ) {
             nodeValues.add(function.exec(collation, 
NodeValue.makeString(string)));
         }
-        nodeValues.sort((NodeValue o1, NodeValue o2) -> NodeValue.compare(o1, 
o2) );
+        nodeValues.sort((NodeValue o1, NodeValue o2) -> NodeValue.compare(o1, 
o2));
         List<String> result = new LinkedList<>();
-        for (NodeValue nv : nodeValues) {
+        for ( NodeValue nv : nodeValues ) {
             String s = nv.toString();
             result.add(s);
         }
         assertArrayEquals(ordered, result.toArray(new String[0]));
     }
+
+    @Test
+    public void collationKey_1() {
+        test("fn:collation-key('foo', 'en') = 'Zm9vQGVu'^^xsd:base64Binary");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void collationKey_2() {
+        test("fn:collation-key('foo', 22) = 'Zm9vQGVu'^^xsd:base64Binary");
+    }
+
+    @Test(expected = ExprEvalException.class)
+    public void collationKey_3() {
+        test("fn:collation-key(<x:bar>, 'en') = 'Zm9vQGVu'^^xsd:base64Binary");
+    }
+
 }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsDateTimeDuration.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsDateTimeDuration.java
new file mode 100644
index 0000000..702a426
--- /dev/null
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsDateTimeDuration.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+import static org.apache.jena.sparql.expr.LibTestExpr.test;
+import static org.junit.Assert.fail;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.LibTestExpr;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.apache.jena.sys.JenaSystem ;
+import org.junit.Test ;
+
+public class TestFnFunctionsDateTimeDuration {
+    static { JenaSystem.init(); }
+
+    @Test
+    public void datetime_1() {
+        test("fn:dateTime('2017-09-14'^^xsd:date, '10:11:23'^^xsd:time) = 
'2017-09-14T10:11:23'^^xsd:dateTime");
+    }
+
+    @Test
+    public void datetime_2() {
+        test("fn:dateTime('2017-09-14+01:00'^^xsd:date, '10:11:23'^^xsd:time) 
= '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
+    }
+
+    @Test
+    public void datetime_3() {
+        test("fn:dateTime('2017-09-14'^^xsd:date, '10:11:23+01:00'^^xsd:time) 
= '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
+    }
+
+    @Test
+    public void datetime_4() {
+        test("fn:dateTime('2017-09-14+01:00'^^xsd:date, 
'10:11:23+01:00'^^xsd:time) = '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
+    }
+
+    @Test(expected=ExprEvalException.class)
+    public void datetime_5() {
+        // Incompatible timezones.
+        test("fn:dateTime('2017-09-14+09:00'^^xsd:date, 
'10:11:23+01:00'^^xsd:time) = '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
+    }
+
+    @Test(expected=ExprEvalException.class)
+    public void datetime_6() {
+        // Bad date
+        test("fn:dateTime('xyz', '10:11:23+01:00'^^xsd:time) = 
'2017-09-14T10:11:23+01:00'^^xsd:dateTime");
+    }
+
+    @Test(expected=ExprEvalException.class)
+    public void datetime_7() {
+        // Bad time
+        test("fn:dateTime('2017-09-14+09:00', 'now'^^xsd:time) = 
'2017-09-14T10:11:23+01:00'^^xsd:dateTime");
+    }
+
+    @Test public void fromDateTime() {
+        test("fn:years-from-dateTime('2017-09-14T17:01:02'^^xsd:dateTime)",   
"2017");
+        test("fn:months-from-dateTime('2017-09-14T17:01:02'^^xsd:dateTime)",  
"09");
+        test("fn:days-from-dateTime('2017-09-14T17:01:02'^^xsd:dateTime)",    
"14");
+        test("fn:hours-from-dateTime('2017-09-14T17:01:02'^^xsd:dateTime)",   
"17");
+        test("fn:minutes-from-dateTime('2017-09-14T17:01:02'^^xsd:dateTime)", 
"01");
+        
test("fn:seconds-from-dateTime('2017-09-14T17:01:02.5'^^xsd:dateTime)", "02.5");
+        
test("fn:timezone-from-dateTime('2017-09-14T17:01:02+01:00'^^xsd:dateTime)", 
"'PT1H'^^xsd:dayTimeDuration");
+    }
+
+    @Test public void fromDate() {
+        test("fn:years-from-date('2017-09-14'^^xsd:date)",   "2017");
+        test("fn:months-from-date('2017-09-14'^^xsd:date)",  "09");
+        test("fn:days-from-date('2017-09-14'^^xsd:date)",    "14");
+        test("fn:timezone-from-date('2017-09-14+01:00'^^xsd:date)", 
"'PT1H'^^xsd:dayTimeDuration");
+    }
+
+    @Test public void fromTime() {
+        test("fn:hours-from-time('17:01:02'^^xsd:time)",   "17");
+        test("fn:minutes-from-time('17:01:02'^^xsd:time)", "01");
+        test("fn:seconds-from-time('17:01:02.5'^^xsd:time)", "02.5");
+        test("fn:timezone-from-time('17:01:02+01:00'^^xsd:time)", 
"'PT1H'^^xsd:dayTimeDuration");
+    }
+
+    @Test public void fromDuration() {
+        String s = "'P1Y2M3DT4H5M6.7S'^^xsd:duration";
+        test("fn:years-from-duration("+s+")",    "1");
+        test("fn:months-from-duration("+s+")",   "2");
+        test("fn:days-from-duration("+s+")",     "3");
+        test("fn:hours-from-duration("+s+")",    "4");
+        test("fn:minutes-from-duration("+s+")",  "5");
+        test("fn:seconds-from-duration("+s+")",  "6.7");
+    }
+
+    @Test public void fromMisc() {
+        test("fn:hours-from-time('17:01:02'^^xsd:time)",   "17");
+        test("fn:minutes-from-time('17:01:02'^^xsd:time)", "01");
+        test("fn:seconds-from-time('17:01:02.5'^^xsd:time)", "02.5");
+        test("fn:timezone-from-time('17:01:02+01:00'^^xsd:time)", 
"'PT1H'^^xsd:dayTimeDuration");
+    }
+
+    @Test public void from_strict() {
+        // ARQ is lax about exact datatypes types unless in strict mode.
+        // If the datatytpe has "days" (xsd:date, xsd;dateTime, xsd:duration 
and derived types)
+        // the normal operating mode for "days-from-*" is to return the days
+        // regardless of the specific datatype.
+        ARQ.getContext().set(ARQ.strictSPARQL, true);
+        try {
+            testException("fn:years-from-dateTime('2017-09-14'^^xsd:date)");
+            
testException("fn:months-from-date('P1Y2M3DT4H5M6.7S'^^xsd:duration)");
+            
testException("fn:days-from-time('2017-09-14T17:01:02'^^xsd:dateTime)");
+        } finally {
+            ARQ.getContext().set(ARQ.strictSPARQL, false);
+        }
+    }
+
+    private static void testException(String exprStr) {
+
+        Expr expr = ExprUtils.parse(exprStr) ;
+        try {
+            NodeValue rExpected = expr.eval(null, LibTestExpr.createTest()) ;
+            fail("Expected exception: "+exprStr);
+        } catch ( ExprEvalException ex) {}
+    }
+
+    @Test public void exprAdjustDatetimeToTz_01(){
+        test(
+            
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime)",
+            
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
+    }
+
+    @Test public void exprAdjustDatetimeToTz_02(){
+        test(
+            
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime)",
+            
"fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
+    }
+
+    @Test public void exprAdjustDatetimeToTz_03() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T10:00:00-10:00"));}
+
+    @Test public void exprAdjustDatetimeToTz_04() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-07T07:00:00-10:00"));}
+
+    @Test public void exprAdjustDatetimeToTz_05() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-08T03:00:00+10:00"));}
+
+    @Test public void exprAdjustDatetimeToTz_06() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T00:00:00+01:00'^^xsd:dateTime,'-PT8H'^^xsd:dayTimeDuration)",NodeValue.makeDateTime("2002-03-06T15:00:00-08:00"));}
+
+    @Test public void exprAdjustDatetimeToTz_07() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
+
+    @Test public void exprAdjustDatetimeToTz_08() { 
test("fn:adjust-dateTime-to-timezone('2002-03-07T10:00:00-07:00'^^xsd:dateTime,'')",NodeValue.makeDateTime("2002-03-07T10:00:00"));}
+
+    @Test public void exprAdjustDateToTz_01(){
+        test(
+            "fn:adjust-date-to-timezone('2002-03-07'^^xsd:date)",
+            
"fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
+    }
+
+    @Test public void exprAdjustDateToTz_02(){
+        test(
+            "fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date)",
+            
"fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
+    }
+
+    @Test public void exprAdjustDateToTz_03() { 
test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-07-10:00"));}
+
+    @Test public void exprAdjustDateToTz_04() { 
test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeDate("2002-03-06-10:00"));}
+
+    @Test public void exprAdjustDateToTz_05() { 
test("fn:adjust-date-to-timezone('2002-03-07'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
+
+    @Test public void exprAdjustDateToTz_06() { 
test("fn:adjust-date-to-timezone('2002-03-07-07:00'^^xsd:date,'')",NodeValue.makeDate("2002-03-07"));}
+
+    @Test public void exprAdjustTimeToTz_01(){
+        test(
+            "fn:adjust-time-to-timezone('10:00:00'^^xsd:time)",
+            
"fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
+    }
+
+    @Test public void exprAdjustTimeToTz_02(){
+        test(
+            "fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time)",
+            
"fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'"+getDynamicDurationString()+"'^^xsd:dayTimeDuration)");
+    }
+
+    @Test public void exprAdjustTimeToTz_03() { 
test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("10:00:00-10:00",XSDDatatype.XSDtime));}
+
+    @Test public void exprAdjustTimeToTz_04() { 
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'-PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("07:00:00-10:00",XSDDatatype.XSDtime));}
+
+    @Test public void exprAdjustTimeToTz_05() { 
test("fn:adjust-time-to-timezone('10:00:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
+
+    @Test public void exprAdjustTimeToTz_06() { 
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'')",NodeValue.makeNode("10:00:00",XSDDatatype.XSDtime));}
+
+    @Test public void exprAdjustTimeToTz_07() { 
test("fn:adjust-time-to-timezone('10:00:00-07:00'^^xsd:time,'PT10H'^^xsd:dayTimeDuration)",NodeValue.makeNode("03:00:00+10:00",XSDDatatype.XSDtime));}
+    //@Test public void exprStrJoin()      { test("fn:string-join('a', 'b')", 
NodeValue.makeString("ab")) ; }
+
+    @Test public void localTimezone_1() { test("fn:implicit-timezone()", 
nv->nv.isDayTimeDuration()); }
+
+    private String getDynamicDurationString(){
+        int tzOffset = TimeZone.getDefault().getOffset(new Date().getTime()) / 
(1000*60);
+        String off = "PT"+Math.abs(tzOffset)+"M";
+        if(tzOffset < 0)
+            off = "-"+off;
+        return off;
+    }
+
+
+
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsFormat.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsFormat.java
new file mode 100644
index 0000000..fd5f61a
--- /dev/null
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsFormat.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+
+import org.apache.jena.sparql.expr.*;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.apache.jena.sys.JenaSystem ;
+import org.junit.Assert;
+import org.junit.Test ;
+
+public class TestFnFunctionsFormat {
+
+    static { JenaSystem.init(); }
+    
+    /* The French grouping separator changed at Java13 because OpenJDK updated
+     * CLDR - Unicode Common Locale Data Repository.
+     * https://bugs.openjdk.java.net/browse/JDK-8225247
+     * 
+     * In the French locale, the grouping separator changes from U+00A0 ==> 
U+202F
+     * Portable tests ...
+     */
+    private static String GroupSepFR = 
Character.toString(DecimalFormatSymbols.getInstance(Locale.FRENCH).getGroupingSeparator());
+    
+    @Test public void formatNumber_01()     { 
testNumberFormat("fn:format-number(0,'#')", "0") ; }
+    @Test public void formatNumber_02()     { 
testNumberFormat("fn:format-number(1234, '#')", "1234") ; }
+    @Test public void formatNumber_03()     { 
testNumberFormat("fn:format-number(1234, '#,###')", "1,234") ; }
+    @Test public void formatNumber_04()     { 
testNumberFormat("fn:format-number(1e3, '#,###,###.#')", "1,000") ; }
+    @Test public void formatNumber_05()     { 
testNumberFormat("fn:format-number(10.5, '##.#')", "10.5") ; }
+    @Test public void formatNumber_06()     { 
testNumberFormat("fn:format-number(-10.5, '##.##')", "-10.5") ; }
+    @Test public void formatNumber_08()     { 
testNumberFormat("fn:format-number(123, 'NotAPattern')", "NotAPattern123") ; }
+    
+    @Test public void formatNumber_11()     { 
testNumberFormat("fn:format-number(0, '#', 'fr')", "0") ; }
+    // No-break space
+    @Test public void formatNumber_12()     { 
testNumberFormat("fn:format-number(1234.5,'#,###.#', 'fr')", 
"1"+GroupSepFR+"234,5") ; }
+    @Test public void formatNumber_13()     { 
testNumberFormat("fn:format-number(1234.5,'#,###.#', 'de')", "1.234,5") ; }
+    
+    @Test public void formatNumber_14()     { 
testNumberFormat("fn:format-number(12, '0,000.0', 'en')", "0,012.0") ; }
+    @Test public void formatNumber_15()     { 
testNumberFormat("fn:format-number(0, '00,000', 'fr')", "00"+GroupSepFR+"000") 
; }
+
+    @Test(expected=ExprEvalException.class)
+    public void formatNumber_20() {
+        // String as number
+        testNumberFormat("fn:format-number('String', '#')", null) ;
+    }
+    @Test(expected=ExprEvalException.class)
+    public void formatNumber_21() {
+        // Pattern is not a string
+        testNumberFormat("fn:format-number(123, <uri>)", null) ; 
+    }
+    @Test(expected=ExprEvalException.class)
+    public void formatNumber_22() {
+        // Locale is not a string
+        testNumberFormat("fn:format-number(123, '###', 123)", null) ; 
+    }
+
+    public void formatNumber_23() {
+        // Not a locale - default to Locale.ROOT
+        testNumberFormat("fn:format-number(123, '###', 'WhereAmI?')", null) ; 
+    }
+    private static void testNumberFormat(String expression, String expected) {
+        Expr expr = ExprUtils.parse(expression) ;
+        NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
+        Assert.assertTrue(r.isString());
+        Assert.assertEquals(expected, r.getString()) ;
+    }
+
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsNumeric.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsNumeric.java
new file mode 100644
index 0000000..aebe074
--- /dev/null
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsNumeric.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import static org.apache.jena.sparql.expr.LibTestExpr.test;
+
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sys.JenaSystem;
+import org.junit.Test;
+
+public class TestFnFunctionsNumeric {
+
+    static { JenaSystem.init(); }
+
+    @Test public void exprRound_01()    { test("fn:round(123)",   
NodeValue.makeInteger(123)) ; }
+    @Test public void exprRound_02()    { test("fn:round(123.5)",  
NodeValue.makeDecimal(124)) ; }
+    @Test public void exprRound_03()    { test("fn:round(-0.5e0)", 
NodeValue.makeDouble(0.0e0)) ; }
+    @Test public void exprRound_04()    { test("fn:round(-1.5)",   
NodeValue.makeDecimal(-1)) ; }
+    // !! I don't think that this is working correctly also if the test is 
passing... need to check!
+    @Test public void exprRound_05()    { test("fn:round(-0)",     
NodeValue.makeInteger("-0")) ; }
+    @Test public void exprRound_06()    { test("fn:round(1.125, 2)",     
NodeValue.makeDecimal(1.13)) ; }
+    @Test public void exprRound_07()    { test("fn:round(8452, -2)",     
NodeValue.makeInteger(8500)) ; }
+    @Test public void exprRound_08()    { test("fn:round(3.1415e0, 2)",     
NodeValue.makeDouble(3.14e0)) ; }
+    // counter-intuitive -- would fail if float/double not translated to 
decimal
+    @Test public void exprRound_09()    { test("fn:round(35.425e0, 2)",     
NodeValue.makeDouble(35.42)) ; }
+
+    @Test public void exprRoundHalfEven_01()    { 
test("fn:round-half-to-even(0.5)",   NodeValue.makeDecimal(0)) ; }
+    @Test public void exprRoundHalfEven_02()    { 
test("fn:round-half-to-even(1.5)",  NodeValue.makeDecimal(2)) ; }
+    @Test public void exprRoundHalfEven_03()    { 
test("fn:round-half-to-even(2.5)", NodeValue.makeDecimal(2)) ; }
+    @Test public void exprRoundHalfEven_04()    { 
test("fn:round-half-to-even(3.567812e+3, 2)",   
NodeValue.makeDouble(3567.81e0)) ; }
+    // !! I don't think that this is working correctly also if the test is 
passing... need to check!
+    @Test public void exprRoundHalfEven_05()    { 
test("fn:round-half-to-even(-0)",     NodeValue.makeInteger(-0)) ; }
+    @Test public void exprRoundHalfEven_06()    { 
test("fn:round-half-to-even(4.7564e-3, 2)",     NodeValue.makeDouble(0.0e0)) ; }
+    @Test public void exprRoundHalfEven_07()    { 
test("fn:round-half-to-even(35612.25, -2)",     NodeValue.makeDecimal(35600)) ; 
}
+    // counter-intuitive -- would fail if float/double not translated to 
decimal
+    @Test public void exprRoundHalfEven_08()    { 
test("fn:round-half-to-even('150.015'^^xsd:float, 2)",     
NodeValue.makeFloat((float)150.01)) ; }
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsOther.java
similarity index 51%
rename from 
jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java
rename to 
jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsOther.java
index 8f2436a..7dc7c4b 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsOther.java
@@ -18,7 +18,7 @@
 
 package org.apache.jena.sparql.function.library;
 
-import static org.apache.jena.sparql.function.library.LibTest.test;
+import static org.apache.jena.sparql.expr.LibTestExpr.test;
 
 import org.apache.jena.sparql.expr.ExprEvalException ;
 import org.apache.jena.sparql.expr.ExprException ;
@@ -26,14 +26,15 @@ import 
org.apache.jena.sparql.expr.VariableNotBoundException ;
 import org.apache.jena.sys.JenaSystem ;
 import org.junit.Test ;
 
-public class TestFnFunctions {
+/** "Other" functions */
+public class TestFnFunctionsOther {
 
     static { JenaSystem.init(); }
-    
+
     @Test public void apply_1() {
         test("fn:apply(math:sqrt, 9)", "3.0e0");
     }
-    
+
     // Under-arity
     @Test(expected=ExprEvalException.class)
     public void apply_2() {
@@ -56,7 +57,7 @@ public class TestFnFunctions {
     public void apply_5() {
         test("fn:apply(?var)", "3.0e0");
     }
-    
+
     @Test(expected=ExprEvalException.class)
     public void apply_6() {
         test("fn:apply(<x:unregistered>)", "false");
@@ -66,58 +67,4 @@ public class TestFnFunctions {
     public void apply_7() {
         test("fn:apply()", "false");
     }
-
-    @Test
-    public void collationKey_1() {
-        test("fn:collation-key('foo', 'en') = 'Zm9vQGVu'^^xsd:base64Binary");
-    }
-    
-    @Test(expected=ExprEvalException.class)
-    public void collationKey_2() {
-        test("fn:collation-key('foo', 22) = 'Zm9vQGVu'^^xsd:base64Binary");
-    }
-
-    @Test(expected=ExprEvalException.class)
-    public void collationKey_3() {
-        test("fn:collation-key(<x:bar>, 'en') = 'Zm9vQGVu'^^xsd:base64Binary");
-    }
-
-    @Test
-    public void datetime_1() {
-        test("fn:dateTime('2017-09-14'^^xsd:date, '10:11:23'^^xsd:time) = 
'2017-09-14T10:11:23'^^xsd:dateTime");
-    }
-
-    @Test
-    public void datetime_2() {
-        test("fn:dateTime('2017-09-14+01:00'^^xsd:date, '10:11:23'^^xsd:time) 
= '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
-    }
-    
-    @Test
-    public void datetime_3() {
-        test("fn:dateTime('2017-09-14'^^xsd:date, '10:11:23+01:00'^^xsd:time) 
= '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
-    }
-
-    @Test
-    public void datetime_4() {
-        test("fn:dateTime('2017-09-14+01:00'^^xsd:date, 
'10:11:23+01:00'^^xsd:time) = '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
-    }
-    
-    @Test(expected=ExprEvalException.class)
-    public void datetime_5() {
-        // Incompatible timezones.
-        test("fn:dateTime('2017-09-14+09:00'^^xsd:date, 
'10:11:23+01:00'^^xsd:time) = '2017-09-14T10:11:23+01:00'^^xsd:dateTime");
-    }
-
-    @Test(expected=ExprEvalException.class)
-    public void datetime_6() {
-        // Bad date
-        test("fn:dateTime('xyz', '10:11:23+01:00'^^xsd:time) = 
'2017-09-14T10:11:23+01:00'^^xsd:dateTime");
-    }
-    
-    @Test(expected=ExprEvalException.class)
-    public void datetime_7() {
-        // Bad time
-        test("fn:dateTime('2017-09-14+09:00', 'now'^^xsd:time) = 
'2017-09-14T10:11:23+01:00'^^xsd:dateTime");
-    }
-
 }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsString.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsString.java
new file mode 100644
index 0000000..67dce3e
--- /dev/null
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctionsString.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import static org.apache.jena.sparql.expr.NodeValue.FALSE;
+import static org.apache.jena.sparql.expr.NodeValue.TRUE;
+import static org.apache.jena.sparql.expr.NodeValue.nvONE;
+import static org.apache.jena.sparql.expr.NodeValue.nvZERO;
+import static org.apache.jena.sparql.expr.LibTestExpr.test;
+import static org.junit.Assert.fail;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.LibTestExpr;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.util.ExprUtils;
+import org.apache.jena.sys.JenaSystem ;
+import org.junit.Test ;
+
+public class TestFnFunctionsString {
+
+    static { JenaSystem.init(); }
+
+    @Test public void exprStrLen1() { test("fn:string-length('')", nvZERO) ; }
+    @Test public void exprStrLen2() { test("fn:string-length('a')", nvONE) ; }
+    // Test from JENA-785
+    @Test public void exprStrLen3() { test("fn:string-length('𐐈𐑌𐐻𐐪𐑉𐐿𐐻𐐮𐐿𐐲')", 
NodeValue.makeInteger(10l)) ; }
+
+    // F&O strings are one-based, and substring takes a length
+    @Test public void exprSubstring1() { test("fn:substring('',0)", 
NodeValue.makeString("")) ; }
+    @Test public void exprSubstring2() { test("fn:substring('',1)", 
NodeValue.makeString("")) ; }
+    @Test public void exprSubstring3() { test("fn:substring('',1,0)", 
NodeValue.makeString("")) ; }
+    @Test public void exprSubstring4() { test("fn:substring('',1,1)", 
NodeValue.makeString("")) ; }
+
+    @Test public void exprSubstring5() { test("fn:substring('abc',1)", 
NodeValue.makeString("abc")) ; }
+    @Test public void exprSubstring6() { test("fn:substring('abc',2)", 
NodeValue.makeString("bc")) ; }
+    @Test public void exprSubstring7() { test("fn:substring('a',1,1)", 
NodeValue.makeString("a")) ; }
+    @Test public void exprSubstring8() { test("fn:substring('a',1,2)", 
NodeValue.makeString("a")) ; }
+    @Test public void exprSubstring9() { test("fn:substring('a',0)", 
NodeValue.makeString("")) ; }
+
+    // Uses round()
+    @Test public void exprSubstring10() { test("fn:substring('abc',1.6,1.33)", 
NodeValue.makeString("b")) ; }
+    // This test was added because the test suite had 1199 tests in.
+    @Test public void exprSubstring11() { test("fn:substring('abc',-1, 
-15.3)", NodeValue.makeString("")) ; }
+    // Test from JENA-785
+    @Test public void exprSubstring12() { test("fn:substring('𐐈𐑌𐐻𐐪𐑉𐐿𐐻𐐮𐐿𐐲', 1, 
1)", NodeValue.makeString("𐐈")) ; }
+
+    @Test public void exprStrStart0() { test("fn:starts-with('abc', '')", 
TRUE) ; }
+    @Test public void exprStrStart1() { test("fn:starts-with('abc', 'a')", 
TRUE) ; }
+    @Test public void exprStrStart2() { test("fn:starts-with('abc', 'ab')", 
TRUE) ; }
+    @Test public void exprStrStart3() { test("fn:starts-with('abc', 'abc')", 
TRUE) ; }
+    @Test public void exprStrStart4() { test("fn:starts-with('abc', 'abcd')", 
FALSE) ; }
+
+        @Test public void exprStrEnds0() { test("fn:ends-with('abc', '')", 
TRUE) ; }
+    @Test public void exprStrEnds1() { test("fn:ends-with('abc', 'c')", TRUE) 
; }
+    @Test public void exprStrEnds2() { test("fn:ends-with('abc', 'bc')", TRUE) 
; }
+    @Test public void exprStrEnds3() { test("fn:ends-with('abc', 'abc')", 
TRUE) ; }
+    @Test public void exprStrEnds4() { test("fn:ends-with('abc', 'zabc')", 
FALSE) ; }
+
+    @Test public void exprStrCase1() { test("fn:lower-case('aBc')", 
NodeValue.makeString("abc")) ; }
+    @Test public void exprStrCase2() { test("fn:lower-case('abc')", 
NodeValue.makeString("abc")) ; }
+    @Test public void exprStrCase3() { test("fn:upper-case('abc')", 
NodeValue.makeString("ABC")) ; }
+    @Test public void exprStrCase4() { test("fn:upper-case('ABC')", 
NodeValue.makeString("ABC")) ; }
+
+    @Test public void exprStrContains0() { test("fn:contains('abc', '')", 
TRUE) ; }
+    @Test public void exprStrContains1() { test("fn:contains('abc', 'a')", 
TRUE) ; }
+    @Test public void exprStrContains2() { test("fn:contains('abc', 'b')", 
TRUE) ; }
+    @Test public void exprStrContains3() { test("fn:contains('abc', 'c')", 
TRUE) ; }
+
+    @Test public void exprStrContains4() { test("fn:contains('abc', 'ab')", 
TRUE) ; }
+    @Test public void exprStrContains5() { test("fn:contains('abc', 'bc')", 
TRUE) ; }
+    @Test public void exprStrContains6() { test("fn:contains('abc', 'abc')", 
TRUE) ; }
+    @Test public void exprStrContains7() { test("fn:contains('abc', 'Xc')", 
FALSE) ; }
+    @Test public void exprStrContains8() { test("fn:contains('abc', 'Xa')", 
FALSE) ; }
+
+    @Test public void exprStrNormalizeSpace0() { test("fn:normalize-space(' 
The    wealthy curled darlings                                         of    
our    nation. ')",
+            NodeValue.makeString("The wealthy curled darlings of our 
nation.")) ; }
+    @Test public void exprStrNormalizeSpace1() { 
test("fn:normalize-space('')",NodeValue.nvEmptyString) ; }
+    @Test public void exprStrNormalizeSpace2() { test("fn:normalize-space('   
Aaa     ')",NodeValue.makeString("Aaa")) ; }
+    @Test public void exprStrNormalizeSpace3() { test("fn:normalize-space('A a 
  a    a a    ')",NodeValue.makeString("A a a a a")) ; }
+
+    // https://www.w3.org/TR/xpath-functions-30/#func-normalize-unicode
+    // and
+    // from http://www.unicode.org/reports/tr15/
+    //l
+    @Test public void exprStrNormalizeUnicode0() { 
test("fn:normalize-unicode('Äffin','nfd')",NodeValue.makeString("Äffin")) ; }
+    @Test public void exprStrNormalizeUnicode1() { 
test("fn:normalize-unicode('Äffin','nfc')",NodeValue.makeString("Äffin")) ; }
+    //m
+    @Test public void exprStrNormalizeUnicode2() { 
test("fn:normalize-unicode('Ä\\uFB03n','nfd')",NodeValue.makeString("Äffin")) ; }
+    @Test public void exprStrNormalizeUnicode3() { 
test("fn:normalize-unicode('Ä\\uFB03n','nfc')",NodeValue.makeString("Äffin")) ; }
+    //n
+    @Test public void exprStrNormalizeUnicode4() { 
test("fn:normalize-unicode('Henry IV','nfd')",NodeValue.makeString("Henry IV")) 
; }
+    @Test public void exprStrNormalizeUnicode5() { 
test("fn:normalize-unicode('Henry IV','nfc')",NodeValue.makeString("Henry IV")) 
; }
+    //l'
+    @Test public void exprStrNormalizeUnicode6() { 
test("fn:normalize-unicode('Äffin','nfkd')",NodeValue.makeString("Äffin")) ; }
+    @Test public void exprStrNormalizeUnicode7() { 
test("fn:normalize-unicode('Äffin','nfkc')",NodeValue.makeString("Äffin")) ; }
+    // r
+    String hw_ka="\uFF76";
+    String hw_ten="\uFF9F";
+    @Test public void exprStrNormalizeUnicode8() { 
test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfd')",NodeValue.makeString(hw_ka+hw_ten))
 ; }
+    @Test public void exprStrNormalizeUnicode9() {
+        
test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfc')",NodeValue.makeString(hw_ka+hw_ten))
 ;
+    }
+    // Not sure why the following tests are not passing
+    // both examples are taken from the http://www.unicode.org/reports/tr15/ 
(Table 8 r')
+    // the translation of hw_ka,hw_ten,ka and ten are taken from Table 4 of 
the same document
+    //
+    // I (Alessandro Seganti) took the ga translation by association (it was 
not defined in the unicode report)
+    // and chosen to be: KATAKANA LETTER GA U+30AC
+    // Everything seems ok to me so there are two options in my opinion:
+    // 1) the java implementation of the nfkd has some flaws
+    // 2) the unicode example is wrong (I cannot judge as I do not know 
japanese or unicode enough :))
+    // The test is failing because the expected string has code when looking 
in the debugger (UTF-16?) (12459 | 12442)
+    // while the Nomalizer.normalize is giving  (12459 | 12441)
+//    @Test public void exprStrNormalizeUnicode10() {
+//        String ka = "\u30AB";
+//        String ten="\u3099";
+//        test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfkd')", 
NodeValue.makeString(ka+ten)) ;
+//    }
+//    @Test public void exprStrNormalizeUnicode11() {
+//        String ga="\u30AC";
+//        
test("fn:normalize-unicode('"+hw_ka+hw_ten+"','nfkc')",NodeValue.makeString(ga))
 ;
+//    }
+
+    // empty argument <-> returns the input string
+    @Test public void exprStrNormalizeUnicode12() { 
test("fn:normalize-unicode('some word','')",NodeValue.makeString("some word")) 
; }
+    // one argument <-> NFC
+    @Test public void exprStrNormalizeUnicode13() { 
test("fn:normalize-unicode('Äffin')",NodeValue.makeString("Äffin")) ; }
+
+    @Test public void exprFnReplace01()  { test("fn:replace('abc', 'b', 'Z')", 
NodeValue.makeString("aZc")) ; }
+    @Test public void exprFnReplace02()  { test("fn:replace('abc', 'b.', 
'Z')", NodeValue.makeString("aZ")) ; }
+    @Test public void exprFnReplace03()  { test("fn:replace('abcbd', 'b.', 
'Z')", NodeValue.makeString("aZZ")) ; }
+
+    @Test public void exprFnReplace04()  { 
test("fn:replace('abcbd'^^xsd:string, 'b.', 'Z')", NodeValue.makeNode("aZZ", 
XSDDatatype.XSDstring)) ; }
+    @Test public void exprFnReplace05()  { test("fn:replace('abcbd'@en, 'b.', 
'Z')", NodeValue.makeNode("aZZ", "en", (String)null)) ; }
+    @Test public void exprFnReplace06()  { test("fn:replace('abcbd', 'B.', 
'Z', 'i')", NodeValue.makeString("aZZ")) ; }
+
+
+    // Bad pattern : dynamic (eval time) exception.
+    // The pattern for fn:replace is not compiled on build - if that changes, 
this test will fail.
+    // See exprReplace14.
+    @Test
+    public void exprReplace15() {
+        testEvalException("fn:replace('abc', '^(a){-9}', 'ABC')");
+    }
+
+    private void testEvalException(String exprStr) {
+        Expr expr = ExprUtils.parse(exprStr) ;
+        try {
+            NodeValue r = expr.eval(null, LibTestExpr.createTest()) ;
+            fail("No exception raised") ;
+        }
+        catch (ExprEvalException ex) {}
+    }
+
+
+}

Reply via email to