Implement New JPA API 3.1.0 Features

Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java
index f671f45..6497c7b 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/Expression.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2021 IBM Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -15,6 +15,8 @@
 //     Oracle - initial API and implementation from Oracle TopLink
 //     05/24/2011-2.3 Guy Pelletier
 //       - 345962: Join fetch query when using tenant discriminator column fails.
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.expressions;
 
 import java.io.BufferedWriter;
@@ -1198,6 +1200,34 @@
 
     /**
      * PUBLIC:
+     * This gives access to the local date and time on the database through expression.
+     */
+    public Expression localDateTime() {
+        return getFunction(ExpressionOperator.LocalDateTime);
+    }
+
+    /**
+     * PUBLIC:
+     * This gives access to the local date only on the database through expression.
+     * Note the difference between localDate() and this method. This method does
+     * not return the time portion of local datetime where as localDate() does.
+     */
+    public Expression localDate() {
+        return getFunction(ExpressionOperator.LocalDate);
+    }
+
+    /**
+     * PUBLIC:
+     * This gives access to the local time only on the database through expression.
+     * Note the difference between localDate() and this method. This method does
+     * not return the date portion of local datetime where as localDate() does.
+     */
+    public Expression localTime() {
+        return getFunction(ExpressionOperator.LocalTime);
+    }
+
+    /**
+     * PUBLIC:
      * Function, Return the difference between the queried part of a date(i.e. years, days etc.)
      * and same part of the given date. The equivalent of the Sybase function DateDiff
      * <p>Example:
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
index be93a0c..3f0cf97 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018, 2021 IBM Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -18,6 +18,8 @@
 //       - 263904: [PATCH] ExpressionOperator doesn't compare arrays correctly
 //     01/23/2018-2.7 Will Dazey
 //       - 530214: trim operation should not bind parameters
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.expressions;
 
 import java.io.IOException;
@@ -215,6 +217,10 @@
      */
     public static final int CurrentTime = 128;
 
+    public static final int LocalDate = 149;
+    public static final int LocalTime = 150;
+    public static final int LocalDateTime = 151;
+
     // Math
     public static final int Ceil = 55;
     public static final int Cos = 56;
@@ -2982,6 +2988,30 @@
 
     /**
      * INTERNAL:
+     * Build operator.
+     */
+    public static ExpressionOperator localDate() {
+        return simpleFunctionNoParentheses(LocalDate,  "CURRENT_DATE");
+    }
+
+    /**
+     * INTERNAL:
+     * Build operator.
+     */
+    public static ExpressionOperator localTime() {
+        return simpleFunctionNoParentheses(LocalTime, "CURRENT_TIME");
+    }
+
+    /**
+     * INTERNAL:
+     * Build operator.
+     */
+    public static ExpressionOperator localDateTime() {
+        return simpleFunctionNoParentheses(LocalDateTime,  "CURRENT_TIMESTAMP");
+    }
+
+    /**
+     * INTERNAL:
      * Create the toLowerCase operator.
      */
     public static ExpressionOperator toLowerCase() {
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java
index 955eb12..0a16a5c 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/core/helper/CoreClassConstants.java
@@ -12,6 +12,8 @@
 
 // Contributors:
 //     Blaise Doughan - 2.5 - initial implementation
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.internal.core.helper;
 
 import java.io.File;
@@ -21,6 +23,9 @@
 import java.sql.Date;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
@@ -78,6 +83,9 @@
     public static final Class<String> STRING = String.class;
     public static final Class<Time> TIME = Time.class;
     public static final Class<Timestamp> TIMESTAMP = Timestamp.class;
+    public static final Class<LocalDate> LOCAL_DATE = LocalDate.class;
+    public static final Class<LocalTime> LOCAL_TIME = LocalTime.class;
+    public static final Class<LocalDateTime> LOCAL_DATETIME = LocalDateTime.class;
     public static final Class<UUID> UUID = UUID.class;
     public static final Class<URL> URL_Class = URL.class;
     public static final Class<java.util.Date> UTILDATE = java.util.Date.class;
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
index 4ddb7c1..35f2811 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
@@ -19,6 +19,8 @@
 //       - 522312: Add the eclipselink.sequencing.start-sequence-at-nextval property
 //     02/20/2018-2.7 Will Dazey
 //       - 529602: Added support for CLOBs in DELETE statements for Oracle
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.internal.databaseaccess;
 
 import java.io.IOException;
@@ -470,6 +472,9 @@
         addOperator(ExpressionOperator.today());
         addOperator(ExpressionOperator.currentDate());
         addOperator(ExpressionOperator.currentTime());
+        addOperator(ExpressionOperator.localDateTime());
+        addOperator(ExpressionOperator.localTime());
+        addOperator(ExpressionOperator.localDate());
         addOperator(ExpressionOperator.extract());
 
         // Math
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java
index 59b5757..62cba10 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DerbyPlatform.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2005, 2021 IBM Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -18,32 +18,36 @@
 //       - 357533: Allow DDL queries to execute even when Multitenant entities are part of the PU
 //     03/18/2015-2.6.0 Joe Grassel
 //       - 462498: Missing isolation level expression in SQL for Derby platform
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.platform.database;
 
-import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
-import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
-import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
-import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
-import org.eclipse.persistence.internal.sessions.AbstractSession;
-import org.eclipse.persistence.internal.helper.ClassConstants;
-import org.eclipse.persistence.internal.helper.DatabaseTable;
-import org.eclipse.persistence.internal.helper.Helper;
-import org.eclipse.persistence.exceptions.ValidationException;
-import org.eclipse.persistence.expressions.ExpressionOperator;
-import org.eclipse.persistence.queries.ValueReadQuery;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Vector;
-import java.io.Writer;
 import java.io.IOException;
+import java.io.Writer;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
-import java.util.Hashtable;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.expressions.Expression;
+import org.eclipse.persistence.expressions.ExpressionOperator;
+import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
+import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
+import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter;
+import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
+import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
+import org.eclipse.persistence.internal.helper.ClassConstants;
 import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.internal.helper.DatabaseTable;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.queries.ValueReadQuery;
 
 /**
  * <p><b>Purpose</b>: Provides Derby DBMS specific behavior.
@@ -313,7 +317,7 @@
 
         fieldTypeMapping.put(java.time.LocalDate.class, new FieldTypeDefinition("DATE"));
         fieldTypeMapping.put(java.time.LocalDateTime.class, new FieldTypeDefinition("TIMESTAMP"));
-        fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIMESTAMP"));
+        fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIME"));
         fieldTypeMapping.put(java.time.OffsetDateTime.class, new FieldTypeDefinition("TIMESTAMP"));
         fieldTypeMapping.put(java.time.OffsetTime.class, new FieldTypeDefinition("TIMESTAMP"));
 
@@ -340,6 +344,149 @@
         // Derby does not support DECIMAL, but does have a DOUBLE function.
         addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.ToNumber, "DOUBLE"));
         addOperator(extractOperator());
+        addOperator(derbyPowerOperator());
+        addOperator(derbyRoundOperator());
+    }
+
+    // Emulate POWER(:a,:b) as EXP((:b)*LN(:a))
+    private static ExpressionOperator derbyPowerOperator() {
+        ExpressionOperator exOperator = new ExpressionOperator() {
+            @Override
+            public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+                printer.printString(getDatabaseStrings()[0]);
+                if (second != null) {
+                    second.printSQL(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[1]);
+                first.printSQL(printer);
+                printer.printString(getDatabaseStrings()[2]);
+            }
+            @Override
+            public void printCollection(List items, ExpressionSQLPrinter printer) {
+                if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
+                    printer.getCall().setUsesBinding(false);
+                }
+                if (items.size() > 0) {
+                    Expression firstItem = (Expression)items.get(0);
+                    Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+                    printDuo(firstItem, secondItem, printer);
+                } else {
+                    throw new IllegalArgumentException("List of items shall contain at least one item");
+                }
+            }
+            @Override
+            public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+                printer.printString(getDatabaseStrings()[0]);
+                if (second != null) {
+                    second.printJava(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[1]);
+                first.printJava(printer);
+                printer.printString(getDatabaseStrings()[2]);
+            }
+            @Override
+            public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+                if (items.size() > 0) {
+                    Expression firstItem = (Expression)items.get(0);
+                    Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+                    printJavaDuo(firstItem, secondItem, printer);
+                } else {
+                    throw new IllegalArgumentException("List of items shall contain at least one item");
+                }
+            }
+        };
+        exOperator.setType(ExpressionOperator.FunctionOperator);
+        exOperator.setSelector(ExpressionOperator.Power);
+        exOperator.setName("POWER");
+        List<String> v = new ArrayList<>(4);
+        v.add("EXP((");
+        v.add(")*LN(");
+        v.add("))");
+        exOperator.printsAs(v);
+        exOperator.bePrefix();
+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
+        return exOperator;
+    }
+
+    // Emulate ROUND as FLOOR((:x)*1e:n+0.5)/1e:n
+    private static ExpressionOperator derbyRoundOperator() {
+        ExpressionOperator exOperator = new ExpressionOperator() {
+            @Override
+            public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+                printer.printString(getDatabaseStrings()[0]);
+                first.printSQL(printer);
+                printer.printString(getDatabaseStrings()[1]);
+                if (second != null) {
+                    second.printSQL(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[2]);
+                if (second != null) {
+                    second.printSQL(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[3]);
+            }
+            @Override
+            public void printCollection(List items, ExpressionSQLPrinter printer) {
+                if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
+                    printer.getCall().setUsesBinding(false);
+                }
+                if (items.size() > 0) {
+                    Expression firstItem = (Expression)items.get(0);
+                    Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+                    printDuo(firstItem, secondItem, printer);
+                } else {
+                    throw new IllegalArgumentException("List of items shall contain at least one item");
+                }
+            }
+            @Override
+            public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+                printer.printString(getDatabaseStrings()[0]);
+                first.printJava(printer);
+                printer.printString(getDatabaseStrings()[1]);
+                if (second != null) {
+                    second.printJava(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[2]);
+                if (second != null) {
+                    second.printJava(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[3]);
+            }
+            @Override
+            public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+                if (items.size() > 0) {
+                    Expression firstItem = (Expression)items.get(0);
+                    Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+                    printJavaDuo(firstItem, secondItem, printer);
+                } else {
+                    throw new IllegalArgumentException("List of items shall contain at least one item");
+                }
+            }
+        };
+        exOperator.setType(ExpressionOperator.FunctionOperator);
+        exOperator.setSelector(ExpressionOperator.Round);
+        exOperator.setName("ROUND");
+        List<String> v = new ArrayList<>(4);
+        v.add("FLOOR((");
+        v.add(")*1e");
+        v.add("+0.5)/1e");
+        v.add("");
+        exOperator.printsAs(v);
+        exOperator.bePrefix();
+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
+        return exOperator;
     }
 
     /**
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java
index 80ea527..fb267bc 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/PostgreSQLPlatform.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2019 IBM Corporation. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -16,28 +16,43 @@
 //     Phillip Ross - LIMIT/OFFSET syntax support
 //     09/14/2011-2.3.1 Guy Pelletier
 //       - 357533: Allow DDL queries to execute even when Multitenant entities are part of the PU
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.platform.database;
 
-import java.io.*;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.sql.Types;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
 
-import org.eclipse.persistence.internal.sessions.AbstractRecord;
-import org.eclipse.persistence.internal.sessions.AbstractSession;
-import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
-import org.eclipse.persistence.queries.StoredProcedureCall;
 import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.expressions.Expression;
 import org.eclipse.persistence.expressions.ExpressionOperator;
 import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
 import org.eclipse.persistence.internal.databaseaccess.DatasourceCall;
 import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
+import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter;
 import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
 import org.eclipse.persistence.internal.expressions.RelationExpression;
 import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
-import org.eclipse.persistence.internal.helper.*;
+import org.eclipse.persistence.internal.helper.ClassConstants;
+import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.internal.helper.DatabaseTable;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
 import org.eclipse.persistence.queries.SQLCall;
+import org.eclipse.persistence.queries.StoredProcedureCall;
 import org.eclipse.persistence.queries.ValueReadQuery;
 import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
 
@@ -130,6 +145,84 @@
         addOperator(operatorLocate2());
         addOperator(toNumberOperator());
         addOperator(regexpOperator());
+        addOperator(pgsqlRoundOperator());
+    }
+
+    // Emulate ROUND(:x,:n) as FLOOR((:x)*10^(:n)+0.5)/10^(:n)
+    private static ExpressionOperator pgsqlRoundOperator() {
+        ExpressionOperator exOperator = new ExpressionOperator() {
+            @Override
+            public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+                printer.printString(getDatabaseStrings()[0]);
+                first.printSQL(printer);
+                printer.printString(getDatabaseStrings()[1]);
+                if (second != null) {
+                    second.printSQL(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[2]);
+                if (second != null) {
+                    second.printSQL(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[3]);
+            }
+            @Override
+            public void printCollection(List items, ExpressionSQLPrinter printer) {
+                if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
+                    printer.getCall().setUsesBinding(false);
+                }
+                if (items.size() > 0) {
+                    Expression firstItem = (Expression)items.get(0);
+                    Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+                    printDuo(firstItem, secondItem, printer);
+                } else {
+                    throw new IllegalArgumentException("List of items shall contain at least one item");
+                }
+            }
+            @Override
+            public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+                printer.printString(getDatabaseStrings()[0]);
+                first.printJava(printer);
+                printer.printString(getDatabaseStrings()[1]);
+                if (second != null) {
+                    second.printJava(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[2]);
+                if (second != null) {
+                    second.printJava(printer);
+                } else {
+                    printer.printString("0");
+                }
+                printer.printString(getDatabaseStrings()[3]);
+            }
+            @Override
+            public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+                if (items.size() > 0) {
+                    Expression firstItem = (Expression)items.get(0);
+                    Expression secondItem = items.size() > 1 ? (Expression)items.get(1) : null;
+                    printJavaDuo(firstItem, secondItem, printer);
+                } else {
+                    throw new IllegalArgumentException("List of items shall contain at least one item");
+                }
+            }
+        };
+        exOperator.setType(ExpressionOperator.FunctionOperator);
+        exOperator.setSelector(ExpressionOperator.Round);
+        exOperator.setName("ROUND");
+        List<String> v = new ArrayList<>(4);
+        v.add("FLOOR((");
+        v.add(")*10^(");
+        v.add(")+0.5)/10^(");
+        v.add(")");
+        exOperator.printsAs(v);
+        exOperator.bePrefix();
+        exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
+        return exOperator;
     }
 
     /**
diff --git a/jpa/eclipselink.jpa.test.jse/pom.xml b/jpa/eclipselink.jpa.test.jse/pom.xml
index 141223e..d09f403 100644
--- a/jpa/eclipselink.jpa.test.jse/pom.xml
+++ b/jpa/eclipselink.jpa.test.jse/pom.xml
@@ -42,6 +42,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>jakarta.enterprise</groupId>
             <artifactId>jakarta.enterprise.cdi-api</artifactId>
             <scope>test</scope>
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
index 965cce7..2070143 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestMathFunctions.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,7 +12,7 @@
 
 // Contributors:
 //     02/01/2022: Tomas Kraus
-//       - #N/A: Test extract() in CriteriaBuilder
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.jpa.test.criteria;
 
 import java.math.RoundingMode;
@@ -31,6 +31,8 @@
 import org.eclipse.persistence.jpa.test.framework.EmfRunner;
 import org.eclipse.persistence.jpa.test.framework.Property;
 import org.eclipse.persistence.sessions.Session;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Assume;
@@ -39,7 +41,7 @@
 import org.junit.runner.RunWith;
 
 /**
- * Test sign() method in CriteriaBuilder.
+ * Test math functions in CriteriaBuilder.
  * Added to JPA-API as PR #351
  */
 @RunWith(EmfRunner.class)
@@ -358,9 +360,10 @@
         return em.createQuery(cq).getSingleResult();
     }
 
-    // Call POWER(n.longValue, 2) on long n>0.
+    // Call POWER(n.longValue, 2) on long n=0.
     @Test
     public void testPower2MethodWithZeroBase() {
+        // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
         Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 2, 0, "longValue");
@@ -372,28 +375,32 @@
     // Call POWER(n.longValue, 2) on long n>0.
     @Test
     public void testPower2MethodWithPositiveLongBase() {
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 2, 3, "longValue");
             Assert.assertEquals(
-                    Double.valueOf(Math.pow(NUMBER[3].getLongValue(), 2)), result);
+                    Double.valueOf(Math.pow(NUMBER[3].getLongValue(), 2)), Double.valueOf(Math.round(result)));
         }
     }
 
     // Call POWER(n.doubleValue, 2) on double n>0.
     @Test
     public void testPower2MethodWithPositiveDoubleBase() {
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 2, 3, "doubleValue");
-            Assert.assertEquals(
-                    Double.valueOf(Math.pow(NUMBER[3].getDoubleValue(), 2)), result);
+            if (emf.unwrap(Session.class).getPlatform().isDerby()) {
+                MatcherAssert.assertThat(
+                        Math.abs(Math.pow(NUMBER[3].getDoubleValue(), 2) - result), Matchers.lessThan(0.0000001d));
+            } else {
+                Assert.assertEquals(
+                        Double.valueOf(Math.pow(NUMBER[3].getDoubleValue(), 2)), result);
+            }
         }
     }
 
     // Call POWER(n.longValue, 2) on long n<0.
     @Test
     public void testPower2MethodWithNegativeLongBase() {
+        // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
         Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 2, 4, "longValue");
@@ -405,6 +412,7 @@
     // Call POWER(n.doubleValue, 2) on double n<0.
     @Test
     public void testPower2MethodWithNegativeDoubleBase() {
+        // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
         Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 2, 4, "doubleValue");
@@ -416,6 +424,7 @@
     // Call POWER(n.longValue, 3) on long n<0.
     @Test
     public void testPower3MethodWithNegativeLongBase() {
+        // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
         Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 3, 4, "longValue");
@@ -427,6 +436,7 @@
     // Call POWER(n.doubleValue, 3) on double n<0.
     @Test
     public void testPower3MethodWithNegativeDoubleBase() {
+        // org.apache.derby.client.am.SqlException: The resulting value is outside the range for the data type DOUBLE.
         Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callPower(em, 3, 4, "doubleValue");
@@ -450,11 +460,16 @@
     // Call POWER(n.doubleValue, n.longValue) on id=7: [14.23D,4L] (result fits in double with no exponent).
     @Test
     public void testPowerMethodWithPositiveArgs() {
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callExprPower(em, 7);
-            Assert.assertEquals(
-                    Double.valueOf(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue())), result);
+            if (emf.unwrap(Session.class).getPlatform().isDerby()) {
+                MatcherAssert.assertThat(
+                        Math.abs(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue()) - result),
+                        Matchers.lessThan(0.0000001d));
+            } else {
+                Assert.assertEquals(
+                        Double.valueOf(Math.pow(NUMBER[7].getDoubleValue(), NUMBER[7].getLongValue())), result);
+            }
         }
     }
 
@@ -462,19 +477,22 @@
     // using CriteriaQuery
     // Matches Expression<Double> power(Expression<? extends Number> x, Number y) prototype
     private static Double callRound(final EntityManager em, final int d, final int id) {
-        CriteriaBuilder cb = em.getCriteriaBuilder();
-        CriteriaQuery<Double> cq = cb.createQuery(Double.class);
-        Root<NumberEntity> number = cq.from(NumberEntity.class);
-        cq.select(cb.round(number.get("doubleValue"), d));
-        cq.where(cb.equal(number.get("id"), id));
-        return em.createQuery(cq).getSingleResult();
+        try {
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+            Root<NumberEntity> number = cq.from(NumberEntity.class);
+            cq.select(cb.round(number.get("doubleValue"), d));
+            cq.where(cb.equal(number.get("id"), id));
+            return em.createQuery(cq).getSingleResult();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        }
     }
 
     // Call ROUND(n) on n>0.
     @Test
     public void testRoundMethodWithPositive() {
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isPostgreSQL());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callRound(em, 6,8);
             Assert.assertEquals(Double.valueOf(44.754238D), result);
@@ -484,8 +502,6 @@
     // Call ROUND(n) on n<0.
     @Test
     public void testRoundMethodWithNegative() {
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isDerby());
-        Assume.assumeFalse(emf.unwrap(Session.class).getPlatform().isPostgreSQL());
         try (final EntityManager em = emf.createEntityManager()) {
             Double result = callRound(em, 6, 9);
             Assert.assertEquals(Double.valueOf(-214.245732D), result);
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestSimpleCase.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestSimpleCase.java
new file mode 100644
index 0000000..412d1e4
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestSimpleCase.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.criteria;
+
+import java.util.List;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Expression;
+import jakarta.persistence.criteria.Root;
+
+import org.eclipse.persistence.jpa.test.criteria.model.NumberEntity;
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test new API 3.1.0 when methods of SimpleCase subclass of CriteriaBuilder.
+ * Added to JPA-API as PR #362
+ */
+@RunWith(EmfRunner.class)
+public class TestSimpleCase {
+
+    @Emf(createTables = DDLGen.DROP_CREATE,
+            classes = {
+                    NumberEntity.class
+            },
+            properties = {
+                    @Property(name = "eclipselink.cache.shared.default", value = "false"),
+                    // This property remaps String from VARCHAR->NVARCHAR(or db equivalent)
+                    @Property(name = "eclipselink.target-database-properties",
+                            value = "UseNationalCharacterVaryingTypeForString=true"),
+            })
+    private EntityManagerFactory emf;
+
+    private final NumberEntity[] NUMBER = {
+            new NumberEntity(1, 0L, 0D),
+            new NumberEntity(2, 3L, 10D),
+            new NumberEntity(3, 3L, 100D),
+            new NumberEntity(4, 5L, 10D),
+            new NumberEntity(5, 5L, 100D),
+            new NumberEntity(6, 5L, 1000D)
+    };
+
+    @Before
+    public void setup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            for (NumberEntity e : NUMBER) {
+                em.persist(e);
+            }
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    @After
+    public void cleanup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            em.createQuery("DELETE FROM NumberEntity e").executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test SimpleCase<C,R> when(Expression<? extends C>, R) prototype
+    // Strategy: Execute the query and check whether 3 rows where returned
+    @Test
+    public void testWhenExpressionValue() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<NumberEntity> cq = cb.createQuery(NumberEntity.class);
+            Root<NumberEntity> entity = cq.from(NumberEntity.class);
+            cq.select(entity);
+            Expression<Object> selectCase = cb.selectCase(entity.get("longValue"))
+                    .when(cb.literal(3), 10)
+                    .when(cb.literal(5), 100)
+                    .otherwise(0);
+            cq.where(cb.equal(entity.get("doubleValue"), selectCase));
+            List<NumberEntity> result = em.createQuery(cq).getResultList();
+            MatcherAssert.assertThat(result.size(), Matchers.equalTo(3));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        }
+    }
+
+    // Test SimpleCase<C,R> when(Expression<? extends C>, Expression<? extends R>) prototype
+    // Strategy: Execute the query and check whether 3 rows where returned
+    @Test
+    public void testWhenExpressionExpression() {
+        try (final EntityManager em = emf.createEntityManager()) {
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<NumberEntity> cq = cb.createQuery(NumberEntity.class);
+            Root<NumberEntity> entity = cq.from(NumberEntity.class);
+            cq.select(entity);
+            Expression<Object> selectCase = cb.selectCase(entity.get("longValue"))
+                    .when(cb.literal(3), cb.literal(10))
+                    .when(cb.literal(5), cb.literal(100))
+                    .otherwise(0);
+            cq.where(cb.equal(entity.get("doubleValue"), selectCase));
+            List<NumberEntity> result = em.createQuery(cq).getResultList();
+            MatcherAssert.assertThat(result.size(), Matchers.equalTo(3));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        }
+    }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/DateTimeEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/DateTimeEntity.java
new file mode 100644
index 0000000..79d3930
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/DateTimeEntity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.criteria.model;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+/**
+ * JPA Entity used in {@code LocalTime}/{@code LocalDate}/{@code LocalDateTime} tests.
+ */
+@Entity
+public class DateTimeEntity {
+
+    @Id
+    private Integer id;
+
+    private LocalTime time;
+
+    private LocalDate date;
+
+    private LocalDateTime datetime;
+
+    public DateTimeEntity() {
+    }
+
+    public DateTimeEntity(final Integer id, final LocalTime time, final LocalDate date, final LocalDateTime datetime) {
+        this.setId(id);
+        this.setTime(time);
+        this.setDate(date);
+        this.setDatetime(datetime);
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public LocalTime getTime() {
+        return time;
+    }
+
+    public void setTime(LocalTime time) {
+        this.time = time;
+    }
+
+    public LocalDate getDate() {
+        return date;
+    }
+
+    public void setDate(LocalDate date) {
+        this.date = date;
+    }
+
+    public LocalDateTime getDatetime() {
+        return datetime;
+    }
+
+    public void setDatetime(LocalDateTime datetime) {
+        this.datetime = datetime;
+    }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
index 61a6c46..ffb02ef 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/NumberEntity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,12 +12,15 @@
 
 // Contributors:
 //     02/01/2022: Tomas Kraus
-//       - #N/A: Test extract() in CriteriaBuilder
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.jpa.test.criteria.model;
 
 import jakarta.persistence.Entity;
 import jakarta.persistence.Id;
 
+/**
+ * JPA Entity used in {@code CriteriaBuilder} math functions tests.
+ */
 @Entity
 public class NumberEntity {
 
@@ -29,7 +32,6 @@
     private Double doubleValue;
 
     public NumberEntity() {
-
     }
 
     public NumberEntity(final Integer id, final Long longValue, final Double doubleValue) {
@@ -61,4 +63,5 @@
     public void setDoubleValue(final Double doubleValue) {
         this.doubleValue = doubleValue;
     }
+
 }
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java
new file mode 100644
index 0000000..85d7fc0
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.criteria.model;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.List;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.CriteriaUpdate;
+import jakarta.persistence.criteria.Root;
+
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@code LocalTime}/{@code LocalDate}/{@code LocalDateTime} functions in CriteriaBuilder.
+ * Added to JPA-API as PR #352
+ */
+@RunWith(EmfRunner.class)
+public class TestDateTimeFunctions {
+
+    @Emf(createTables = DDLGen.DROP_CREATE,
+            classes = {
+                    DateTimeEntity.class
+            },
+            properties = {
+                    @Property(name = "eclipselink.cache.shared.default", value = "false"),
+                    // This property remaps String from VARCHAR->NVARCHAR(or db equivalent)
+                    @Property(name = "eclipselink.target-database-properties",
+                            value = "UseNationalCharacterVaryingTypeForString=true"),
+            })
+    private EntityManagerFactory emf;
+
+    private final LocalDateTime[] TS = {
+            LocalDateTime.of(1970, 1, 1, 1, 11, 11),
+            LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0)
+    };
+
+    private final DateTimeEntity[] ENTITY = {
+            new DateTimeEntity(1, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalTime
+            new DateTimeEntity(2, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalDate
+            new DateTimeEntity(3, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0]), // reserved for testCriteriaUpdateLocalDateTime
+            new DateTimeEntity(4, TS[1].toLocalTime(), TS[1].toLocalDate(), TS[1])  // reserved for testCriteriaQueryWhereLocalTime
+                                                                                       //              testCriteriaQueryWhereLocalTimeReturnsEmpty
+                                                                                       //              testCriteriaQueryWhereLocalDate
+                                                                                       //              testCriteriaQueryWhereLocalDateReturnsEmpty
+                                                                                       //              testCriteriaQueryWhereLocalDateTime
+                                                                                       //              testCriteriaQueryWhereLocalDateTimeReturnsEmpty
+                                                                                       //              testCriteriaQuerySelectLocalTime
+                                                                                       //              testCriteriaQuerySelectLocalDate
+                                                                                       //              testCriteriaQuerySelectLocalDateTime
+    };
+
+    @Before
+    public void setup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            for (DateTimeEntity e : ENTITY) {
+                em.persist(e);
+            }
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    @After
+    public void cleanup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            em.createQuery("DELETE FROM DateTimeEntity e").executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaUpdate of localTime.
+    // Strategy: Update 1:11:11 to current LocalTime and verify that time in database
+    //           differs in less than 30 seconds from current time.
+    // May fail when database time is not set properly.
+    @Test
+    public void testCriteriaUpdateLocalTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            // Run the update query: Set current time
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
+            cu.set("time", cb.localTime());
+            cu.where(cb.equal(entity.get("id"), 1));
+            em.createQuery(cu).executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+            // Verify updated entity
+            DateTimeEntity data = em.find(DateTimeEntity.class, 1);
+            long diffMilis = Duration.between(data.getTime(), LocalTime.now()).toMillis();
+            // Positive value means that test did not pass midnight.
+            if (diffMilis > 0) {
+                MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+            // Midnight pass correction.
+            } else {
+                MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
+            }
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaUpdate of localDate.
+    // Strategy: Update 1. 1. 1970 to current LocalDate and verify that time in database
+    //           differs in less than two days from current date. One day difference
+    //           may be caused by midnight being just passed.
+    // May fail when database time is not set properly.
+    @Test
+    public void testCriteriaUpdateLocalDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            // Run the update query: Set current time
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
+            cu.set("date", cb.localDate());
+            cu.where(cb.equal(entity.get("id"), 2));
+            em.createQuery(cu).executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+            // Verify updated entity
+            DateTimeEntity data = em.find(DateTimeEntity.class, 2);
+            Period diff = Period.between(data.getDate(), LocalDate.now());
+            MatcherAssert.assertThat(diff.getYears(), Matchers.equalTo(0));
+            MatcherAssert.assertThat(diff.getMonths(), Matchers.equalTo(0));
+            MatcherAssert.assertThat(diff.getDays(), Matchers.lessThan(2));
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaUpdate of localDateTime.
+    // Strategy: Update 1. 1. 1970 1:11:11 to current LocalDateTime and verify that timestamp in database
+    //           differs in less than 30 seconds from current timestamp.
+    // May fail when database time is not set properly.
+    @Test
+    public void testCriteriaUpdateLocalDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            // Run the update query: Set current time
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaUpdate<DateTimeEntity> cu = cb.createCriteriaUpdate(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cu.from(DateTimeEntity.class);
+            cu.set("datetime", cb.localDateTime());
+            cu.where(cb.equal(entity.get("id"), 3));
+            em.createQuery(cu).executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+            // Verify updated entity
+            DateTimeEntity data = em.find(DateTimeEntity.class, 3);
+            long diffMilis = Duration.between(data.getDatetime(), LocalDateTime.now()).toMillis();
+            MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localTime in WHERE condition.
+    // Strategy: SELECT DateTimeEntity WHERE time column < localTime AND id = :id
+    //           LocalTime in entity is set to 0:00:00.0.
+    @Test
+    public void testCriteriaQueryWhereLocalTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(entity);
+            cq.where(cb.and(cb.lessThan(entity.get("time"), cb.localTime()), cb.equal(entity.get("id"), 4)));
+            em.createQuery(cq).getSingleResult();
+            em.getTransaction().commit();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localTime in WHERE condition with no record matching it.
+    // Strategy: SELECT DateTimeEntity WHERE time column > localTime AND id = :id
+    //           localTime in entity is set to 0:00:00.0
+    // Such a record does not exist so empty result is expected.
+    @Test
+    public void testCriteriaQueryWhereLocalTimeReturnsEmpty() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(entity);
+            cq.where(cb.and(cb.greaterThan(entity.get("time"), cb.localTime()), cb.equal(entity.get("id"), 4)));
+            List<DateTimeEntity> data = em.createQuery(cq).getResultList();
+            em.getTransaction().commit();
+            MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localDate in WHERE condition.
+    // Strategy: SELECT DateTimeEntity WHERE time column < localTime AND id = :id
+    //           LocalDate in entity is set to 1.1.1970.
+    @Test
+    public void testCriteriaQueryWhereLocalDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(entity);
+            cq.where(cb.and(cb.lessThan(entity.get("date"), cb.localDate()), cb.equal(entity.get("id"), 4)));
+            em.createQuery(cq).getSingleResult();
+            em.getTransaction().commit();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localDate in WHERE condition with no record matching it.
+    // Strategy: SELECT DateTimeEntity WHERE time column > localTime AND id = :id
+    //           LocalDate in entity is set to 1.1.1970.
+    // Such a record does not exist so empty result is expected
+    @Test
+    public void testCriteriaQueryWhereLocalDateReturnsEmpty() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(entity);
+            cq.where(cb.and(cb.greaterThan(entity.get("date"), cb.localDate()), cb.equal(entity.get("id"), 4)));
+            List<DateTimeEntity> data = em.createQuery(cq).getResultList();
+            em.getTransaction().commit();
+            MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localDateTime in WHERE condition.
+    // Strategy: SELECT DateTimeEntity WHERE time column < localDateTime AND id = :id
+    //           LocalDate in entity is set to 1.1.1970 0:00:00.
+    @Test
+    public void testCriteriaQueryWhereLocalDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(entity);
+            cq.where(cb.and(cb.lessThan(entity.get("datetime"), cb.localDateTime()), cb.equal(entity.get("id"), 4)));
+            em.createQuery(cq).getSingleResult();
+            em.getTransaction().commit();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localDateTime in WHERE condition with no record matching it.
+    // Strategy: SELECT DateTimeEntity WHERE time column > localDateTime AND id = :id
+    //           LocalDate in entity is set to 1.1.1970 0:00:00.
+    // Such a record does not exist so empty result is expected
+    @Test
+    public void testCriteriaQueryWhereLocalDateTimeReturnsEmpty() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<DateTimeEntity> cq = cb.createQuery(DateTimeEntity.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(entity);
+            cq.where(cb.and(cb.greaterThan(entity.get("datetime"), cb.localDateTime()), cb.equal(entity.get("id"), 4)));
+            List<DateTimeEntity> data = em.createQuery(cq).getResultList();
+            em.getTransaction().commit();
+            MatcherAssert.assertThat(data.size(), Matchers.equalTo(0));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localTime in SELECT column list
+    @Test
+    public void testCriteriaQuerySelectLocalTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<LocalTime> cq = cb.createQuery(LocalTime.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(cb.localTime());
+            cq.where(cb.equal(entity.get("id"), 4));
+            LocalTime time = em.createQuery(cq).getSingleResult();
+            em.getTransaction().commit();
+            long diffMilis = Duration.between(time, LocalTime.now()).toMillis();
+            // Positive value means that test did not pass midnight.
+            if (diffMilis > 0) {
+                MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+            // Midnight pass correction.
+            } else {
+                MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
+            }
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localDate in SELECT column list
+    @Test
+    public void testCriteriaQuerySelectLocalDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<LocalDate> cq = cb.createQuery(LocalDate.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(cb.localDate());
+            cq.where(cb.equal(entity.get("id"), 4));
+            LocalDate date = em.createQuery(cq).getSingleResult();
+            em.getTransaction().commit();
+            Period diff = Period.between(date, LocalDate.now());
+            MatcherAssert.assertThat(diff.getYears(), Matchers.equalTo(0));
+            MatcherAssert.assertThat(diff.getMonths(), Matchers.equalTo(0));
+            MatcherAssert.assertThat(diff.getDays(), Matchers.lessThan(2));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test CriteriaQuery with localDateTime in SELECT column list
+    @Test
+    public void testCriteriaQuerySelectLocalDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<LocalDateTime> cq = cb.createQuery(LocalDateTime.class);
+            Root<DateTimeEntity> entity = cq.from(DateTimeEntity.class);
+            cq.select(cb.localDateTime());
+            cq.where(cb.equal(entity.get("id"), 4));
+            LocalDateTime datetime = em.createQuery(cq).getSingleResult();
+            em.getTransaction().commit();
+            long diffMilis = Duration.between(datetime, LocalDateTime.now()).toMillis();
+            MatcherAssert.assertThat(diffMilis, Matchers.lessThan(30000L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+}
diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
index 03dec36..9568729 100644
--- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
+++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java
@@ -13,15 +13,13 @@
 
 // Contributors:
 //     Gordon Yorke - Initial development
-//
+//     02/01/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
 package org.eclipse.persistence.internal.jpa.querydef;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -2315,36 +2313,33 @@
     }
 
     /**
-     * Create expression to return current local date.
+     * Create expression to return local datetime.
      *
-     * @return expression for current date
-     */
-    @Override
-    public Expression<java.time.LocalDate> localDate() {
-        // TODO: Implement
-        throw new UnsupportedOperationException("Local date expression is not implemented yet");
-    }
-
-    /**
-     * Create expression to return current local datetime.
-     *
-     * @return expression for current timestamp
+     * @return expression for local timestamp
      */
     @Override
     public Expression<java.time.LocalDateTime> localDateTime() {
-        // TODO: Implement
-        throw new UnsupportedOperationException("Local datetime expression is not implemented yet");
+        return new ExpressionImpl(metamodel, ClassConstants.LOCAL_DATETIME, new ExpressionBuilder().localDateTime());
     }
 
     /**
-     * Create expression to return current local time.
+     * Create expression to return local date.
      *
-     * @return expression for current time
+     * @return expression for local date
+     */
+    @Override
+    public Expression<java.time.LocalDate> localDate() {
+        return new ExpressionImpl(metamodel, ClassConstants.LOCAL_DATE, new ExpressionBuilder().localDate());
+    }
+
+    /**
+     * Create expression to return local time.
+     *
+     * @return expression for local time
      */
     @Override
     public Expression<java.time.LocalTime> localTime() {
-        // TODO: Implement
-        throw new UnsupportedOperationException("Local time expression is not implemented yet");
+        return new ExpressionImpl(metamodel, ClassConstants.LOCAL_TIME, new ExpressionBuilder().localTime());
     }
 
     /**
@@ -2765,12 +2760,12 @@
          */
         @Override
         public SimpleCase<C, R> when(Expression<? extends C> condition, R result) {
-            org.eclipse.persistence.expressions.Expression conditionExp = org.eclipse.persistence.expressions.Expression.from(condition, new ExpressionBuilder());
+            org.eclipse.persistence.expressions.Expression conditionExp = ((InternalSelection)condition).getCurrentNode();
+            conditionExp = org.eclipse.persistence.expressions.Expression.from(conditionExp, currentNode);
             ((FunctionExpression)currentNode).addChild(conditionExp);
             this.expressions.add(condition);
 
-            org.eclipse.persistence.expressions.Expression resultExp = ((InternalSelection)result).getCurrentNode();
-            resultExp = org.eclipse.persistence.expressions.Expression.from(resultExp, currentNode);
+            org.eclipse.persistence.expressions.Expression resultExp = org.eclipse.persistence.expressions.Expression.from(result, new ExpressionBuilder());
             ((FunctionExpression)currentNode).addChild(resultExp);
             Expression<R> resultLiteral = internalLiteral(result);
             this.expressions.add(resultLiteral);
@@ -2787,7 +2782,8 @@
          */
         @Override
         public SimpleCase<C, R> when(Expression<? extends C> condition, Expression<? extends R> result) {
-            org.eclipse.persistence.expressions.Expression conditionExp = org.eclipse.persistence.expressions.Expression.from(condition, new ExpressionBuilder());
+            org.eclipse.persistence.expressions.Expression conditionExp = ((InternalSelection)condition).getCurrentNode();
+            conditionExp = org.eclipse.persistence.expressions.Expression.from(conditionExp, currentNode);
             ((FunctionExpression)currentNode).addChild(conditionExp);
             this.expressions.add(condition);