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

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

commit 86b20ccbdab615b3b94bc9a32bd08d323f4a0c06
Merge: bb9d125402 f358804252
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Jun 24 00:01:23 2024 +0200

    Merge branch 'geoapi-3.1'

 .../org/apache/sis/coverage/grid/PixelInCell.java  |   3 +
 .../main/org/apache/sis/filter/TemporalFilter.java |  22 +-
 .../org/apache/sis/filter/TemporalOperation.java   | 107 ++++--
 .../main/org/apache/sis/filter/privy/Visitor.java  |   2 +-
 .../apache/sis/geometry/wrapper/Geometries.java    |   1 +
 .../apache/sis/geometry/wrapper/esri/Factory.java  |   5 +-
 .../test/org/apache/sis/filter/PeriodLiteral.java  |  34 +-
 .../org/apache/sis/filter/TemporalFilterTest.java  |   2 +-
 .../org.apache.sis.metadata/main/module-info.java  |  12 +
 .../apache/sis/metadata/iso/DefaultMetadata.java   |   2 +-
 .../sis/metadata/iso/acquisition/DefaultEvent.java |   2 +-
 .../iso/acquisition/DefaultRequestedDate.java      |   2 +-
 .../iso/acquisition/DefaultRequirement.java        |   2 +-
 .../sis/metadata/iso/citation/DefaultCitation.java |   2 +-
 .../metadata/iso/citation/DefaultCitationDate.java |   4 +-
 .../distribution/DefaultStandardOrderProcess.java  |   2 +-
 .../metadata/iso/extent/DefaultTemporalExtent.java |  12 +-
 .../apache/sis/metadata/iso/extent/Extents.java    |  21 +-
 .../metadata/iso/identification/DefaultUsage.java  |  21 +-
 .../sis/metadata/iso/legacy/TemporalToDate.java    |   2 +-
 .../metadata/iso/lineage/DefaultProcessStep.java   |  25 +-
 .../org/apache/sis/metadata/sql/privy/Dialect.java |  22 +-
 .../geoapi/temporal/IndeterminateValue.java}       |  17 +-
 .../sis/pending/geoapi/temporal/Instant.java}      |  31 +-
 .../apache/sis/pending/geoapi/temporal/Period.java |  17 +-
 .../geoapi/temporal}/TemporalOperatorName.java     |  28 +-
 .../sis/pending/geoapi/temporal/package-info.java  |   0
 .../apache/sis/pending/temporal/DefaultPeriod.java |  71 ----
 .../sis/pending/temporal/TemporalUtilities.java    | 137 --------
 .../org/apache/sis/temporal/DefaultInstant.java    | 335 +++++++++++++++++++
 .../org/apache/sis/temporal/DefaultPeriod.java     | 206 ++++++++++++
 .../temporal/DefaultPeriodDuration.java            |   9 +-
 .../org/apache/sis/temporal/GeneralDuration.java   | 369 +++++++++++++++++++++
 .../apache/sis/temporal/LenientDateFormat.java}    |  17 +-
 .../org/apache/sis/temporal}/TemporalDate.java     |  38 +--
 .../org/apache/sis/temporal/TemporalObject.java    |  89 +++++
 .../org/apache/sis/temporal/TemporalObjects.java   | 154 +++++++++
 .../main/org/apache/sis/temporal}/TimeMethods.java | 120 ++++---
 .../sis/{pending => }/temporal/package-info.java   |   9 +-
 .../apache/sis/xml/bind/IdentifierMapAdapter.java  |   1 +
 .../org/apache/sis/xml/bind/gml/TM_Primitive.java  |  14 +-
 .../apache/sis/xml/bind/gml/TimePeriodBound.java   |  19 +-
 .../apache/sis/xml/bind/gts/TM_PeriodDuration.java |   2 +-
 .../org/apache/sis/xml/privy/XmlUtilities.java     |   2 +-
 .../iso/citation/DefaultCitationDateTest.java      |  10 +-
 .../sis/metadata/iso/extent/DefaultExtentTest.java |   3 +-
 .../apache/sis/temporal/DefaultInstantTest.java    | 215 ++++++++++++
 .../org/apache/sis/temporal/DefaultPeriodTest.java | 131 ++++++++
 .../GeneralDurationTest.java}                      |  35 +-
 .../sis/temporal/LenientDateFormatTest.java}       |  46 +--
 .../apache/sis/xml/bind/gml/TimePeriodTest.java    |  17 +-
 .../org/apache/sis/geometry/CoordinateFormat.java  |   2 +-
 .../main/org/apache/sis/io/wkt/AbstractParser.java |   4 +-
 .../main/org/apache/sis/io/wkt/Formatter.java      |   6 +-
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |   2 +-
 .../main/org/apache/sis/io/wkt/WKTFormat.java      |   6 +-
 .../sis/referencing/crs/DefaultTemporalCRS.java    |   2 +-
 .../sis/referencing/datum/AbstractDatum.java       |   2 +-
 .../referencing/datum/DefaultTemporalDatum.java    |   2 +-
 .../referencing/factory/sql/EPSGDataAccess.java    |   6 +-
 .../operation/CoordinateOperationFinder.java       |   2 +-
 .../sis/referencing/privy/AxisDirections.java      |  11 +
 .../sis/referencing/privy/ExtentSelector.java      |   2 +-
 .../referencing/privy/GeodeticObjectBuilder.java   |   2 +-
 .../test/org/apache/sis/io/wkt/ElementTest.java    |   2 +-
 .../sis/io/wkt/GeodeticObjectParserTest.java       |   2 +-
 .../org/apache/sis/referencing/CommonCRSTest.java  |   2 +-
 .../datum/DefaultTemporalDatumTest.java            |   2 +-
 .../sis/referencing/privy/AxisDirectionsTest.java  |   3 +-
 .../sis/test/integration/MetadataVerticalTest.java |   2 +-
 .../sis/storage/geotiff/reader/XMLMetadata.java    |   6 +-
 .../storage/geotiff/reader/XMLMetadataTest.java    |   4 +-
 .../apache/sis/storage/netcdf/base/CRSBuilder.java |   2 +-
 .../sis/storage/netcdf/classic/ChannelDecoder.java |   8 +-
 .../sis/storage/netcdf/classic/VariableInfo.java   |   4 +-
 .../apache/sis/storage/netcdf/base/TestCase.java   |   2 +-
 .../sis/storage/sql/feature/FeatureStream.java     |   4 +-
 .../sis/storage/sql/feature/GeometryGetter.java    |  48 ++-
 .../main/org/apache/sis/storage/gpx/Copyright.java |   2 +-
 .../sis/storage/xml/stream/StaxStreamReader.java   |   6 +-
 .../main/org/apache/sis/storage/csv/Store.java     |   6 +-
 .../org/apache/sis/storage/csv/TimeEncoding.java   |   6 +-
 .../src/org.apache.sis.util/main/module-info.java  |   4 -
 .../main/org/apache/sis/measure/RangeFormat.java   |  44 ++-
 .../main/org/apache/sis/pending/jdk/JDK18.java     |  11 +
 .../privy/LazyCandidate.java}                      |  26 +-
 .../main/org/apache/sis/util/resources/Errors.java |   7 +-
 .../apache/sis/util/resources/Errors.properties    |   3 +-
 .../apache/sis/util/resources/Errors_fr.properties |   1 +
 .../sis/storage/shapefile/ShapefileStore.java      |   8 +-
 .../sis/storage/shapefile/shp/ShapeWriter.java     |   4 +-
 .../sis/storage/shapefile/shx/IndexWriter.java     |   6 +-
 92 files changed, 2162 insertions(+), 561 deletions(-)

diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/PixelInCell.java
index cd33d042c1,5b2b7a1f8d..a50a4ad129
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/PixelInCell.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/PixelInCell.java
@@@ -109,7 -113,22 +109,10 @@@ public enum PixelInCell 
       *
       * @return all names of this constant. This array is never null and never 
empty.
       */
 -    @Override
      public String[] names() {
+         if (this == CELL_CENTER) {
+             return new String[] {name(), identifier, "cell centre"};
+         }
          return new String[] {name(), identifier};
      }
 -
 -    /**
 -     * Returns the enumeration of the same kind as this item.
 -     * This is equivalent to {@link #values()}.
 -     *
 -     * @return the enumeration of the same kind as this item.
 -     */
 -    @Override
 -    public ControlledVocabulary[] family() {
 -        return values();
 -    }
  }
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalFilter.java
index 21629a0fda,38d1984a85..4d3b5d4bea
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalFilter.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalFilter.java
@@@ -16,12 -16,19 +16,15 @@@
   */
  package org.apache.sis.filter;
  
+ import java.time.DateTimeException;
  import org.apache.sis.util.Classes;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.temporal.TimeMethods;
  import org.apache.sis.feature.privy.FeatureExpression;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Period;
 -import org.opengis.filter.Filter;
 -import org.opengis.filter.Expression;
 -import org.opengis.filter.TemporalOperator;
 -import org.opengis.filter.TemporalOperatorName;
 -import org.opengis.filter.InvalidFilterValueException;
 +// Specific to the main branch:
 +import org.apache.sis.pending.geoapi.temporal.Period;
- import org.apache.sis.pending.geoapi.filter.TemporalOperatorName;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
  
  
  /**
@@@ -172,6 -179,8 +175,8 @@@ class TemporalFilter<R,T> extends Binar
      /**
       * Determines if the test(s) represented by this filter passes with the 
given operands.
       * Values of {@link #expression1} and {@link #expression2} shall be two 
single values.
+      *
 -     * @throws InvalidFilterValueException if two temporal objects cannot be 
compared.
++     * @throws IllegalArgumentException if two temporal objects cannot be 
compared.
       */
      @Override
      public boolean test(final R candidate) {
@@@ -190,6 -199,9 +195,9 @@@
                  } else {
                      return operation.evaluate(left, right);
                  }
+             } catch (DateTimeException e) {
 -                throw new InvalidFilterValueException(Errors.format(
++                throw new IllegalArgumentException(Errors.format(
+                         Errors.Keys.CannotCompareInstanceOf_2, 
left.getClass(), right.getClass()));
              }
          }
          return false;
@@@ -224,8 -236,11 +232,11 @@@
              final T left = expression1.apply(candidate);
              if (left != null) {
                  final T right = expression2.apply(candidate);
-                 if (right != null) {
+                 if (right != null) try {
                      return operation.evaluate(left, right);
+                 } catch (DateTimeException e) {
 -                    throw new InvalidFilterValueException(Errors.format(
++                    throw new IllegalArgumentException(Errors.format(
+                             Errors.Keys.CannotCompareInstanceOf_2, 
left.getClass(), right.getClass()));
                  }
              }
              return false;
@@@ -261,8 -276,11 +272,11 @@@
              final Period left = expression1.apply(candidate);
              if (left != null) {
                  final Period right = expression2.apply(candidate);
-                 if (right != null) {
+                 if (right != null) try {
                      return operation.evaluate(left, right);
+                 } catch (DateTimeException e) {
 -                    throw new InvalidFilterValueException(Errors.format(
++                    throw new IllegalArgumentException(Errors.format(
+                             Errors.Keys.CannotCompareInstanceOf_2, 
left.getClass(), right.getClass()));
                  }
              }
              return false;
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalOperation.java
index db72aafd22,a59b174dc6..c95e471866
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalOperation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/TemporalOperation.java
@@@ -16,18 -16,22 +16,22 @@@
   */
  package org.apache.sis.filter;
  
+ import java.util.Optional;
  import java.io.Serializable;
+ import java.time.DateTimeException;
  import java.time.temporal.Temporal;
- import org.apache.sis.util.Classes;
  import org.apache.sis.util.privy.Strings;
  import org.apache.sis.util.collection.WeakHashSet;
- import static org.apache.sis.filter.TimeMethods.BEFORE;
- import static org.apache.sis.filter.TimeMethods.AFTER;
- import static org.apache.sis.filter.TimeMethods.EQUAL;
+ import org.apache.sis.temporal.TimeMethods;
+ import static org.apache.sis.temporal.TimeMethods.BEFORE;
+ import static org.apache.sis.temporal.TimeMethods.AFTER;
+ import static org.apache.sis.temporal.TimeMethods.EQUAL;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Period;
 -import org.opengis.temporal.Instant;
 -import org.opengis.filter.TemporalOperatorName;
 -import org.opengis.temporal.IndeterminateValue;
 +// Specific to the main branch:
 +import org.apache.sis.pending.geoapi.temporal.Period;
- import org.apache.sis.pending.geoapi.filter.TemporalOperatorName;
++import org.apache.sis.pending.geoapi.temporal.Instant;
++import org.apache.sis.pending.geoapi.temporal.IndeterminateValue;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/Visitor.java
index f6b47d6f17,b314efe3de..517433d4bb
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/Visitor.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/Visitor.java
@@@ -23,14 -23,15 +23,14 @@@ import java.util.Collections
  import java.util.function.BiConsumer;
  import org.apache.sis.feature.internal.Resources;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.util.CodeList;
 -import org.opengis.filter.Filter;
 -import org.opengis.filter.Expression;
 -import org.opengis.filter.LogicalOperatorName;
 -import org.opengis.filter.SpatialOperatorName;
 -import org.opengis.filter.DistanceOperatorName;
 -import org.opengis.filter.TemporalOperatorName;
 -import org.opengis.filter.ComparisonOperatorName;
 +// Specific to the main branch:
 +import org.apache.sis.filter.Filter;
 +import org.apache.sis.filter.Expression;
 +import org.apache.sis.pending.geoapi.filter.LogicalOperatorName;
 +import org.apache.sis.pending.geoapi.filter.SpatialOperatorName;
 +import org.apache.sis.pending.geoapi.filter.DistanceOperatorName;
- import org.apache.sis.pending.geoapi.filter.TemporalOperatorName;
 +import org.apache.sis.pending.geoapi.filter.ComparisonOperatorName;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java
index faa3eb8cf3,f547361c23..e724f0968b
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/PeriodLiteral.java
@@@ -18,11 -18,13 +18,12 @@@ package org.apache.sis.filter
  
  import java.time.Instant;
  import java.io.Serializable;
+ import org.apache.sis.temporal.TemporalObjects;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.feature.Feature;
 -import org.opengis.filter.Expression;
 -import org.opengis.filter.Literal;
 -import org.opengis.temporal.Period;
 +// Specific to the main branch:
 +import org.apache.sis.feature.AbstractFeature;
 +import org.apache.sis.pending.geoapi.filter.Literal;
 +import org.apache.sis.pending.geoapi.temporal.Period;
  
  
  /**
@@@ -47,17 -49,35 +48,40 @@@ final class PeriodLiteral implements Pe
      /**
       * Returns the constant value held by this object.
       */
-     @Override public Period getValue() {return this;}
+     @Override
+     public Period getValue() {
+         return this;
+     }
+ 
 +    @Override public Period apply(AbstractFeature input) {return this;}
 +
-     /** Returns a bound of this period. */
-     @Override public Instant getBeginning() {return 
Instant.ofEpochMilli(begin);}
-     @Override public Instant getEnding()    {return 
Instant.ofEpochMilli(end);}
+     /**
+      * Returns the beginning of this period.
+      */
+     @Override
 -    public org.opengis.temporal.Instant getBeginning() {
++    public org.apache.sis.pending.geoapi.temporal.Instant getBeginning() {
+         return TemporalObjects.createInstant(Instant.ofEpochMilli(begin));
+     }
+ 
+     /**
+      * Returns the ending of this period.
+      */
+     @Override
 -    public org.opengis.temporal.Instant getEnding() {
++    public org.apache.sis.pending.geoapi.temporal.Instant getEnding() {
+         return TemporalObjects.createInstant(Instant.ofEpochMilli(end));
+     }
+ 
+     /**
+      * Not needed for the tests.
+      */
+     @Override
 -    public <N> Expression<Feature,N> toValueType(Class<N> target) {
++    public <N> Expression<AbstractFeature,N> toValueType(Class<N> target) {
+         throw new UnsupportedOperationException();
+     }
  
 +    /** Not needed for the tests. */
-     @Override public <N> Expression<AbstractFeature,N> toValueType(Class<N> 
target) {throw new UnsupportedOperationException();}
 +    @Override public Class<AbstractFeature> getResourceClass() {return 
AbstractFeature.class;}
 +
      /**
       * Hash code value. Used by the tests for checking the results of 
deserialization.
       */
diff --cc 
endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/TemporalFilterTest.java
index a07fd7b129,b349e62672..7c043c6476
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/TemporalFilterTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/TemporalFilterTest.java
@@@ -26,10 -26,12 +26,10 @@@ import static org.junit.jupiter.api.Ass
  import org.apache.sis.test.TestCase;
  import static org.apache.sis.test.Assertions.assertSerializedEquals;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Period;
 -import org.opengis.feature.Feature;
 -import org.opengis.filter.FilterFactory;
 -import org.opengis.filter.TemporalOperator;
 -import org.opengis.filter.TemporalOperatorName;
 +// Specific to the main branch:
 +import org.apache.sis.feature.AbstractFeature;
 +import org.apache.sis.pending.geoapi.temporal.Period;
- import org.apache.sis.pending.geoapi.filter.TemporalOperatorName;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
  
  
  /**
diff --cc endorsed/src/org.apache.sis.metadata/main/module-info.java
index d4ac51c38b,2f35b34abf..eae5879e88
--- a/endorsed/src/org.apache.sis.metadata/main/module-info.java
+++ b/endorsed/src/org.apache.sis.metadata/main/module-info.java
@@@ -76,6 -76,15 +76,18 @@@ module org.apache.sis.metadata 
      /*
       * Internal API open only to other Apache SIS modules.
       */
++    exports org.apache.sis.pending.geoapi.temporal to
++            org.apache.sis.feature;
++
+     exports org.apache.sis.temporal to
+             org.apache.sis.referencing,
+             org.apache.sis.feature,
+             org.apache.sis.storage,
+             org.apache.sis.storage.xml,
+             org.apache.sis.storage.netcdf,
+             org.apache.sis.storage.geotiff,
+             org.apache.sis.cql;                 // In the "incubator" 
sub-project.
+ 
      exports org.apache.sis.metadata.privy to
              org.apache.sis.referencing,
              org.apache.sis.feature,
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultEvent.java
index 1b72a5ab4d,732bc85b94..49942940fe
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultEvent.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultEvent.java
@@@ -31,13 -31,8 +31,13 @@@ import org.opengis.metadata.acquisition
  import org.opengis.metadata.acquisition.Sequence;
  import org.opengis.metadata.acquisition.Trigger;
  import org.apache.sis.metadata.iso.ISOMetadata;
- import org.apache.sis.util.privy.TemporalDate;
+ import org.apache.sis.temporal.TemporalDate;
  
 +// Specific to the main branch:
 +import org.opengis.annotation.UML;
 +import static org.opengis.annotation.Obligation.MANDATORY;
 +import static org.opengis.annotation.Specification.ISO_19115_2;
 +
  
  /**
   * Identification of a significant collection point within an operation.
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultCitationDate.java
index 107f978fea,8f75d6a434..3a9d3ddd7b
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultCitationDate.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultCitationDate.java
@@@ -25,13 -25,8 +25,13 @@@ import org.opengis.metadata.citation.Ci
  import org.opengis.metadata.citation.DateType;
  import org.apache.sis.metadata.TitleProperty;
  import org.apache.sis.metadata.iso.ISOMetadata;
- import org.apache.sis.util.privy.TemporalDate;
+ import org.apache.sis.temporal.TemporalDate;
  
 +// Specific to the main branch:
 +import org.opengis.annotation.UML;
 +import static org.opengis.annotation.Obligation.MANDATORY;
 +import static org.opengis.annotation.Specification.ISO_19115;
 +
  
  /**
   * Reference date and event used to describe it.
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java
index d2f256f7de,207f5ce244..074078276d
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java
@@@ -30,13 -30,8 +30,13 @@@ import org.opengis.metadata.distributio
  import org.apache.sis.xml.bind.gco.GO_RecordType;
  import org.apache.sis.xml.bind.gco.GO_Record;
  import org.apache.sis.metadata.iso.ISOMetadata;
- import org.apache.sis.util.privy.TemporalDate;
+ import org.apache.sis.temporal.TemporalDate;
  
 +// Specific to the main branch:
 +import org.opengis.annotation.UML;
 +import static org.opengis.annotation.Obligation.OPTIONAL;
 +import static org.opengis.annotation.Specification.ISO_19115;
 +
  
  /**
   * Common ways in which the resource may be obtained or received, and related 
instructions
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultUsage.java
index 06b4b7afc0,a47f0e1dea..6c60fc875c
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultUsage.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultUsage.java
@@@ -41,12 -41,6 +41,11 @@@ import org.apache.sis.util.iso.Types
  // Specific to the main and geoapi-3.1 branches:
  import org.opengis.metadata.citation.ResponsibleParty;
  
 +// Specific to the main branch:
 +import org.opengis.annotation.UML;
 +import static org.opengis.annotation.Obligation.OPTIONAL;
 +import static org.opengis.annotation.Specification.ISO_19115;
- import org.apache.sis.pending.temporal.DefaultPeriod;
 +
  
  /**
   * Brief description of ways in which the resource(s) is/are currently or has 
been used.
@@@ -173,20 -167,12 +172,20 @@@ public class DefaultUsage extends ISOMe
          super(object);
          if (object != null) {
              specificUsage             = object.getSpecificUsage();
 -            usageDates                = 
copyCollection(object.getUsageDates(), TemporalPrimitive.class);
              userDeterminedLimitations = object.getUserDeterminedLimitations();
              userContactInfo           = 
copyCollection(object.getUserContactInfo(), ResponsibleParty.class);
 -            responses                 = copyCollection(object.getResponses(), 
InternationalString.class);
 -            additionalDocumentation   = 
copyCollection(object.getAdditionalDocumentation(), Citation.class);
 -            identifiedIssues          = 
copyCollection(object.getIdentifiedIssues(), Citation.class);
 +            if (object instanceof DefaultUsage) {
 +                final var c = (DefaultUsage) object;
 +                usageDates                = copyCollection(c.getUsageDates(), 
TemporalPrimitive.class);
 +                responses                 = copyCollection(c.getResponses(), 
InternationalString.class);
 +                additionalDocumentation   = 
copyCollection(c.getAdditionalDocumentation(), Citation.class);
 +                identifiedIssues          = 
copyCollection(c.getIdentifiedIssues(), Citation.class);
 +            } else {
-                 TemporalPrimitive t = 
TemporalUtilities.createInstant(TemporalDate.toTemporal(object.getUsageDate()));
++                Date t = object.getUsageDate();
 +                if (t != null) {
-                     usageDates = List.of(t);
++                    usageDates = 
List.of(TemporalObjects.createInstant(TemporalDate.toTemporal(t)));
 +                }
 +            }
          }
      }
  
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
index 9fa6dcf771,feb25054d3..e45767dde6
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultProcessStep.java
@@@ -41,12 -41,10 +41,13 @@@ import org.apache.sis.temporal.Temporal
  
  // Specific to the main and geoapi-3.1 branches:
  import org.opengis.metadata.citation.ResponsibleParty;
+ import org.apache.sis.temporal.TemporalDate;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.maintenance.Scope;
 +// Specific to the main branch:
 +import org.opengis.metadata.quality.Scope;
 +import org.opengis.annotation.UML;
 +import static org.opengis.annotation.Obligation.OPTIONAL;
 +import static org.opengis.annotation.Specification.ISO_19115;
  
  
  /**
@@@ -189,16 -187,14 +190,17 @@@ public class DefaultProcessStep extend
          if (object != null) {
              description           = object.getDescription();
              rationale             = object.getRationale();
-             stepDateTime          = 
TemporalUtilities.createInstant(object.getDate());
+             stepDateTime          = toInstant(object.getDate());
              processors            = copyCollection(object.getProcessors(), 
ResponsibleParty.class);
 -            references            = copyCollection(object.getReferences(), 
Citation.class);
              sources               = copyCollection(object.getSources(), 
Source.class);
 -            scope                 = object.getScope();
              outputs               = copyCollection(object.getOutputs(), 
Source.class);
              processingInformation = object.getProcessingInformation();
              reports               = copyCollection(object.getReports(), 
ProcessStepReport.class);
 +            if (object instanceof DefaultProcessStep) {
-                 references = copyCollection(((DefaultProcessStep) 
object).getReferences(), Citation.class);
-                 scope      = ((DefaultProcessStep) object).getScope();
++                final var c = (DefaultProcessStep) object;
++                references = copyCollection(c.getReferences(), 
Citation.class);
++                scope      = c.getScope();
 +            }
          }
      }
  
@@@ -305,7 -302,14 +307,11 @@@
      @Deprecated(since="1.0")
      @XmlElement(name = "dateTime", namespace = LegacyNamespaces.GMD)
      public Date getDate() {
-         return FilterByVersion.LEGACY_METADATA.accept() ? 
TemporalUtilities.getAnyDate(getStepDateTime()) : null;
+         if (FilterByVersion.LEGACY_METADATA.accept()) {
+             Date date = TemporalObjects.getAnyDate(getStepDateTime());
 -            if (date == null) {
 -                date = ProcessStep.super.getDate();
 -            }
+             return date;
+         }
+         return null;
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/IndeterminateValue.java
index a30135526f,0bddfc2b60..7377346529
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/IndeterminateValue.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/IndeterminateValue.java
@@@ -14,19 -14,17 +14,20 @@@
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
- package org.apache.sis.pending.geoapi.filter;
 -package org.apache.sis.map.service.se1;
++package org.apache.sis.pending.geoapi.temporal;
++
++import java.util.Locale;
  
  
  /**
-  * Placeholder for GeoAPI 3.1 interfaces (not yet released).
-  * Shall not be visible in public API, as it will be deleted after next 
GeoAPI release.
 - * Subclassed by symbolizer who have resource caches.
 - * This could contain images or models.
 - * The cache instance may be shared by multiple {@link SymbolizerToScene2D} 
instances
 - * using method {@link 
SymbolizerToScene2D#sharedCache(org.apache.sis.internal.renderer.SymbolizerCache)
 }.
++ * Indications that a temporal position is not precisely known.
+  *
 - * @author Johann Sorel (Geomatys)
++ * @author  Martin Desruisseaux (Geomatys)
   */
- @SuppressWarnings("doclint:missing")
- public enum TemporalOperatorName {
-     AFTER, BEFORE, BEGINS, BEGUN_BY, CONTAINS, DURING, EQUALS, OVERLAPS, 
MEETS, ENDS,
-     OVERLAPPED_BY, MET_BY, ENDED_BY, ANY_INTERACTS;
 -public abstract class SymbolizerCache {
++public enum IndeterminateValue {
++    UNKNOWN, NOW, BEFORE, AFTER;
  
 +    public String identifier() {
-         return name().toLowerCase();
++        return name().toLowerCase(Locale.US);
 +    }
  }
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/Instant.java
index f8ccb863ff,0000000000..02b3685f77
mode 100644,000000..100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/Instant.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/Instant.java
@@@ -1,44 -1,0 +1,57 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one or more
 + * contributor license agreements.  See the NOTICE file distributed with
 + * this work for additional information regarding copyright ownership.
 + * The ASF licenses this file to You under the Apache License, Version 2.0
 + * (the "License"); you may not use this file except in compliance with
 + * the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.sis.pending.geoapi.temporal;
 +
++import java.util.Optional;
 +import java.time.temporal.Temporal;
++import java.time.temporal.TemporalAmount;
 +import org.opengis.temporal.TemporalPrimitive;
 +
 +
 +/**
 + * Placeholder for a GeoAPI interfaces not present in GeoAPI 3.0.
 + *
 + * @author  Martin Desruisseaux (Geomatys)
-  * @since   0.3
++ * @since   1.5
 + * @version 1.5
 + */
- public interface Period extends TemporalPrimitive {
++public interface Instant extends TemporalPrimitive {
 +    /**
-      * Links this period to the instant at which it ends.
-      *
-      * @return The beginning instant.
++     * Returns the date, time or position on the time-scale represented by 
this primitive.
 +     */
-     Temporal getBeginning();
++    Temporal getPosition();
 +
 +    /**
-      * Links this period to the instant at which it ends.
++     * Returns the reason why the temporal position is missing or inaccurate.
 +     *
-      * @return The end instant.
++     * @return the reason why the position is indeterminate.
 +     */
-     Temporal getEnding();
++    default Optional<IndeterminateValue> getIndeterminatePosition() {
++        return Optional.empty();
++    }
++
++    /**
++     * Returns the distance from this instant to another instant or a period 
(optional operation).
++     */
++    default TemporalAmount distance(TemporalPrimitive other) {
++        throw new UnsupportedOperationException();
++    }
++
++    default TemporalOperatorName findRelativePosition(TemporalPrimitive 
other) {
++        throw new UnsupportedOperationException();
++    }
 +}
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/Period.java
index f8ccb863ff,a6eeffe0b8..1212795911
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/Period.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/Period.java
@@@ -14,31 -14,32 +14,42 @@@
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
 -package org.apache.sis.xml.bind.referencing;
 +package org.apache.sis.pending.geoapi.temporal;
  
- import java.time.temporal.Temporal;
 -import org.opengis.referencing.datum.VerticalDatumType;
 -import org.apache.sis.xml.bind.gml.CodeListAdapter;
++import java.time.temporal.TemporalAmount;
 +import org.opengis.temporal.TemporalPrimitive;
  
  
  /**
 - * JAXB adapter for (un)marshalling of GeoAPI code list.
 + * Placeholder for a GeoAPI interfaces not present in GeoAPI 3.0.
   *
   * @author  Martin Desruisseaux (Geomatys)
 + * @since   0.3
 + * @version 1.5
   */
 -@SuppressWarnings("deprecation")
 -public final class CD_VerticalDatumType extends 
CodeListAdapter<VerticalDatumType> {
 +public interface Period extends TemporalPrimitive {
      /**
 -     * Empty constructor for JAXB only.
 +     * Links this period to the instant at which it ends.
 +     *
 +     * @return The beginning instant.
       */
-     Temporal getBeginning();
 -    public CD_VerticalDatumType() {
 -    }
++    Instant getBeginning();
  
      /**
 -     * {@inheritDoc}
 +     * Links this period to the instant at which it ends.
       *
 -     * @return {@code VerticalDatumType.class}
 +     * @return The end instant.
       */
-     Temporal getEnding();
 -    @Override
 -    protected Class<VerticalDatumType> getCodeListClass() {
 -        return VerticalDatumType.class;
++    Instant getEnding();
++
++    /**
++     * Returns the duration of this period (optional operation).
++     */
++    default TemporalAmount length() {
++        return getBeginning().distance(getEnding());
++    }
++
++    default TemporalOperatorName findRelativePosition(TemporalPrimitive 
other) {
++        throw new UnsupportedOperationException();
+     }
  }
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/TemporalOperatorName.java
index a30135526f,0000000000..50a05aa191
mode 100644,000000..100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/TemporalOperatorName.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/pending/geoapi/temporal/TemporalOperatorName.java
@@@ -1,32 -1,0 +1,54 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one or more
 + * contributor license agreements.  See the NOTICE file distributed with
 + * this work for additional information regarding copyright ownership.
 + * The ASF licenses this file to You under the Apache License, Version 2.0
 + * (the "License"); you may not use this file except in compliance with
 + * the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
- package org.apache.sis.pending.geoapi.filter;
++package org.apache.sis.pending.geoapi.temporal;
++
++import java.util.Optional;
 +
 +
 +/**
 + * Placeholder for GeoAPI 3.1 interfaces (not yet released).
 + * Shall not be visible in public API, as it will be deleted after next 
GeoAPI release.
 + */
 +@SuppressWarnings("doclint:missing")
 +public enum TemporalOperatorName {
-     AFTER, BEFORE, BEGINS, BEGUN_BY, CONTAINS, DURING, EQUALS, OVERLAPS, 
MEETS, ENDS,
-     OVERLAPPED_BY, MET_BY, ENDED_BY, ANY_INTERACTS;
++    AFTER, BEFORE(AFTER), BEGINS, BEGUN_BY(BEGINS), CONTAINS, 
DURING(CONTAINS),
++    EQUALS, OVERLAPS, MEETS, ENDS,
++    OVERLAPPED_BY(OVERLAPS), MET_BY(MEETS), ENDED_BY(ENDS), ANY_INTERACTS;
++
++    static {
++        EQUALS.reversed = EQUALS;
++        ANY_INTERACTS.reversed = ANY_INTERACTS;
++    }
++
++    private TemporalOperatorName reversed;
++
++    private TemporalOperatorName() {
++    }
++
++    private TemporalOperatorName(TemporalOperatorName r) {
++        reversed = r;
++        r.reversed = this;
++    }
 +
 +    public String identifier() {
 +        return name().toLowerCase();
 +    }
++
++    public Optional<TemporalOperatorName> reversed() {
++        return Optional.of(reversed);
++    }
 +}
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
index 0000000000,0eb5682cbe..7e4a105e37
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultInstant.java
@@@ -1,0 -1,336 +1,335 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.temporal;
+ 
+ import java.util.Objects;
+ import java.util.Optional;
+ import java.time.Duration;
+ import java.time.DateTimeException;
+ import java.time.ZonedDateTime;
+ import java.time.temporal.Temporal;
+ import java.time.temporal.TemporalAmount;
++import org.opengis.temporal.TemporalPrimitive;
+ import org.apache.sis.util.Classes;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.ComparisonMode;
+ import org.apache.sis.util.resources.Errors;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Period;
 -import org.opengis.temporal.Instant;
 -import org.opengis.temporal.TemporalPrimitive;
 -import org.opengis.temporal.IndeterminateValue;
 -import org.opengis.filter.TemporalOperatorName;
 -import org.opengis.temporal.IndeterminatePositionException;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.temporal.Period;
++import org.apache.sis.pending.geoapi.temporal.Instant;
++import org.apache.sis.pending.geoapi.temporal.IndeterminateValue;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
+ 
+ 
+ /**
+  * Default implementation of an instant as defined by ISO 19108.
+  * This is not the same as {@link java.time.Instant}, because the
+  * instant can actually be a date, or may be indeterminate.
+  *
+  * <h2>Thread-safety</h2>
+  * Instances of this class are mostly immutable, except for the list of 
identifiers.
+  * All instances are thread-safe.
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  */
+ final class DefaultInstant extends TemporalObject implements Instant {
+     /**
+      * For cross-version compatibility.
+      */
+     private static final long serialVersionUID = 3898772638524283287L;
+ 
+     /**
+      * The constant for the "unknown" instant.
+      */
+     static final DefaultInstant UNKNOWN = new DefaultInstant(null, 
IndeterminateValue.UNKNOWN);
+ 
+     /**
+      * The temporal position as a date, time or date/time.
+      * May be {@code null} if {@link #indeterminate} is non-null and not 
"before" or "after".
+      */
+     @SuppressWarnings("serial")         // Standard implementations are 
serializable.
+     private final Temporal position;
+ 
+     /**
+      * The indeterminate value, or {@code null} if none.
+      */
+     private final IndeterminateValue indeterminate;
+ 
+     /**
+      * Creates a new instant.
+      *
+      * @param  position       the temporal position, or {@code null} if 
unknown or now.
+      * @param  indeterminate  the indeterminate value, or {@code null} if 
none.
+      */
+     DefaultInstant(final Temporal position, final IndeterminateValue 
indeterminate) {
+         this.position = position;
+         this.indeterminate = indeterminate;
+     }
+ 
+     /**
+      * Returns the given instant as an instance of this implementation class.
+      *
+      * @param  other  the other instance to cast or copy, or {@code null}.
+      * @return the given instant as an {@code DefaultInstant}, or {@code 
null} if the given value was null.
+      */
+     public static DefaultInstant castOrCopy(final Instant other) {
+         if (other == null || other instanceof DefaultInstant) {
+             return (DefaultInstant) other;
+         } else {
+             final var indeterminate = 
other.getIndeterminatePosition().orElse(null);
+             if (indeterminate == IndeterminateValue.UNKNOWN) {
+                 return UNKNOWN;
+             }
+             final Temporal position = other.getPosition();
+             if (indeterminate == null || indeterminate == 
IndeterminateValue.BEFORE || indeterminate == IndeterminateValue.AFTER) {
+                 Objects.requireNonNull(position);
+             }
+             return new DefaultInstant(position, indeterminate);
+         }
+     }
+ 
+     /**
+      * Returns the date, time or position on the time-scale represented by 
this primitive.
+      * Should not be null, unless the value is {@linkplain 
IndeterminateValue#UNKNOWN unknown}.
+      *
+      * @return the date, time or position on the time-scale represented by 
this primitive.
+      */
+     @Override
+     public final Temporal getPosition() {
+         if (indeterminate != IndeterminateValue.NOW) {
+             return position;
+         }
+         return ZonedDateTime.now();
+     }
+ 
+     /**
+      * Returns the reason why the temporal position is missing or inaccurate.
+      *
+      * @return the reason why the temporal position is missing or inaccurate.
+      */
+     @Override
+     public final Optional<IndeterminateValue> getIndeterminatePosition() {
+         return Optional.ofNullable(indeterminate);
+     }
+ 
+     /**
+      * Returns the distance from this instant to another instant or period.
+      *
+      * @param  other the other object from which to measure the distance.
+      * @return the distance from this instant to another instant or period.
+      * @throws DateTimeException if the duration cannot be computed.
+      * @throws ArithmeticException if the calculation exceeds the integer 
capacity.
+      */
+     @Override
+     public TemporalAmount distance(final TemporalPrimitive other) {
+         ArgumentChecks.ensureNonNull("other", other);
+         if (other instanceof Instant) {
+             return GeneralDuration.distance(this, (Instant) other, false, 
true);
+         }
+         if (other instanceof Period) {
+             final var p = (Period) other;
+             TemporalAmount t = GeneralDuration.distance(this, 
p.getBeginning(), false, false);
+             if (t == null) {
+                 t = GeneralDuration.distance(this, p.getEnding(), true, 
false);
+                 if (t == null) {
+                     return Duration.ZERO;
+                 }
+             }
+             return t;
+         }
+         throw new 
DateTimeException(Errors.format(Errors.Keys.UnsupportedType_1, 
other.getClass()));
+     }
+ 
+     /**
+      * Determines the position of this instant relative to another temporal 
primitive.
+      * The relative position is identified by an operator which evaluates to 
{@code true}
+      * when the two operands are {@code this} and {@code other}.
+      *
+      * @param  other the other primitive for which to determine the relative 
position.
+      * @return a temporal operator which is true when evaluated between this 
instant and the other primitive.
+      * @throws DateTimeException if the temporal objects cannot be compared.
+      */
+     @Override
+     public TemporalOperatorName findRelativePosition(final TemporalPrimitive 
other) {
+         ArgumentChecks.ensureNonNull("other", other);
+         if (other instanceof Instant) {
+             return relativeToInstant((Instant) other);
+         }
+         if (other instanceof Period) {
+             return relativeToPeriod((Period) other);
+         }
+         throw new 
DateTimeException(Errors.format(Errors.Keys.UnsupportedType_1, 
other.getClass()));
+     }
+ 
+     /**
+      * Determines the position of this instant relative to a period.
+      *
+      * @param  other the period for which to determine the relative position.
+      * @return a temporal operator which is true when evaluated between this 
primitive and the period.
+      * @throws DateTimeException if the temporal objects cannot be compared.
+      */
+     final TemporalOperatorName relativeToPeriod(final Period other) {
+         TemporalOperatorName relation = 
relativeToInstant(other.getBeginning());
+         String erroneous;
+         if (relation == TemporalOperatorName.BEFORE) return relation;
+         if (relation == TemporalOperatorName.EQUALS) return 
TemporalOperatorName.BEGINS;
+         if (relation == TemporalOperatorName.AFTER) {
+             relation = relativeToInstant(other.getEnding());
+             if (relation == TemporalOperatorName.AFTER)  return relation;
+             if (relation == TemporalOperatorName.EQUALS) return 
TemporalOperatorName.ENDS;
+             if (relation == TemporalOperatorName.BEFORE) return 
TemporalOperatorName.DURING;
+             erroneous = "ending";
+         } else {
+             erroneous = "beginning";
+         }
+         // Should never happen.
+         throw new 
DateTimeException(Errors.format(Errors.Keys.IllegalMapping_2, erroneous, 
relation));
+     }
+ 
+     /**
+      * Determines the position of this instant relative to another instant.
+      *
+      * @param  other the other instant for which to determine the relative 
position.
+      * @return a temporal operator which is true when evaluated between this 
primitive and the other primitive.
+      * @throws DateTimeException if the temporal objects cannot be compared.
+      */
+     @SuppressWarnings({"rawtypes", "unchecked"})    // See end of method.
+     private TemporalOperatorName relativeToInstant(final Instant other) {
+         boolean canTestBefore = true;
+         boolean canTestAfter  = true;
+         boolean canTestEqual  = true;
+         if (indeterminate != null && indeterminate != IndeterminateValue.NOW) 
{
+             canTestBefore = (indeterminate == IndeterminateValue.BEFORE);
+             canTestAfter  = (indeterminate == IndeterminateValue.AFTER);
+             canTestEqual  = false;
+         }
+         final IndeterminateValue oip = 
other.getIndeterminatePosition().orElse(null);
+         if (oip != null) {
+             if (oip != IndeterminateValue.NOW) {
+                 canTestBefore &= (oip == IndeterminateValue.AFTER);
+                 canTestAfter  &= (oip == IndeterminateValue.BEFORE);
+                 canTestEqual   = false;
+             } else if (indeterminate == IndeterminateValue.NOW) {
+                 return TemporalOperatorName.EQUALS;
+             }
+         }
+ cmp:    if (canTestBefore | canTestAfter | canTestEqual) {
+             final Temporal t1;                  // Same as `this.position` 
except if "now".
+             final Temporal t2;                  // Position of the other 
instant.
+             final TimeMethods<?> comparators;   // The "is before", "is 
after" and "is equal" methods to invoke.
+             /*
+              * First, resolve the case when the indeterminate value is "now". 
Do not invoke `getPosition()`
+              * because the results could differ by a few nanoseconds when two 
"now" instants are compared,
+              * and also for getting a temporal object of the same type than 
the other instant.
+              */
+             if (oip == IndeterminateValue.NOW) {
+                 t1 = position;
+                 if (t1 == null) break cmp;
+                 comparators = TimeMethods.find(t1.getClass());
+                 t2 = comparators.now();
+             } else {
+                 t2 = other.getPosition();
+                 if (t2 == null) break cmp;
+                 if (indeterminate == IndeterminateValue.NOW) {
+                     comparators = TimeMethods.find(t2.getClass());
+                     t1 = comparators.now();
+                 } else {
+                     t1 = position;
+                     if (t1 == null) break cmp;
+                     comparators = 
TimeMethods.find(Classes.findCommonClass(t1.getClass(), t2.getClass()));
+                 }
+             }
+             // This is where the @SuppressWarnings(…) apply.
+             if (canTestBefore && ((TimeMethods) 
comparators).isBefore.test(t1, t2)) return TemporalOperatorName.BEFORE;
+             if (canTestAfter  && ((TimeMethods) comparators).isAfter 
.test(t1, t2)) return TemporalOperatorName.AFTER;
+             if (canTestEqual  && ((TimeMethods) comparators).isEqual 
.test(t1, t2)) return TemporalOperatorName.EQUALS;
+         }
 -        throw new 
IndeterminatePositionException(Errors.format(Errors.Keys.IndeterminatePosition));
++        throw new 
DateTimeException(Errors.format(Errors.Keys.IndeterminatePosition));
+     }
+ 
+     /**
+      * Compares this instant with the given object, optionally ignoring 
timezone.
+      * If the comparison mode ignores metadata, this method compares only the 
position on the timeline.
+      *
+      * @param  other  the object to compare to {@code this}.
+      * @param  mode   the strictness level of the comparison.
+      * @return {@code true} if both objects are equal according the given 
comparison mode.
+      */
+     @Override
+     public boolean equals(final Object object, final ComparisonMode mode) {
+         if (mode.equals(ComparisonMode.STRICT)) {   // Use `mode.equals(…)` 
for opportunistic null check.
+             return equals(object);
+         }
+         if (object instanceof Instant) {
+             final var that = (Instant) object;
+             if (indeterminate == 
that.getIndeterminatePosition().orElse(null)) {
+                 if (indeterminate == IndeterminateValue.NOW || indeterminate 
== IndeterminateValue.UNKNOWN) {
+                     return true;
+                 }
+                 final Temporal other = that.getPosition();
+                 return Objects.equals(position, other) ||       // Needed in 
all cases for testing null values.
+                         (mode.isIgnoringMetadata() && 
TimeMethods.compareAny(TimeMethods.EQUAL, position, other));
+             }
+         }
+         return false;
+     }
+ 
+     /**
+      * Compares this instant with the given object for equality.
+      */
+     @Override
+     public boolean equals(final Object object) {
+         if (object == this) {
+             return true;
+         }
+         if (object instanceof DefaultInstant) {
+             final var that = (DefaultInstant) object;
+             return indeterminate == that.indeterminate
+                     && Objects.equals(position, that.position)
+                     && equalIdentifiers(that);
+         }
+         return false;
+     }
+ 
+     /**
+      * Computes a hash code value for this instant.
+      */
+     @Override
+     public int hashCode() {
+         return Objects.hash(position, indeterminate);
+     }
+ 
+     /**
+      * Returns a string representation of this instant.
+      * This is either the date, the indeterminate position (e.g., "now"),
+      * or a combination of both (e.g., "after 2000-01-01").
+      */
+     @Override
+     public String toString() {
+         final var s = new StringBuilder();
+         if (indeterminate != null) {
+             s.append(indeterminate.identifier());
+             if (position != null) {
+                 s.append(' ').append(position);
+             }
+         } else {
+             s.append(position);     // Should never be null.
+         }
+         return s.toString();
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultPeriod.java
index 0000000000,a5142cc6c0..0db49a9a5b
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultPeriod.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultPeriod.java
@@@ -1,0 -1,206 +1,206 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.temporal;
+ 
+ import java.util.Map;
+ import java.time.DateTimeException;
+ import java.time.temporal.TemporalAmount;
++import org.opengis.temporal.TemporalPrimitive;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.ComparisonMode;
+ import org.apache.sis.util.Utilities;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.privy.LazyCandidate;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Instant;
 -import org.opengis.temporal.Period;
 -import org.opengis.temporal.TemporalPrimitive;
 -import org.opengis.filter.TemporalOperatorName;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.temporal.Instant;
++import org.apache.sis.pending.geoapi.temporal.Period;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
+ 
+ 
+ /**
+  * Default implementation of GeoAPI period.
+  *
+  * <h2>Thread-safety</h2>
+  * Instances of this class are mostly immutable, except for the list of 
identifiers.
+  * All instances are thread-safe.
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  */
+ final class DefaultPeriod extends TemporalObject implements Period {
+     /**
+      * For cross-version compatibility.
+      */
+     private static final long serialVersionUID = 3870895998810224339L;
+ 
+     /**
+      * Bounds making this period.
+      */
+     @SuppressWarnings("serial")         // Default implementations are 
serializable.
+     private final Instant beginning, ending;
+ 
+     /**
+      * Creates a new period with the given bounds.
+      */
+     DefaultPeriod(final Instant beginning, final Instant ending) {
+         this.beginning = beginning;
+         this.ending    = ending;
+     }
+ 
+     /**
+      * Returns the beginning instant at which this period starts.
+      */
+     @Override
+     public Instant getBeginning() {
+         return beginning;
+     }
+ 
+     /**
+      * Returns the ending instant at which this period ends.
+      */
+     @Override
+     public Instant getEnding() {
+         return ending;
+     }
+ 
+     /**
+      * Returns the duration of this period.
+      */
+     @Override
+     public TemporalAmount length() {
+         return GeneralDuration.distance(beginning, ending, false, false);
+     }
+ 
+     /**
+      * Determines the position of this period relative to another temporal 
primitive.
+      * The relative position is identified by an operator which evaluates to 
{@code true}
+      * when the two operands are {@code this} and {@code other}.
+      *
+      * @param  other the other primitive for which to determine the relative 
position.
+      * @return a temporal operator which is true when evaluated between this 
period and the other primitive.
+      * @throws DateTimeException if the temporal objects cannot be compared.
+      */
+     @Override
+     public TemporalOperatorName findRelativePosition(final TemporalPrimitive 
other) {
+         ArgumentChecks.ensureNonNull("other", other);
+         if (other instanceof Instant) {
+             return DefaultInstant.castOrCopy((Instant) 
other).relativeToPeriod(this).reversed().orElseThrow();
+         }
+         if (other instanceof Period) {
+             final var period = (Period) other;
+             String erroneous;
+             TemporalOperatorName relation = 
DefaultInstant.castOrCopy(beginning).relativeToPeriod(period);
+             final var map = POSITIONS.get(relation);
+             if (map != null) {
+                 relation = 
DefaultInstant.castOrCopy(ending).relativeToPeriod(period);
+                 final var result = map.get(relation);
+                 if (result != null) {
+                     return result;
+                 }
+                 erroneous = "ending";
+             } else {
+                 erroneous = "beginning";
+             }
+             // Should never happen.
+             throw new 
DateTimeException(Errors.format(Errors.Keys.IllegalMapping_2, erroneous, 
relation));
+         }
+         throw new 
DateTimeException(Errors.format(Errors.Keys.UnsupportedType_1, 
other.getClass()));
+     }
+ 
+     /**
+      * Relative positions for given pairs (beginning, ending) relative 
positions.
+      * Keys of this static map are the relative positions of the beginning of 
this period relative to the other period.
+      * Keys of the enclosed maps are the relative positions of the ending of 
this period relative to the other period.
+      */
+     @LazyCandidate
+     private static final Map<TemporalOperatorName, Map<TemporalOperatorName, 
TemporalOperatorName>> POSITIONS = Map.of(
+             TemporalOperatorName.BEFORE, Map.of(
+                     TemporalOperatorName.BEFORE, TemporalOperatorName.BEFORE,
+                     TemporalOperatorName.BEGINS, TemporalOperatorName.MEETS,
+                     TemporalOperatorName.DURING, 
TemporalOperatorName.OVERLAPS,
+                     TemporalOperatorName.ENDS,   
TemporalOperatorName.ENDED_BY,
+                     TemporalOperatorName.AFTER,  
TemporalOperatorName.CONTAINS),
+             TemporalOperatorName.BEGINS, Map.of(
+                     TemporalOperatorName.BEGINS, TemporalOperatorName.MEETS,
+                     TemporalOperatorName.DURING, TemporalOperatorName.BEGINS,
+                     TemporalOperatorName.ENDS,   TemporalOperatorName.EQUALS,
+                     TemporalOperatorName.AFTER,  
TemporalOperatorName.BEGUN_BY),
+             TemporalOperatorName.DURING, Map.of(
+                     TemporalOperatorName.DURING, TemporalOperatorName.DURING,
+                     TemporalOperatorName.ENDS,   TemporalOperatorName.ENDS,
+                     TemporalOperatorName.AFTER,  
TemporalOperatorName.OVERLAPPED_BY),
+             TemporalOperatorName.ENDS, Map.of(
+                     TemporalOperatorName.ENDS,   TemporalOperatorName.MET_BY,
+                     TemporalOperatorName.AFTER,  TemporalOperatorName.MET_BY),
+             TemporalOperatorName.AFTER, Map.of(
+                     TemporalOperatorName.AFTER,  TemporalOperatorName.AFTER));
+ 
+     /**
+      * Returns a string representation in ISO 8601 format.
+      * The format is {@code <start>/<end>}.
+      */
+     @Override
+     public String toString() {
+         return beginning + "/" + ending;
+     }
+ 
+     /**
+      * Hash code value of the time position.
+      */
+     @Override
+     public int hashCode() {
+         return beginning.hashCode() + 37 * ending.hashCode();
+     }
+ 
+     /**
+      * Compares with given object for equality.
+      */
+     @Override
+     public boolean equals(final Object obj) {
+         if (obj == this) {
+             return true;
+         }
+         if (obj instanceof DefaultPeriod) {
+             final var other = (DefaultPeriod) obj;
+             return beginning.equals(other.beginning)
+                    && ending.equals(other.ending)
+                    && equalIdentifiers(other);
+         }
+         return false;
+     }
+ 
+     /**
+      * Compares this period with the given object, optionally ignoring 
timezone.
+      * If the comparison mode ignores metadata, this method compares only the 
position on the timeline.
+      *
+      * @param  other  the object to compare to {@code this}.
+      * @param  mode   the strictness level of the comparison.
+      * @return {@code true} if both objects are equal according the given 
comparison mode.
+      */
+     @Override
+     public boolean equals(final Object object, final ComparisonMode mode) {
+         if (mode.equals(ComparisonMode.STRICT)) {   // Use `mode.equals(…)` 
for opportunistic null check.
+             return equals(object);
+         }
+         if (object instanceof Period) {
+             final var other = (Period) object;
+             return Utilities.deepEquals(beginning, other.getBeginning(), 
mode) &&
+                    Utilities.deepEquals(ending,    other.getEnding(),    
mode);
+         }
+         return false;
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultPeriodDuration.java
index fb98bc56fe,c0372e7397..0ef273bdd6
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultPeriodDuration.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/DefaultPeriodDuration.java
@@@ -20,13 -20,21 +20,16 @@@ import java.io.Serializable
  import java.time.temporal.TemporalAmount;
  import org.opengis.temporal.PeriodDuration;
  
 -// Specific to the geoapi-3.1 branch:
 -import java.time.temporal.Temporal;
 -import java.time.temporal.TemporalUnit;
 -import java.util.List;
 -
  
  /**
-  * Default implementation of GeoAPI period duration. This is a temporary 
class;
-  * GeoAPI temporal interfaces are expected to change a lot in a future 
revision.
+  * Default implementation of GeoAPI period duration.
   *
   * @author  Martin Desruisseaux (Geomatys)
+  *
+  * @deprecated This is a temporary class for compatibility with GeoAPI 3.x 
only.
+  * It should disappear with GeoAPI 4.0.
   */
+ @Deprecated
  @SuppressWarnings("serial")
  public final class DefaultPeriodDuration implements PeriodDuration, 
Serializable {
      /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
index 0000000000,6cd1724710..894d4c6ac5
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java
@@@ -1,0 -1,370 +1,369 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.temporal;
+ 
+ import java.util.List;
+ import java.util.Optional;
+ import java.io.Serializable;
+ import java.time.Period;
+ import java.time.Duration;
+ import java.time.LocalTime;
+ import java.time.LocalDateTime;
+ import java.time.ZonedDateTime;
+ import java.time.DateTimeException;
+ import java.time.chrono.ChronoLocalDate;
+ import java.time.chrono.ChronoPeriod;
+ import java.time.temporal.ChronoField;
+ import java.time.temporal.ChronoUnit;
+ import java.time.temporal.Temporal;
+ import java.time.temporal.TemporalAmount;
+ import java.time.temporal.TemporalUnit;
+ import java.time.temporal.UnsupportedTemporalTypeException;
+ import org.apache.sis.pending.jdk.JDK18;
+ import org.apache.sis.util.privy.UnmodifiableArrayList;
+ import org.apache.sis.util.resources.Errors;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Instant;
 -import org.opengis.temporal.IndeterminateValue;
 -import org.opengis.temporal.IndeterminatePositionException;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.temporal.Instant;
++import org.apache.sis.pending.geoapi.temporal.IndeterminateValue;
+ 
+ 
+ /**
+  * A data type to be used for describing length or distance in the temporal 
dimension.
+  * This implementation combines {@link java.time.Period} with {@link 
java.time.Duration}
+  * for situations where both of them are needed together (which is not 
recommended).
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  */
+ public final class GeneralDuration implements TemporalAmount, Serializable {
+     /**
+      * For cross-version compatibility.
+      */
+     private static final long serialVersionUID = -521478824158640275L;
+ 
+     /**
+      * The period in numbers of years, months and days.
+      * Shall be non-null and non-zero.
+      */
+     public final Period period;
+ 
+     /**
+      * The time part of the period in numbers of hours, minutes and seconds.
+      * Shall be non-null, non-zero and less than one day.
+      */
+     public final Duration time;
+ 
+     /**
+      * Creates a new instance with the given parts.
+      * The two parts must be non-null and non-zero.
+      *
+      * @param  period  the period in numbers of years, months and days.
+      * @param  time    the time part of the period in numbers of hours, 
minutes and seconds.
+      */
+     private GeneralDuration(final Period period, final Duration time) {
+         this.period = period;
+         this.time   = time;
+     }
+ 
+     /**
+      * Returns the duration for the given components.
+      * If any component is zero, the other component is returned.
+      *
+      * @param  period  the period.
+      * @param  time    the component.
+      * @return the temporal amount from the given components.
+      */
+     public static TemporalAmount of(final Period period, final Duration time) 
{
+         if (period.isZero()) return time;
+         if (time.isZero()) return period;
+         return new GeneralDuration(period, time);
+     }
+ 
+     /**
+      * Parses a temporal amount which may contain a period and a duration 
part.
+      * This method returns a {@link Period} or {@link Duration} if those 
objects
+      * are sufficient, or an instance of {@code GeneralDuration} is last 
resort.
+      *
+      * @param  text  the text to parse.
+      * @return the parsed period and/or duration.
+      * @throws DateTimeParseException if the given text cannot be parsed.
+      *
+      * @see Period#parse(CharSequence)
+      * @see Duration#parse(CharSequence)
+      */
+     public static TemporalAmount parse(final CharSequence text) {
+         char previousLetter = 0;
+         final int length = text.length();
+         for (int i=0; i<length; i++) {
+             char c = text.charAt(i);
+             if (c >= 'a' && c <= 'z') c -= 'a' - 'A';       // Quick upper 
case, ASCII characters only.
+             if (c >= 'A' && c <= 'Z') {
+                 if (c == 'T') {
+                     if (previousLetter == 'P') {
+                         return Duration.parse(text);
+                     }
+                     return of(Period.parse(text.subSequence(0, i)),
+                             Duration.parse(new StringBuilder(length - i + 
1).append('P').append(text, i, length)));
+                 }
+                 previousLetter = c;
+             }
+         }
+         return Period.parse(text);
+     }
+ 
+     /**
+      * Returns the temporal position of the given instant if that position is 
determinate or is "now".
+      * Otherwise, throws an exception. If the position is "now", then this 
method returns {@code null}
+      * instead of fetching the current time in order to avoid mismatch when 
comparing two "now" values
+      * that are a few nanoseconds apart.
+      *
+      * @param  t  the instant for which to get the temporal position, or 
{@code null}.
+      * @return temporal position of the given instant, or {@code null} for 
"now".
+      * @throws DateTimeException if the given instant is null or its position 
is indeterminate.
+      */
+     private static Temporal getDeterminatePosition(final Instant t) {
+         if (t != null) {
+             final Optional<IndeterminateValue> p = 
t.getIndeterminatePosition();
+             if (p.isEmpty()) {
+                 return t.getPosition();
+             }
+             if (p.get() == IndeterminateValue.NOW) {
+                 return null;        // Avoid fetching the current time now.
+             }
+         }
 -        throw new 
IndeterminatePositionException(Errors.format(Errors.Keys.IndeterminatePosition));
++        throw new 
DateTimeException(Errors.format(Errors.Keys.IndeterminatePosition));
+     }
+ 
+     /**
+      * Returns the distance between the two given ISO 19108 instants.
+      * If the result is negative, then the return value depends on the {@code 
absolute} argument:
+      * If {@code true}, this method returns the absolute value. Otherwise, it 
returns {@code null}.
+      *
+      * <p>If everything else is equal, methods such as {@link 
ChronoLocalDate#until(ChronoLocalDate)}
+      * will be invoked on the {@code self} instance. It makes a difference in 
the type of the result.
+      * For computing a duration with arguments in the reverse order, the 
{@code negate} parameter can
+      * be set to {@code true}.</p>
+      *
+      * @param  self      the first instant from which to measure the distance.
+      * @param  other     the second instant from which to measure the 
distance.
+      * @param  negate    whether to negate the result. True for duration from 
{@code other} to {@code self}.
+      * @param  absolute  whether to return absolute value. If false, negative 
result is replaced by null.
+      * @return the distance, or {@code null} if the result is negative and 
{@code absolute} is false.
+      * @throws DateTimeException if the duration cannot be computed.
+      * @throws ArithmeticException if the calculation exceeds the integer 
capacity.
+      */
+     static TemporalAmount distance(final Instant self, final Instant other, 
final boolean negate, final boolean absolute) {
+         /*
+          * Get the temporal value, or null if "now". Other indeterminate 
values cause an exception to be thrown.
+          * We use null for "now" instead of fetching the current time for two 
reasons: avoid mismatch by a few
+          * nanoseconds when comparing `t1` with `t2`, and for choosing a type 
compatible with the other instant.
+          */
+         Temporal t1 = getDeterminatePosition(self);
+         Temporal t2 = getDeterminatePosition(other);
+         /*
+          * Ensures that the given objects both have a date part, or that none 
of them have a date part.
+          * Note that the "epoch day" field is supported by `LocalDate` as 
well as the dates with zone ID.
+          */
+         final boolean hasDate = isSupportedByBoth(ChronoField.EPOCH_DAY, t1, 
t2);
+         /*
+          * If at least one date has a timezone, then we require that both 
dates have a timezone.
+          * It allows an unambiguous duration in number of days, without 
time-varying months or years.
+          * If one date has a timezone and the other does not, a 
`DateTimeException` will be thrown.
+          *
+          * Note 1: we could be lenient and handle the two dates as if they 
were local, ignoring the timezone.
+          * But we avoid false sense of accuracy for now. We may revisit this 
policy later if there is a need.
+          */
+         if (t1 != null && t1.isSupported(ChronoField.OFFSET_SECONDS)) {
+             if (t2 == null) t2 = ZonedDateTime.now();
+             final Duration p = Duration.between(t1, t2);
+             return (absolute || p.isNegative() == negate) ? p.abs() : null;
+         }
+         if (t2 != null && (!hasDate || 
t2.isSupported(ChronoField.OFFSET_SECONDS))) {
+             if (t1 == null) t1 = ZonedDateTime.now();
+             final Duration p = Duration.between(t2, t1);        // Negative 
of the result.
+             return (absolute || p.isNegative() != negate) ? p.abs() : null;
+         }
+         /*
+          * Ensure that the given temporal objects both have a time part, or 
none of them have a time part.
+          * If only one of them has a time part, we do not interpret the other 
one as an instant at midnight
+          * in order to avoid false sense of accuracy.
+          */
+         final boolean hasTime = isSupportedByBoth(ChronoField.SECOND_OF_DAY, 
t1, t2);
+         if (t1 == null) t1 = LocalDateTime.now();
+         if (t2 == null) t2 = LocalDateTime.now();
+         ChronoLocalDate d1 = null, d2 = null;
+         if (hasDate) {
+             d1 = ChronoLocalDate.from(t1);
+             d2 = ChronoLocalDate.from(t2);
+             if (!absolute && (negate ? d1.isBefore(d2) : d1.isAfter(d2))) {
+                 return null;        // Stop early if we can.
+             }
+         }
+         /*
+          * Compute the duration in the time part. If negative (after negation 
if `negate` is true),
+          * then we add the necessary number of days to make it positive and 
remove the same number
+          * of days from the date. We adjust the date instead of the period 
computed by `d1.until(d2)`
+          * in order to have the correct adjustment for the variable number of 
days in each month.
+          */
+         Duration time = Duration.ZERO;
+         if (hasTime) {
+             time = Duration.between(LocalTime.from(t1), LocalTime.from(t2));
+             if (hasDate) {
+                 final boolean isPositive = d1.isBefore(d2);
+                 if (isPositive || d1.isAfter(d2)) {                 // 
Require the period to be non-zero.
+                     if (isPositive ? time.isNegative() : 
JDK18.isPositive(time)) {
+                         long n = time.toDays();                     // 
Truncated toward 0.
+                         if (isPositive) {
+                             d2 = d2.plus (--n, ChronoUnit.DAYS);    // `n` is 
negative. Reduces period by decreasing the ending.
+                         } else {
+                             d1 = d1.minus(++n, ChronoUnit.DAYS);    // `n` is 
positive. Reduces period by increasing the beginning.
+                         }
+                         time = time.minusDays(n);                   // If 
negative, make positive. If positive, make negative.
+                     }
+                 }
+             }
+         }
+         /*
+          * Get the period for the date part, then combine with the time part 
if non-zero.
+          * The result shall be either positive or null.
+          */
+         if (hasDate) {
+             ChronoPeriod period = d1.until(d2);
+             if (period.isZero()) {
+                 if (time.isZero()) {
+                     return period;
+                 }
+             } else {
+                 if (period.isNegative()) {
+                     if (!(negate | absolute)) {                 // Equivalent 
to (!negate && !absolute).
+                         return null;
+                     }
+                     period = period.negated();
+                 } else if (negate & !absolute) {                // Equivalent 
to (negate && !absolute).
+                     return null;
+                 }
+                 return time.isZero() ? period : new 
GeneralDuration(Period.from(period), time.abs());
+             }
+         }
+         return (absolute || time.isNegative() == negate) ? time.abs() : null;
+     }
+ 
+     /**
+      * Verifies if the given field is supported by both temporal objects.
+      * Either the field is supported by both objects, or either it is 
supported by none of them.
+      * If one object support the field and the other does not, the two 
objects are considered incompatible.
+      * At least one of the given objects shall be non-null.
+      *
+      * @param  field  the field to test.
+      * @param  t1     the first temporal object, or {@code null} for "now".
+      * @param  t2     the second temporal object, or {@code null} for "now".
+      * @return whether the given object is supported.
+      * @throws DateTimeException if the two objects are incompatible.
+      */
+     private static boolean isSupportedByBoth(final ChronoField field, final 
Temporal t1, final Temporal t2) {
+         final boolean isSupported = (t1 != null ? t1 : t2).isSupported(field);
+         if (t1 != null && t2 != null && isSupported != t2.isSupported(field)) 
{
+             throw new 
DateTimeException(Errors.format(Errors.Keys.CanNotConvertFromType_2,
+                     (isSupported ? t2 : t1).getClass(),
+                     (isSupported ? t1 : t2).getClass()));
+         }
+         return isSupported;
+     }
+ 
+     /**
+      * Returns the list of units that are supported by this implementation.
+      * This is the union of the units supported by the date part and by the 
time part.
+      */
+     @Override
+     public List<TemporalUnit> getUnits() {
+         var prefix = period.getUnits();
+         var suffix = time.getUnits();
+         int i      = prefix.size();
+         var units  = prefix.toArray(new TemporalUnit[i + suffix.size()]);
+         for (TemporalUnit unit : suffix) {
+             units[i++] = unit;
+         }
+         return UnmodifiableArrayList.wrap(units);
+     }
+ 
+     /**
+      * Returns the value of the requested unit.
+      *
+      * @param  unit  the unit to get;
+      * @return value of the specified unit.
+      * @throws UnsupportedTemporalTypeException if the {@code unit} is not 
supported.
+      */
+     @Override
+     public long get(final TemporalUnit unit) {
+         return (unit.isDateBased() ? period : time).get(unit);
+     }
+ 
+     /**
+      * Adds this duration to the specified temporal object.
+      *
+      * @param temporal  the temporal object to add the amount to.
+      * @return an object with the addition done.
+      */
+     @Override
+     public Temporal addTo(Temporal temporal) {
+         return temporal.plus(period).plus(time);
+     }
+ 
+     /**
+      * Subtracts this duration from the specified temporal object.
+      *
+      * @param temporal  the temporal object to subtract the amount from.
+      * @return an object with the subtraction done.
+      */
+     @Override
+     public Temporal subtractFrom(Temporal temporal) {
+         return temporal.minus(period).minus(time);
+     }
+ 
+     /**
+      * Compares this duration with the given object for equality.
+      *
+      * @param  object  the object to compare with this duration.
+      * @return whether the two objects are equal.
+      */
+     @Override
+     public boolean equals(final Object object) {
+         if (object instanceof GeneralDuration) {
+             final var other = (GeneralDuration) object;
+             return period.equals(other.period) && time.equals(other.time);
+         }
+         return false;
+     }
+ 
+     /**
+      * Returns a hash code value for this duration.
+      *
+      * @return a hash code value for this duration.
+      */
+     @Override
+     public int hashCode() {
+         return period.hashCode() * 31 + time.hashCode();
+     }
+ 
+     /**
+      * Returns this duration in ISO 8601 format.
+      */
+     @Override
+     public String toString() {
+         return period.toString() + time.toString().substring(1);
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalObjects.java
index 0000000000,027c086aa2..72877deddd
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalObjects.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/TemporalObjects.java
@@@ -1,0 -1,154 +1,154 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.temporal;
+ 
+ import java.util.Date;
+ import java.time.temporal.Temporal;
+ import org.opengis.temporal.TemporalPrimitive;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.resources.Errors;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.IndeterminateValue;
 -import org.opengis.temporal.Instant;
 -import org.opengis.temporal.Period;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.temporal.IndeterminateValue;
++import org.apache.sis.pending.geoapi.temporal.Instant;
++import org.apache.sis.pending.geoapi.temporal.Period;
+ 
+ 
+ /**
+  * Utilities related to ISO 19108 objects.
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  * @author  Guilhem Legal (Geomatys)
+  */
+ public final class TemporalObjects {
+     /**
+      * Do not allow instantiation of this class.
+      */
+     private TemporalObjects() {
+     }
+ 
+     /**
+      * Creates an instant for the given Java temporal instant.
+      *
+      * @param  position  the date for which to create instant, or {@code 
null}.
+      * @return the instant, or an unknown instant if the given time was null.
+      */
+     public static Instant createInstant(final Temporal position) {
+         return (position == null) ? DefaultInstant.UNKNOWN : new 
DefaultInstant(position, null);
+     }
+ 
+     /**
+      * Creates an instant for the given Java temporal instant associated to 
the indeterminate value.
+      * This is used for creating "before" or "after" instant.
+      *
+      * @param  position       the date and/or time for which to create 
instant.
+      * @param  indeterminate  the indeterminate value, or {@code null} if the 
value is not indeterminate.
+      * @return the instant.
+      */
+     public static Instant createInstant(final Temporal position, final 
IndeterminateValue indeterminate) {
+         if (indeterminate == IndeterminateValue.UNKNOWN) {
+             return DefaultInstant.UNKNOWN;
+         }
+         if (indeterminate == null || indeterminate == 
IndeterminateValue.BEFORE || indeterminate == IndeterminateValue.AFTER) {
+             ArgumentChecks.ensureNonNull("position", position);
+         }
+         return new DefaultInstant(position, indeterminate);
+     }
+ 
+     /**
+      * Creates an instant for the given indeterminate value.
+      * The given value cannot be "before" or "after".
+      *
+      * @param  indeterminate  the indeterminate value.
+      * @return the instant for the given indeterminate value.
+      * @throws IllegalArgumentException if the given value is "before" or 
"after".
+      */
+     public static Instant createInstant(final IndeterminateValue 
indeterminate) {
+         ArgumentChecks.ensureNonNull("indeterminate", indeterminate);
+         if (indeterminate == IndeterminateValue.BEFORE || indeterminate == 
IndeterminateValue.AFTER) {
+             throw new 
IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, 
"indeterminate", indeterminate));
+         }
+         return (indeterminate == IndeterminateValue.UNKNOWN) ? 
DefaultInstant.UNKNOWN : new DefaultInstant(null, indeterminate);
+     }
+ 
+     /**
+      * Creates a period for the given begin and end instant.
+      *
+      * @param  beginning  the begin instant (inclusive), or {@code null}.
+      * @param  ending     the end instant (exclusive), or {@code null}.
+      * @return the period, or {@code null} if both arguments are null.
+      */
+     public static Period createPeriod(final Temporal beginning, final 
Temporal ending) {
+         if (beginning == null && ending == null) {
+             return null;
+         }
+         return new DefaultPeriod(createInstant(beginning), 
createInstant(ending));
+     }
+ 
+     /**
+      * Creates a period for the given begin and end instant.
+      *
+      * @param  beginning  the begin instant (inclusive), or {@code null}.
+      * @param  ending     the end instant (exclusive), or {@code null}.
+      * @return the period, or {@code null} if both arguments are null.
+      */
+     public static Period createPeriod(Instant beginning, Instant ending) {
+         if (beginning == null && ending == null) {
+             return null;
+         }
+         if (beginning == null) beginning = DefaultInstant.UNKNOWN;
+         if    (ending == null)    ending = DefaultInstant.UNKNOWN;
+         return new DefaultPeriod(beginning, ending);
+     }
+ 
+     /**
+      * Returns the given value as a temporal position, or {@code null} if not 
available.
+      *
+      * @param  time  the instant or period for which to get a date, or {@code 
null}.
+      * @return the temporal position, or {@code null} if indeterminate.
+      */
+     public static Temporal getInstant(final TemporalPrimitive time) {
+         if (time instanceof Instant) {
+             return ((Instant) time).getPosition();
+         }
+         return null;
+     }
+ 
+     /**
+      * Infers a value from the instant or extent as a {@link Date} object.
+      * This method is used for compatibility with legacy API and may 
disappear in future SIS version.
+      *
+      * @param  time  the instant or period for which to get a date, or {@code 
null}.
+      * @return the requested time as a Java date, or {@code null} if none.
+      */
+     public static Date getAnyDate(final TemporalPrimitive time) {
+         Temporal t = null;
+         if (time instanceof Instant) {
+             t = ((Instant) time).getPosition();
+         } else if (time instanceof Period) {
+             final var p = (Period) time;
+             Instant i = p.getEnding();      // Should never be null, but we 
are paranoiac.
+             if (i == null || (t = i.getPosition()) == null) {
+                 i = p.getBeginning();
+                 if (i != null) {
+                     t = i.getPosition();
+                 }
+             }
+         }
+         return TemporalDate.toDate(t);
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TM_Primitive.java
index d291a2adcf,4cd2880a96..cae2ac62a2
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TM_Primitive.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TM_Primitive.java
@@@ -23,11 -23,11 +23,11 @@@ import org.opengis.temporal.TemporalPri
  import org.apache.sis.xml.privy.XmlUtilities;
  import org.apache.sis.xml.bind.Context;
  import org.apache.sis.xml.bind.gco.PropertyType;
- import org.apache.sis.pending.temporal.TemporalUtilities;
+ import org.apache.sis.temporal.TemporalObjects;
  import org.apache.sis.util.resources.Errors;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Period;
 +// Specific to the main branch:
 +import org.apache.sis.pending.geoapi.temporal.Period;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimePeriodBound.java
index ab2ee6fb11,5b51345ec5..921666dc80
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimePeriodBound.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimePeriodBound.java
@@@ -23,6 -22,9 +22,9 @@@ import jakarta.xml.bind.annotation.XmlE
  import jakarta.xml.bind.annotation.XmlAttribute;
  import jakarta.xml.bind.annotation.XmlTransient;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.temporal.Instant;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.temporal.Instant;
+ 
  
  /**
   * The {@linkplain TimePeriod#begin begin} or {@linkplain TimePeriod#end end} 
position in
diff --cc 
endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultCitationDateTest.java
index c841f55160,55e52af59c..7c97bc963d
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultCitationDateTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/citation/DefaultCitationDateTest.java
@@@ -47,13 -47,14 +47,13 @@@ public final class DefaultCitationDateT
       */
      @Test
      public void testCopyConstructor() {
-         final CitationDate original = new CitationDate() {
-             @Override public Date     getDate()     {return new 
Date(1305716658508L);}
-             @Override public DateType getDateType() {return 
DateType.CREATION;}
+         final var original = new CitationDate() {
 -            @Override public Temporal getReferenceDate() {return 
Instant.ofEpochMilli(1305716658508L);}
++            @Override public Date     getDate()          {return new 
Date(1305716658508L);}
+             @Override public DateType getDateType()      {return 
DateType.CREATION;}
          };
-         final DefaultCitationDate copy = new DefaultCitationDate(original);
+         final var copy = new DefaultCitationDate(original);
          assertEquals(Instant.ofEpochMilli(1305716658508L), 
copy.getReferenceDate());
          assertEquals(DateType.CREATION, copy.getDateType());
-         assertFalse(copy.equals(original, ComparisonMode.STRICT)); // 
Opportunist test.
 -        assertTrue (copy.equals(original, ComparisonMode.DEBUG));
+         assertFalse(copy.equals(original, ComparisonMode.STRICT));          
// Opportunist test.
      }
  }
diff --cc 
endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/DefaultInstantTest.java
index 0000000000,403defe9f3..4607c106c1
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/DefaultInstantTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/DefaultInstantTest.java
@@@ -1,0 -1,215 +1,215 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.temporal;
+ 
+ import java.time.Period;
+ import java.time.Duration;
+ import java.time.LocalDate;
+ import java.time.LocalDateTime;
+ import java.time.ZonedDateTime;
+ import java.time.ZoneOffset;
+ import org.opengis.temporal.TemporalPrimitive;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.filter.TemporalOperatorName;
 -import org.opengis.temporal.IndeterminateValue;
 -import org.opengis.temporal.IndeterminatePositionException;
++// Specific to the main branch:
++import java.time.DateTimeException;
++import org.apache.sis.pending.geoapi.temporal.IndeterminateValue;
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
+ 
+ // Test dependencies
+ import org.junit.jupiter.api.Test;
+ import org.junit.jupiter.api.function.Executable;
+ import static org.junit.jupiter.api.Assertions.*;
+ import org.apache.sis.test.TestCase;
+ 
+ 
+ /**
+  * Tests the {@link DefaultInstant} class.
+  *
+  * @author  Mehdi Sidhoum (Geomatys)
+  * @author  Martin Desruisseaux (Geomatys)
+  */
+ public final class DefaultInstantTest extends TestCase {
+     /**
+      * Creates a new test case.
+      */
+     public DefaultInstantTest() {
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#getPosition()}.
+      * Opportunistically tests {@link 
TemporalObjects#createInstant(Temporal)} too.
+      */
+     @Test
+     public void testGetPosition() {
+         var date    = LocalDate.of(2010, 5, 1);
+         var instant = TemporalObjects.createInstant(date);
+         assertEquals(date, instant.getPosition());
+     }
+ 
+     /**
+      * Test of equals and hash code methods.
+      */
+     @Test
+     public void testEquals() {
+         var instant1 = new DefaultInstant(LocalDate.of(2000, 1, 1), null);
+         var instant2 = new DefaultInstant(LocalDate.of(1988, 1, 1), null);
+         var instant3 = new DefaultInstant(LocalDate.of(1988, 1, 1), null);
+ 
+         assertNotEquals(instant1, instant2);
+         assertNotEquals(instant1.hashCode(), instant2.hashCode());
+ 
+         assertEquals(instant3, instant2);
+         assertEquals(instant3.hashCode(), instant2.hashCode());
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#toString()}.
+      */
+     @Test
+     public void testToString() {
+         var date = LocalDate.of(2010, 5, 1);
+         assertEquals("2010-05-01", new DefaultInstant(date, null).toString());
+         assertEquals("after 2010-05-01", new DefaultInstant(date, 
IndeterminateValue.AFTER).toString());
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#findRelativePosition(TemporalPrimitive)} 
between instants.
+      */
+     @Test
+     public void testRelativePositionBetweenInstants() {
+         final var t1981 = new DefaultInstant(LocalDate.of(1981, 6, 5), null);
+         final var t2000 = new DefaultInstant(LocalDate.of(2000, 1, 1), null);
+         assertEquals(TemporalOperatorName.BEFORE, 
t1981.findRelativePosition(t2000));
+         assertEquals(TemporalOperatorName.AFTER,  
t2000.findRelativePosition(t1981));
+         assertEquals(TemporalOperatorName.EQUALS, 
t2000.findRelativePosition(t2000));
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#findRelativePosition(TemporalPrimitive)} 
between an instant and a period.
+      */
+     @Test
+     public void testRelativePositionBetweenInstantAndPeriod() {
+         final var before = new DefaultInstant(LocalDate.of(1981, 1, 1), null);
+         final var begins = new DefaultInstant(LocalDate.of(1981, 6, 5), null);
+         final var during = new DefaultInstant(LocalDate.of(1990, 1, 1), null);
+         final var ends   = new DefaultInstant(LocalDate.of(2000, 1, 1), null);
+         final var after  = new DefaultInstant(LocalDate.of(2000, 1, 2), null);
+         final var period = new DefaultPeriod(begins, ends);
+         assertEquals(TemporalOperatorName.BEFORE, 
before.findRelativePosition(period));
+         assertEquals(TemporalOperatorName.BEGINS, 
begins.findRelativePosition(period));
+         assertEquals(TemporalOperatorName.DURING, 
during.findRelativePosition(period));
+         assertEquals(TemporalOperatorName.ENDS,   ends  
.findRelativePosition(period));
+         assertEquals(TemporalOperatorName.AFTER,  after 
.findRelativePosition(period));
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#findRelativePosition(TemporalPrimitive)} 
with indeterminate instants.
+      * The position tested are "before" and "after".
+      */
+     @Test
+     public void testIndeterminatePosition() {
+         final var before2000 = new DefaultInstant(LocalDate.of(2000, 1, 1), 
IndeterminateValue.BEFORE);
+         final var  after2010 = new DefaultInstant(LocalDate.of(2010, 1, 1), 
IndeterminateValue.AFTER);
+         final var before2020 = new DefaultInstant(LocalDate.of(2020, 1, 1), 
IndeterminateValue.BEFORE);
+ 
+         assertEquals(TemporalOperatorName.BEFORE, 
before2000.findRelativePosition( after2010));
+         assertEquals(TemporalOperatorName.AFTER,   
after2010.findRelativePosition(before2000));
+         assertIndeterminate(() ->  
after2010.findRelativePosition(before2020));
+         assertIndeterminate(() -> 
before2000.findRelativePosition(before2020));
+         assertIndeterminate(() -> 
before2020.findRelativePosition(before2000));
+     }
+ 
+     /**
+      * Asserts that the result of the given comparison is indeterminate.
+      *
+      * @param  c  the comparison to perform.
+      */
+     private static void assertIndeterminate(final Executable c) {
 -        assertNotNull(assertThrows(IndeterminatePositionException.class, 
c).getMessage());
++        assertNotNull(assertThrows(DateTimeException.class, c).getMessage());
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#distance(TemporalPrimitive)} between two 
locale dates.
+      */
+     @Test
+     public void testDistanceBetweenLocalDates() {
+         final var t1981 = new DefaultInstant(LocalDate.of(1981, 6, 5), null);
+         final var t2000 = new DefaultInstant(LocalDate.of(2000, 8, 8), null);
+         final Period expected = Period.of(19, 2, 3);
+         assertEquals(expected,    t1981.distance(t2000));
+         assertEquals(expected,    t2000.distance(t1981));
+         assertEquals(Period.ZERO, t2000.distance(t2000));
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#distance(TemporalPrimitive)} between two 
dates with timezone.
+      */
+     @Test
+     public void testDistanceBetweenZonedDates() {
+         final var t2000 = new DefaultInstant(ZonedDateTime.of(2000, 6, 5, 12, 
4, 0, 0, ZoneOffset.UTC), null);
+         final var t2001 = new DefaultInstant(ZonedDateTime.of(2001, 6, 5, 14, 
4, 0, 0, ZoneOffset.UTC), null);
+         final Duration expected = Duration.ofDays(365).plusHours(2);
+         assertEquals(expected,      t2000.distance(t2001));
+         assertEquals(expected,      t2001.distance(t2000));
+         assertEquals(Duration.ZERO, t2000.distance(t2000));
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#distance(TemporalPrimitive)} between two 
dates with times.
+      * The period cannot be expressed with standard {@link java.time} objects.
+      */
+     @Test
+     public void testDistanceBetweenLocalDateTimes() {
+         final var t1 = new DefaultInstant(LocalDateTime.of(2000, 6, 5, 12, 4, 
0, 0), null);
+         final var t3 = new DefaultInstant(LocalDateTime.of(2001, 6, 9, 14, 4, 
0, 0), null);
+         final var t2 = new DefaultInstant(LocalDateTime.of(2001, 6, 9, 10, 4, 
0, 0), null);
+ 
+         Object expected = "P1Y4DT2H";
+         assertEquals(expected,    t1.distance(t3).toString());
+         assertEquals(expected,    t3.distance(t1).toString());
+         assertEquals(Period.ZERO, t1.distance(t1));
+ 
+         expected = "P1Y3DT22H";
+         assertEquals(expected,    t1.distance(t2).toString());
+         assertEquals(expected,    t2.distance(t1).toString());
+         assertEquals(Period.ZERO, t2.distance(t2));
+ 
+         expected = Duration.ofHours(4);
+         assertEquals(expected,    t2.distance(t3));
+         assertEquals(expected,    t3.distance(t2));
+         assertEquals(Period.ZERO, t3.distance(t3));
+     }
+ 
+     /**
+      * Tests {@link DefaultInstant#distance(TemporalPrimitive)} between an 
instant and a period.
+      */
+     @Test
+     public void testDistanceWithPeriod() {
+         final var before = new DefaultInstant(LocalDate.of(1981, 1, 1), null);
+         final var begins = new DefaultInstant(LocalDate.of(1981, 6, 5), null);
+         final var during = new DefaultInstant(LocalDate.of(1990, 1, 1), null);
+         final var ends   = new DefaultInstant(LocalDate.of(2000, 1, 1), null);
+         final var after  = new DefaultInstant(LocalDate.of(2000, 1, 2), null);
+         final var period = new DefaultPeriod(begins, ends);
+ 
+         assertEquals(Period.of(0, 5, 4), before.distance(period));
+         assertEquals(Period.ZERO,        begins.distance(period));
+         assertEquals(Duration.ZERO,      during.distance(period));      // 
`Duration` considered an implementation details.
+         assertEquals(Period.ZERO,        ends  .distance(period));
+         assertEquals(Period.of(0, 0, 1), after .distance(period));
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/DefaultPeriodTest.java
index 0000000000,9fbeb6c8f9..73829fe91b
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/DefaultPeriodTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/temporal/DefaultPeriodTest.java
@@@ -1,0 -1,128 +1,131 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.temporal;
+ 
+ import java.time.LocalDate;
+ import java.time.Period;
+ import java.time.Year;
+ import org.opengis.temporal.TemporalPrimitive;
+ 
+ // Test dependencies
+ import org.junit.jupiter.api.Test;
+ import static org.junit.jupiter.api.Assertions.*;
+ import org.apache.sis.test.TestCase;
 -import org.opengis.filter.TemporalOperatorName;
++
++// Specific to the main branch
++import org.apache.sis.pending.geoapi.temporal.TemporalOperatorName;
+ 
+ 
+ /**
+  * Tests the {@link DefaultPeriod} class.
+  *
+  * @author  Mehdi Sidhoum (Geomatys)
+  * @author  Martin Desruisseaux (Geomatys)
+  */
+ public final class DefaultPeriodTest extends TestCase {
+     /**
+      * Creates a new test case.
+      */
+     public DefaultPeriodTest() {
+     }
+ 
+     /**
+      * Tests {@link DefaultPeriod#getBeginning()} and {@link 
DefaultPeriod#getEnding()}.
+      * Opportunistically tests {@link TemporalObjects#createPeriod(Temporal, 
Temporal)} too.
+      */
+     @Test
+     public void testBounds() {
+         var beginning = LocalDate.of(2010, 5, 1);
+         var ending    = LocalDate.of(2015, 8, 6);
+         var period    = TemporalObjects.createPeriod(beginning, ending);
+         assertEquals(beginning, period.getBeginning().getPosition());
+         assertEquals(ending,    period.getEnding().getPosition());
+     }
+ 
+     /**
+      * Test of equals and hash code methods.
+      */
+     @Test
+     public void testEquals() {
+         var p1 = TemporalObjects.createPeriod(LocalDate.of(2000, 1, 1), 
LocalDate.of(2010, 1, 1));
+         var p2 = TemporalObjects.createPeriod(LocalDate.of(1988, 1, 1), 
LocalDate.of(2010, 1, 1));
+         var p3 = TemporalObjects.createPeriod(LocalDate.of(1988, 1, 1), 
LocalDate.of(2010, 1, 1));
+ 
+         assertNotEquals(p1, p2);
+         assertNotEquals(p1.hashCode(), p2.hashCode());
+ 
+         assertEquals(p3, p2);
+         assertEquals(p3.hashCode(), p2.hashCode());
+     }
+ 
+     /**
+      * Tests {@link DefaultPeriod#toString()}.
+      */
+     @Test
+     public void testToString() {
+         var p1 = TemporalObjects.createPeriod(LocalDate.of(2000, 1, 1), 
LocalDate.of(2010, 1, 1));
+         assertEquals("2000-01-01/2010-01-01", p1.toString());
+     }
+ 
+     /**
+      * Tests {@link DefaultPeriod#length()}.
+      */
+     @Test
+     public void testLength() {
+         var beginning = LocalDate.of(2010, 5, 1);
+         var ending    = LocalDate.of(2015, 8, 6);
+         var period    = TemporalObjects.createPeriod(beginning, ending);
+         assertEquals(Period.of(5, 3, 5), period.length());
+     }
+ 
+     /**
+      * Tests {@link DefaultPeriod#findRelativePosition(TemporalPrimitive)}.
+      */
+     @Test
+     public void testFindRelativePosition() {
+         var p04 = TemporalObjects.createPeriod(Year.of(2000), Year.of(2004));
+         var p56 = TemporalObjects.createPeriod(Year.of(2005), Year.of(2006));
+         var p13 = TemporalObjects.createPeriod(Year.of(2001), Year.of(2003));
+         var p14 = TemporalObjects.createPeriod(Year.of(2001), Year.of(2004));
+         assertRelativePositionEquals(TemporalOperatorName.EQUALS,    p04, 
p04);
+         assertRelativePositionEquals(TemporalOperatorName.BEFORE,    p04, 
p56);
+         assertRelativePositionEquals(TemporalOperatorName.CONTAINS,  p04, 
p13);
+         assertRelativePositionEquals(TemporalOperatorName.ENDED_BY,  p04, 
p14);
+         assertRelativePositionEquals(TemporalOperatorName.EQUALS,    p56, 
p56);
+         assertRelativePositionEquals(TemporalOperatorName.AFTER,     p56, 
p13);
+         assertRelativePositionEquals(TemporalOperatorName.AFTER,     p56, 
p14);
+         assertRelativePositionEquals(TemporalOperatorName.EQUALS,    p13, 
p13);
+         assertRelativePositionEquals(TemporalOperatorName.BEGINS,    p13, 
p14);
+         assertRelativePositionEquals(TemporalOperatorName.EQUALS,    p14, 
p14);
+     }
+ 
+     /**
+      * Finds the relative position of {@code p1} relative to {@code p2} and 
compare against the expected value.
+      * Then reverses argument order and test again.
+      *
+      * @param expected  the expected result.
+      * @param self      period for which to find the relative position.
+      * @param other     the period against which {@code self} is compared.
+      */
+     private static void assertRelativePositionEquals(TemporalOperatorName 
expected,
 -            org.opengis.temporal.Period self, org.opengis.temporal.Period 
other)
++            org.apache.sis.pending.geoapi.temporal.Period self,
++            org.apache.sis.pending.geoapi.temporal.Period other)
+     {
+         assertEquals(expected, self.findRelativePosition(other));
+         assertEquals(expected.reversed().orElseThrow(), 
other.findRelativePosition(self));
+     }
+ }
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index c88ab0f08e,433f389f3f..abaa640846
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@@ -87,11 -87,8 +87,11 @@@ import org.apache.sis.util.iso.Types
  // Specific to the main and geoapi-3.1 branches:
  import org.opengis.referencing.ReferenceIdentifier;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.ObjectDomain;
 +// Specific to the main branch:
 +import org.opengis.referencing.ReferenceSystem;
 +import org.apache.sis.referencing.internal.ServicesForMetadata;
- import org.apache.sis.util.privy.TemporalDate;
 +import org.apache.sis.referencing.factory.GeodeticObjectFactory;
++import org.apache.sis.temporal.TemporalDate;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultTemporalDatum.java
index 87d21d75f6,455ce924a3..2ad8984f4c
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultTemporalDatum.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultTemporalDatum.java
@@@ -35,10 -35,10 +35,10 @@@ import org.apache.sis.io.wkt.Formatter
  import org.apache.sis.io.wkt.FormattableObject;
  
  // Specific to the main and geoapi-3.1 branches:
- import org.apache.sis.util.privy.TemporalDate;
+ import org.apache.sis.temporal.TemporalDate;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.Identifier;
 +// Specific to the main branch:
 +import org.opengis.referencing.ReferenceIdentifier;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index 4cd1a9ec72,7bf491410a..a0cfacca43
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@@ -121,12 -121,9 +121,12 @@@ import static org.apache.sis.util.privy
  import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
  import static 
org.apache.sis.referencing.internal.ServicesForMetadata.CONNECTION;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.Identifier;
 -import org.opengis.referencing.ObjectDomain;
 +// Specific to the main branch:
++import org.apache.sis.temporal.TemporalDate;
 +import org.apache.sis.referencing.internal.ServicesForMetadata;
- import org.apache.sis.util.privy.TemporalDate;
 +import org.apache.sis.referencing.cs.DefaultParametricCS;
 +import org.apache.sis.referencing.datum.DefaultParametricDatum;
 +import org.apache.sis.referencing.factory.GeodeticObjectFactory;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
index 3826d39454,29b606fbce..e0023115c4
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
@@@ -71,90 -65,7 +71,98 @@@ public final class AxisDirections exten
       *
       * @see #isUserDefined(AxisDirection)
       */
 -    private static final int LAST_ORDINAL = UNSPECIFIED.ordinal();
 +    private static final int LAST_ORDINAL = DISPLAY_DOWN.ordinal();
 +
 +    /**
 +     * Forward direction.
 +     * For an observer at the centre of the object this is will be towards 
its front, bow or nose.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
 +    @UML(identifier="forward", obligation=CONDITIONAL, 
specification=ISO_19111)
 +    public static final AxisDirection FORWARD = 
AxisDirection.valueOf("FORWARD");
 +
 +    /**
 +     * Starboard direction.
 +     * For an observer at the centre of the object this will be towards its 
right.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
 +    @UML(identifier="starboard", obligation=CONDITIONAL, 
specification=ISO_19111)
 +    public static final AxisDirection STARBOARD = 
AxisDirection.valueOf("STARBOARD");
 +
 +    /**
 +     * Port direction.
 +     * For an observer at the centre of the object this will be towards its 
left.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
 +    @UML(identifier="port", obligation=CONDITIONAL, specification=ISO_19111)
 +    public static final AxisDirection PORT = AxisDirection.valueOf("PORT");
 +
 +    /**
 +     * Direction of geographic angles (bearing).
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
 +    @UML(identifier="clockwise", obligation=CONDITIONAL, 
specification=ISO_19111)
 +    public static final AxisDirection CLOCKWISE = 
AxisDirection.valueOf("CLOCKWISE");
 +
 +    /**
 +     * Direction of arithmetic angles. Used in polar coordinate systems.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
 +    @UML(identifier="counterClockwise", obligation=CONDITIONAL, 
specification=ISO_19111)
 +    public static final AxisDirection COUNTER_CLOCKWISE = 
AxisDirection.valueOf("COUNTER_CLOCKWISE");
 +
 +    /**
 +     * Distance from the origin in a polar coordinate system.
 +     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
 +     */
 +    @UML(identifier="awayFrom", obligation=CONDITIONAL, 
specification=ISO_19111)
 +    public static final AxisDirection AWAY_FROM = 
AxisDirection.valueOf("AWAY_FROM");
 +
++    /**
++     * Distance toward the origin.
++     * Added in ISO 19111:2019 (was not in ISO 19111:2007).
++     */
++    @UML(identifier="towards", obligation=CONDITIONAL, 
specification=ISO_19111)
++    public static final AxisDirection TOWARDS = 
AxisDirection.valueOf("TOWARDS");
++
 +    /**
 +     * Axis positive direction is unspecified.
 +     */
 +    @UML(identifier="unspecified", obligation=CONDITIONAL, 
specification=ISO_19111)
 +    public static final AxisDirection UNSPECIFIED = 
AxisDirection.valueOf("UNSPECIFIED");
 +
 +    /**
 +     * For each direction, the opposite direction.
 +     * This map shall be immutable after construction.
 +     */
 +    private static final Map<AxisDirection,AxisDirection> OPPOSITES = new 
HashMap<>(24);
 +    static {
 +        put(UNSPECIFIED,       UNSPECIFIED);
 +        put(NORTH,             SOUTH);
 +        put(NORTH_NORTH_EAST,  SOUTH_SOUTH_WEST);
 +        put(NORTH_EAST,        SOUTH_WEST);
 +        put(EAST_NORTH_EAST,   WEST_SOUTH_WEST);
 +        put(EAST,              WEST);
 +        put(EAST_SOUTH_EAST,   WEST_NORTH_WEST);
 +        put(SOUTH_EAST,        NORTH_WEST);
 +        put(SOUTH_SOUTH_EAST,  NORTH_NORTH_WEST);
 +        put(UP,                DOWN);
 +        put(FUTURE,            PAST);
 +        put(COLUMN_POSITIVE,   COLUMN_NEGATIVE);
 +        put(ROW_POSITIVE,      ROW_NEGATIVE);
 +        put(DISPLAY_RIGHT,     DISPLAY_LEFT);
 +        put(DISPLAY_UP,        DISPLAY_DOWN);
 +        put(COUNTER_CLOCKWISE, CLOCKWISE);
++        put(AWAY_FROM,         TOWARDS);
 +    }
 +
 +    /**
 +     * Stores the given directions in the {@link #OPPOSITES} array.
 +     */
 +    private static void put(final AxisDirection dir, final AxisDirection 
opposite) {
 +        OPPOSITES.put(dir, opposite);
 +        OPPOSITES.put(opposite, dir);
 +    }
  
      /**
       * Proposed abbreviations for some axis directions.
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
index 42c5ae51e6,778b40195b..fefbca7818
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/GeodeticObjectBuilder.java
@@@ -61,8 -61,8 +61,8 @@@ import org.apache.sis.referencing.cs.Ax
  import org.apache.sis.referencing.internal.Resources;
  import org.apache.sis.parameter.Parameters;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.ObjectDomain;
 +// Specific to the main branch:
- import org.apache.sis.util.privy.TemporalDate;
++import org.apache.sis.temporal.TemporalDate;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CommonCRSTest.java
index e312138ac2,296cb93297..1373e98bd9
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CommonCRSTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CommonCRSTest.java
@@@ -52,11 -52,11 +52,11 @@@ import static org.apache.sis.test.Asser
  import static org.apache.sis.test.TestUtilities.*;
  
  // Specific to the main and geoapi-3.1 branches:
- import org.apache.sis.util.privy.TemporalDate;
+ import org.apache.sis.temporal.TemporalDate;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.datum.RealizationMethod;
 -import static org.opengis.test.Assertions.assertAxisDirectionsEqual;
 +// Specific to the main branch:
 +import org.opengis.referencing.datum.VerticalDatumType;
 +import static org.apache.sis.test.GeoapiAssert.assertAxisDirectionsEqual;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
index adb23afaff,dc6845ddef..96a022b0fe
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/datum/DefaultTemporalDatumTest.java
@@@ -35,11 -35,12 +35,11 @@@ import static org.apache.sis.test.TestU
  import static org.apache.sis.test.TestUtilities.getScope;
  
  // Specific to the main and geoapi-3.1 branches:
- import org.apache.sis.util.privy.TemporalDate;
+ import org.apache.sis.temporal.TemporalDate;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import static org.opengis.referencing.ObjectDomain.*;
 -import static org.opengis.referencing.IdentifiedObject.*;
 -import static org.opengis.test.Assertions.assertIdentifierEquals;
 +// Specific to the main branch:
 +import static org.opengis.referencing.ReferenceSystem.*;
 +import static org.apache.sis.test.GeoapiAssert.assertIdentifierEquals;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java
index f50eb7f5f9,92f84c5ac2..29672463ff
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/privy/AxisDirectionsTest.java
@@@ -32,12 -32,6 +32,13 @@@ import org.apache.sis.referencing.cs.Ha
  import org.apache.sis.referencing.cs.HardCodedCS;
  import org.apache.sis.test.TestCase;
  
 +// Specific to the main branch:
++import static org.apache.sis.referencing.privy.AxisDirections.TOWARDS;
 +import static org.apache.sis.referencing.privy.AxisDirections.AWAY_FROM;
 +import static org.apache.sis.referencing.privy.AxisDirections.CLOCKWISE;
 +import static 
org.apache.sis.referencing.privy.AxisDirections.COUNTER_CLOCKWISE;
 +import static org.apache.sis.referencing.privy.AxisDirections.UNSPECIFIED;
 +
  
  /**
   * Tests the {@link AxisDirections} class.
diff --cc 
endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
index c39a324c08,e8cdfc64e5..9f3be45a8a
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/base/CRSBuilder.java
@@@ -59,9 -59,8 +59,9 @@@ import org.apache.sis.util.resources.Er
  import org.apache.sis.measure.NumberRange;
  import org.apache.sis.measure.Units;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.referencing.operation.CoordinateOperationFactory;
 +// Specific to the main branch:
++import org.apache.sis.temporal.TemporalDate;
 +import org.apache.sis.referencing.factory.GeodeticObjectFactory;
- import org.apache.sis.util.privy.TemporalDate;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/Copyright.java
index 7223b7a6f6,88bc82ca4e..0697471ebc
--- 
a/endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/Copyright.java
+++ 
b/endorsed/src/org.apache.sis.storage.xml/main/org/apache/sis/storage/gpx/Copyright.java
@@@ -39,16 -39,11 +39,16 @@@ import org.opengis.metadata.constraint.
  import org.opengis.util.InternationalString;
  import org.apache.sis.util.iso.Types;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.citation.Party;
 -import org.opengis.metadata.citation.Responsibility;
 -import org.opengis.metadata.identification.BrowseGraphic;
 -import org.apache.sis.util.SimpleInternationalString;
 +// Specific to the main branch:
 +import java.util.Date;
 +import org.opengis.metadata.citation.Contact;
 +import org.opengis.metadata.citation.Series;
 +import org.opengis.metadata.citation.ResponsibleParty;
- import org.apache.sis.util.privy.TemporalDate;
 +import org.apache.sis.metadata.iso.citation.AbstractParty;
 +import org.apache.sis.metadata.iso.citation.DefaultCitation;
 +import org.apache.sis.metadata.iso.citation.DefaultResponsibility;
 +import org.apache.sis.metadata.iso.constraint.DefaultConstraints;
++import org.apache.sis.temporal.TemporalDate;
  
  
  /**

Reply via email to