Added missing EXTRACT(QUARTER FROM date/datetime) for Derby platform. Covered EXTRACT queries with jUnit.

Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
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 2259854..5eec658 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
@@ -32,6 +32,7 @@
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Vector;
 
 import org.eclipse.persistence.exceptions.ValidationException;
@@ -41,6 +42,7 @@
 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.LiteralExpression;
 import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
 import org.eclipse.persistence.internal.helper.ClassConstants;
 import org.eclipse.persistence.internal.helper.DatabaseField;
@@ -345,7 +347,7 @@
         addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.ToNumber, "DOUBLE"));
         // LocalTime should be processed as TIMESTAMP
         addOperator(ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.LocalTime, "CAST(CURRENT_TIME AS TIMESTAMP)"));
-        addOperator(extractOperator());
+        addOperator(derbyExtractOperator());
         addOperator(derbyPowerOperator());
         addOperator(derbyRoundOperator());
     }
@@ -495,12 +497,74 @@
      * INTERNAL:
      * Derby does not support EXTRACT, but does have YEAR, MONTH, DAY, etc.
      */
-    public static ExpressionOperator extractOperator() {
-        ExpressionOperator exOperator = new ExpressionOperator();
+    public static ExpressionOperator derbyExtractOperator() {
+
+        ExpressionOperator exOperator = new ExpressionOperator() {
+
+            // QUARTER emulation: ((MONTH(:first)+2)/3)
+            private final String[] QUARTER_STRINGS = new String[] {"((MONTH(", ")+2)/3)"};
+
+            private void printQuarterSQL(final Expression first, final ExpressionSQLPrinter printer) {
+                printer.printString(QUARTER_STRINGS[0]);
+                first.printSQL(printer);
+                printer.printString(QUARTER_STRINGS[1]);
+            }
+
+            private void printQuarterJava(final Expression first, final ExpressionJavaPrinter printer) {
+                printer.printString(QUARTER_STRINGS[0]);
+                first.printJava(printer);
+                printer.printString(QUARTER_STRINGS[1]);
+            }
+
+            @Override
+            public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
+                if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
+                    printQuarterSQL(first, printer);
+                } else {
+                    super.printDuo(first, second, printer);
+                }
+            }
+
+            @Override
+            public void printCollection(List items, ExpressionSQLPrinter printer) {
+                if (items.size() == 2) {
+                    Expression first = (Expression)items.get(0);
+                    Expression second = (Expression)items.get(1);
+                    if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
+                        printQuarterSQL(first, printer);
+                        return;
+                    }
+                }
+                super.printCollection(items, printer);
+            }
+
+            @Override
+            public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
+                if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
+                    printQuarterJava(first, printer);
+                } else {
+                    super.printJavaDuo(first, second, printer);
+                }
+            }
+
+            @Override
+            public void printJavaCollection(List items, ExpressionJavaPrinter printer) {
+                if (items.size() == 2) {
+                    Expression first = (Expression)items.get(0);
+                    Expression second = (Expression)items.get(1);
+                    if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
+                        printQuarterJava(first, printer);
+                        return;
+                    }
+                }
+                super.printJavaCollection(items, printer);
+            }
+        };
+
         exOperator.setType(ExpressionOperator.FunctionOperator);
         exOperator.setSelector(ExpressionOperator.Extract);
         exOperator.setName("EXTRACT");
-        List<String> v = new ArrayList<>(5);
+        List<String> v = new ArrayList<>(3);
         v.add("");
         v.add("(");
         v.add(")");
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/TestDateTimeFunctions.java
similarity index 95%
rename from jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/model/TestDateTimeFunctions.java
rename to jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestDateTimeFunctions.java
index 85d7fc0..d48bd9a 100644
--- 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/TestDateTimeFunctions.java
@@ -13,7 +13,7 @@
 // Contributors:
 //     02/01/2022: Tomas Kraus
 //       - Issue 1442: Implement New JPA API 3.1.0 Features
-package org.eclipse.persistence.jpa.test.criteria.model;
+package org.eclipse.persistence.jpa.test.criteria;
 
 import java.time.Duration;
 import java.time.LocalDate;
@@ -29,6 +29,7 @@
 import jakarta.persistence.criteria.CriteriaUpdate;
 import jakarta.persistence.criteria.Root;
 
+import org.eclipse.persistence.jpa.test.criteria.model.DateTimeEntity;
 import org.eclipse.persistence.jpa.test.framework.DDLGen;
 import org.eclipse.persistence.jpa.test.framework.Emf;
 import org.eclipse.persistence.jpa.test.framework.EmfRunner;
@@ -227,9 +228,6 @@
             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();
@@ -255,9 +253,6 @@
             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();
@@ -281,9 +276,6 @@
             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();
@@ -309,9 +301,6 @@
             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();
@@ -335,9 +324,6 @@
             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();
@@ -363,9 +349,6 @@
             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();
@@ -395,9 +378,6 @@
             } else {
                 MatcherAssert.assertThat(86400000L + diffMilis, Matchers.lessThan(30000L));
             }
-        } catch (Throwable t) {
-            t.printStackTrace();
-            throw t;
         } finally {
             if (em.getTransaction().isActive()) {
                 em.getTransaction().rollback();
@@ -423,9 +403,6 @@
             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();
@@ -449,9 +426,6 @@
             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();
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java
new file mode 100644
index 0000000..6c481b0
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestDateTimeFunctions.java
@@ -0,0 +1,392 @@
+/*
+ * 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:
+//     03/09/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.query;
+
+import java.time.LocalDateTime;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.TypedQuery;
+
+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.eclipse.persistence.jpa.test.query.model.DateTimeQueryEntity;
+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 queries.
+ * Added to JPA-API as PR #352
+ */
+@RunWith(EmfRunner.class)
+public class TestDateTimeFunctions {
+    @Emf(createTables = DDLGen.DROP_CREATE,
+            classes = {
+                    DateTimeQueryEntity.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(2022, 3, 9, 14, 30, 25, 0)
+    };
+
+    private final DateTimeQueryEntity[] ENTITY = {
+            new DateTimeQueryEntity(1, TS[0].toLocalTime(), TS[0].toLocalDate(), TS[0])
+    };
+
+    @Before
+    public void setup() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            for (DateTimeQueryEntity 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 DateTimeQueryEntity e").executeUpdate();
+            em.flush();
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(YEAR FROM date)
+    @Test
+    public void testCriteriaExtractYearFromDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(YEAR FROM e.date) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(2022L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(QUARTER FROM date)
+    @Test
+    public void testCriteriaExtractQuarterFromDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(QUARTER FROM e.date) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(1L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(MONTH FROM date)
+    @Test
+    public void testCriteriaExtractMonthFromDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(MONTH FROM e.date) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(3L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(DAY FROM date)
+    @Test
+    public void testCriteriaExtractDayFromDate() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(DAY FROM e.date) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(9L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(HOUR FROM time)
+    @Test
+    public void testCriteriaExtractHourFromTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(HOUR FROM e.time) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(14L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(MINUTE FROM time)
+    @Test
+    public void testCriteriaExtractMinuteFromTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(MINUTE FROM e.time) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(30L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(SECOND FROM time)
+    @Test
+    public void testCriteriaExtractSecondFromTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(SECOND FROM e.time) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(25L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    /////////////////////////////////////////////////////////
+
+    // Test JPQL EXTRACT(YEAR FROM datetime)
+    @Test
+    public void testCriteriaExtractYearFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(YEAR FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(2022L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(QUARTER FROM datetime)
+    @Test
+    public void testCriteriaExtractQuarterFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(QUARTER FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(1L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(MONTH FROM datetime)
+    @Test
+    public void testCriteriaExtractMonthFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(MONTH FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(3L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(DAY FROM datetime)
+    @Test
+    public void testCriteriaExtractDayFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(DAY FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(9L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(HOUR FROM datetime)
+    @Test
+    public void testCriteriaExtractHourFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(HOUR FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(14L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(MINUTE FROM datetime)
+    @Test
+    public void testCriteriaExtractMinuteFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(MINUTE FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(30L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+    // Test JPQL EXTRACT(SECOND FROM datetime)
+    @Test
+    public void testCriteriaExtractSecondFromDateTime() {
+        final EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            TypedQuery<Number> q = em.createQuery("SELECT EXTRACT(SECOND FROM e.datetime) FROM DateTimeQueryEntity e WHERE e.id = :id", Number.class);
+            q.setParameter("id", 1);
+            long y = q.getSingleResult().longValue();
+            MatcherAssert.assertThat(y, Matchers.equalTo(25L));
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            em.close();
+        }
+    }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.java
new file mode 100644
index 0000000..d754cac
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/model/DateTimeQueryEntity.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:
+//     03/09/2022: Tomas Kraus
+//       - Issue 1442: Implement New JPA API 3.1.0 Features
+package org.eclipse.persistence.jpa.test.query.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 DateTimeQueryEntity {
+
+    @Id
+    private Integer id;
+
+    private LocalTime time;
+
+    private LocalDate date;
+
+    private LocalDateTime datetime;
+
+    public DateTimeQueryEntity() {
+    }
+
+    public DateTimeQueryEntity(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;
+    }
+
+}