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

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 5f13a88877 Fix timezone issues: - Specify better how timezone is used 
in `WKTFormat(Locale, Timezone)` constructor. - Broken output in 
`StandardDateFormat.format(Date)` when the timezone is not UTC. - Command-line 
tool should not use local timezone unless explicitely requested.
5f13a88877 is described below

commit 5f13a88877da3843cf04a598343a9da6a05422ef
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Jan 29 18:37:55 2024 +0100

    Fix timezone issues:
    - Specify better how timezone is used in `WKTFormat(Locale, Timezone)` 
constructor.
    - Broken output in `StandardDateFormat.format(Date)` when the timezone is 
not UTC.
    - Command-line tool should not use local timezone unless explicitely 
requested.
---
 .../main/org/apache/sis/console/CommandRunner.java | 19 ++++-
 .../apache/sis/console/FormattedOutputCommand.java |  4 +-
 .../main/org/apache/sis/console/InfoCommand.java   |  4 +-
 .../main/org/apache/sis/io/wkt/WKTDictionary.java  |  2 +-
 .../main/org/apache/sis/io/wkt/WKTFormat.java      | 20 +++++-
 .../referencing/factory/sql/EPSGDataAccess.java    |  3 +-
 .../operation/AbstractCoordinateOperation.java     |  2 +-
 .../org/apache/sis/io/wkt/ComparisonWithEPSG.java  |  2 +-
 .../test/org/apache/sis/io/wkt/WKTFormatTest.java  | 20 +++---
 .../org/apache/sis/referencing/Assertions.java     |  2 +-
 .../operation/CoordinateOperationFinderTest.java   |  2 +-
 .../operation/CoordinateOperationRegistryTest.java |  2 +-
 .../DefaultCoordinateOperationFactoryTest.java     |  2 +-
 .../sis/test/integration/ConsistencyTest.java      | 10 +--
 .../sis/storage/sql/feature/InfoStatements.java    |  2 +-
 .../org/apache/sis/storage/base/URIDataStore.java  |  3 +-
 .../main/org/apache/sis/storage/wkt/Store.java     | 18 +----
 .../main/org/apache/sis/io/CompoundFormat.java     |  1 +
 .../sis/util/internal/StandardDateFormat.java      | 81 +++++++---------------
 .../sis/storage/shapefile/ShapefileStore.java      |  5 +-
 .../sis/gui/metadata/StandardMetadataTree.java     |  2 +-
 21 files changed, 92 insertions(+), 114 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
index 8b121d2371..355a5e1857 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
@@ -89,8 +89,12 @@ abstract class CommandRunner {
     protected final Locale locale;
 
     /**
-     * The locale specified by the {@code "--timezone"} option. If no such 
option was provided,
-     * then this field is set to the {@linkplain TimeZone#getDefault() default 
timezone}.
+     * The locale specified by the {@code "--timezone"} option, or null if no 
timezone was specified.
+     * The null value may be interpreted as the {{@linkplain 
TimeZone#getDefault() default timezone}
+     * or as UTC, depending on the context. For example, WKT parsing and 
formatting use UTC unless
+     * specified otherwise.
+     *
+     * @see #getTimeZone()
      */
     protected final TimeZone timezone;
 
@@ -220,7 +224,7 @@ abstract class CommandRunner {
             locale = (s != null) ? Locales.parse(s) : 
Locale.getDefault(Locale.Category.DISPLAY);
 
             value = s = getOptionAsString(option = Option.TIMEZONE);
-            timezone = (s != null) ? TimeZone.getTimeZone(s) : 
TimeZone.getDefault();
+            timezone = (s != null) ? TimeZone.getTimeZone(s) : null;
 
             value = s = getOptionAsString(option = Option.ENCODING);
             explicitEncoding = (s != null);
@@ -255,6 +259,15 @@ abstract class CommandRunner {
         }
     }
 
+    /**
+     * {@return a non-null timezone, either the specified timezone or the 
default one}.
+     * This method is invoked when a null {@link #timezone} would be 
interpreted as UTC,
+     * but the {@linkplain TimeZone#getDefault() default timezone} is 
preferred instead.
+     */
+    protected final TimeZone getTimeZone() {
+        return (timezone != null) ? timezone : TimeZone.getDefault();
+    }
+
     /**
      * Returns the value of the specified option as a character string.
      *
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
index 4bda2c2de6..ab86ed268f 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
@@ -18,9 +18,9 @@ package org.apache.sis.console;
 
 import java.util.Locale;
 import java.util.EnumSet;
+import java.util.function.Predicate;
 import java.io.Console;
 import java.io.IOException;
-import java.util.function.Predicate;
 import jakarta.xml.bind.Marshaller;
 import jakarta.xml.bind.JAXBException;
 import org.opengis.metadata.Metadata;
@@ -221,7 +221,7 @@ abstract class FormattedOutputCommand extends CommandRunner 
{
                 final TreeTable tree = 
MetadataStandard.ISO_19115.asTreeTable(object,
                         (object instanceof Metadata) ? Metadata.class : null,
                         ValueExistencePolicy.COMPACT);
-                final var tf = new TreeTableFormat(locale, timezone);
+                final var tf = new TreeTableFormat(locale, getTimeZone());
                 tf.setColumns(TableColumn.NAME, TableColumn.VALUE);
                 tf.setNodeFilter(getNodeFilter());
                 tf.format(tree, out);
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
index ce0314b417..a888d498b5 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
@@ -94,7 +94,7 @@ final class InfoCommand extends FormattedOutputCommand {
         } catch (BackingStoreException e) {
             throw e.unwrapOrRethrow(DataStoreException.class);
         }
-        final var tf = new TreeTableFormat(locale, timezone);
+        final var tf = new TreeTableFormat(locale, getTimeZone());
         tf.format(tree, out);
         return 0;
     }
@@ -133,7 +133,7 @@ final class InfoCommand extends FormattedOutputCommand {
                         final TableColumn<? super String> column)
     {
         target.setValue(column, 
Vocabulary.forLocale(locale).getString(Vocabulary.Keys.SampleDimensions));
-        final var rf = new RangeFormat(locale, timezone);
+        final var rf = new RangeFormat(locale, getTimeZone());
         final var sb = new StringBuffer();
         for (SampleDimension band : bands) {
             band = band.forConvertedValues(true);
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTDictionary.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTDictionary.java
index 1213f4451b..142be5aef6 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTDictionary.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTDictionary.java
@@ -414,7 +414,7 @@ public class WKTDictionary extends GeodeticAuthorityFactory 
{
         definitions = new HashMap<>();
         codeCaches  = new HashMap<>();
         codespaces  = new FrequencySortedSet<>(true);
-        parser      = new WKTFormat(null, null);
+        parser      = new WKTFormat();
         lock        = new ReentrantReadWriteLock();
         authorities = (authority != null) ? null : new 
FrequencySortedSet<>(true);
         this.authority = authority;
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
index ac1e9489ed..3ac3fde4dd 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
@@ -299,12 +299,28 @@ public class WKTFormat extends CompoundFormat<Object> {
      */
     private transient Warnings warnings;
 
+    /**
+     * Creates a format for the root locale and UTC timezone.
+     * This is the standard configuration for ISO 19162 Well-Known Text.
+     *
+     * @since 1.5
+     */
+    public WKTFormat() {
+        this(null, null);
+    }
+
     /**
      * Creates a format for the given locale and timezone. The given locale 
will be used for
-     * {@link InternationalString} localization; this is <strong>not</strong> 
the locale for number format.
+     * {@link InternationalString} localization, <strong>not</strong> for 
formatting numbers.
+     * The given timezone will be used for parsing and formatting dates in 
temporal elements
+     * such as {@code TimeOrigin[…]}. Note that the specified timezone will 
not be formatted
+     * in WKT elements, as it will be assumed implicit.
      *
      * @param  locale    the locale for the new {@code Format}, or {@code 
null} for {@code Locale.ROOT}.
-     * @param  timezone  the timezone, or {@code null} for UTC.
+     * @param  timezone  the timezone for dates in the WKT temporal elements, 
or {@code null} for UTC.
+     *
+     * @see #getLocale()
+     * @see #getTimeZone()
      */
     public WKTFormat(final Locale locale, final TimeZone timezone) {
         super(locale, timezone);
diff --git 
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
index 93763bd14f..d445a891a4 100644
--- 
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
@@ -1733,8 +1733,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                             throw new 
FactoryDataException(resources().getString(Resources.Keys.DatumOriginShallBeDate));
                         }
                         if (dateFormat == null) {
-                            dateFormat = new StandardDateFormat();
-                            dateFormat.setCalendar(getCalendar());          // 
Use UTC timezone.
+                            dateFormat = new StandardDateFormat();      // 
Default to UTC timezone.
                         }
                         try {
                             originDate = dateFormat.parse(anchor);
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 218d4ae696..b87ba7353b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@ -913,7 +913,7 @@ check:      for (int isTarget=0; ; isTarget++) {        // 
0 == source check; 1
      * To enabled this variant, {@link org.apache.sis.io.wkt.WKTFormat} can be 
configured as below:
      *
      * {@snippet lang="java" :
-     *     format = new WKTFormat(null, null);
+     *     format = new WKTFormat();
      *     format.setConvention(Convention.WKT1_IGNORE_AXES);
      *     format.setNameAuthority(Citations.ESRI);
      *     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
index dbd2152e25..9a8cfb3949 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ComparisonWithEPSG.java
@@ -181,7 +181,7 @@ public final class ComparisonWithEPSG extends TestCase {
         assumeTrue(factory != null);
         CoordinateOperation opFromCode = 
factory.createCoordinateOperation("5630");
         String wkt = opFromCode.toWKT();
-        WKTFormat parser = new WKTFormat(null, null);
+        WKTFormat parser = new WKTFormat();
         CoordinateOperation opFromWKT = (CoordinateOperation) 
parser.parseObject(wkt);
         assertEqualsIgnoreMetadata(opFromCode, opFromWKT);
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTFormatTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTFormatTest.java
index c998e75f7a..e368cbaf0f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTFormatTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/WKTFormatTest.java
@@ -77,7 +77,7 @@ public final class WKTFormatTest extends TestCase {
      */
     @Test
     public void testParse() throws ParseException {
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         final VerticalCRS crs = (VerticalCRS) format.parseObject(
                 "VERT_CS[“Gravity-related height”,\n" +
                 "  VERT_DATUM[“Mean Sea Level”, 2005],\n" +
@@ -96,7 +96,7 @@ public final class WKTFormatTest extends TestCase {
      */
     @Test
     public void testConsistencyOfWKT1() throws ParseException {
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.setConvention(Convention.WKT1);
         parser = format;
         testConsistency();
@@ -112,9 +112,9 @@ public final class WKTFormatTest extends TestCase {
     @Test
     @DependsOnMethod("testConsistencyOfWKT1")
     public void testConsistencyOfWKT1_WithCommonUnits() throws ParseException {
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.setConvention(Convention.WKT1_COMMON_UNITS);
-        parser = new WKTFormat(null, null);
+        parser = new WKTFormat();
         parser.setConvention(Convention.WKT1);
         testConsistency();
         testConsistencyWithDenormalizedBaseCRS();
@@ -129,7 +129,7 @@ public final class WKTFormatTest extends TestCase {
     @Test
     @DependsOnMethod("testConsistencyOfWKT1")
     public void testConsistencyOfWKT2() throws ParseException {
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.setConvention(Convention.WKT2);
         parser = format;
         testConsistency();
@@ -144,7 +144,7 @@ public final class WKTFormatTest extends TestCase {
     @Test
     @DependsOnMethod("testConsistencyOfWKT2")
     public void testConsistencyOfWKT2_Simplified() throws ParseException {
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.setConvention(Convention.WKT2_SIMPLIFIED);
         parser = format;
         testConsistency();
@@ -249,7 +249,7 @@ public final class WKTFormatTest extends TestCase {
     public void testConsistencyOfGeogTran() throws ParseException {
         final Symbols symbols = new Symbols(Symbols.SQUARE_BRACKETS);
         symbols.setPairedQuotes("“”", "\"\"");
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.setConvention(Convention.WKT1_IGNORE_AXES);
         format.setNameAuthority(Citations.ESRI);
         format.setSymbols(symbols);
@@ -299,7 +299,7 @@ public final class WKTFormatTest extends TestCase {
     public void testVariousConventions() throws ParseException {
         final Symbols symbols = new Symbols(Symbols.SQUARE_BRACKETS);
         symbols.setPairedQuotes("“”");
-        parser = format = new WKTFormat(null, null);
+        parser = format = new WKTFormat();
         format.setSymbols(symbols);
         final DefaultProjectedCRS crs = (DefaultProjectedCRS) 
parser.parseObject(
             "PROJCS[“OSGB 1936 / British National Grid”,\n" +
@@ -463,7 +463,7 @@ public final class WKTFormatTest extends TestCase {
      */
     @Test
     public void testFragments() throws ParseException {
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.addFragment("deg",    "UNIT[“degree”, 0.0174532925199433]");
         format.addFragment("Bessel", "SPHEROID[“Bessel 1841”, 6377397.155, 
299.1528128, AUTHORITY[“EPSG”,“7004”]]");
         format.addFragment("Tokyo",  "DATUM[“Tokyo”, $Bessel]");
@@ -493,7 +493,7 @@ public final class WKTFormatTest extends TestCase {
     public void testSourceFile() throws Exception {
         final var source  = new URI("test/wkt.prj");
         final var factory = new MathTransformFactoryMock("Mercator");
-        format = new WKTFormat(null, null);
+        format = new WKTFormat();
         format.setSourceFile(source);
         format.setFactory(MathTransformFactory.class, factory);
         final Parameterized mt = assertInstanceOf(Parameterized.class, 
format.parseObject(
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
index 052b56de5b..53e1b0f8ac 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/Assertions.java
@@ -65,7 +65,7 @@ public final class Assertions extends Static {
      * This formatter uses the {@code “…”} quotation marks instead of {@code 
"…"}
      * for easier readability of {@link String} constants in Java code.
      */
-    private static final WKTFormat WKT_FORMAT = new WKTFormat(null, null);
+    private static final WKTFormat WKT_FORMAT = new WKTFormat();
     static {
         final Symbols s = new Symbols(Symbols.SQUARE_BRACKETS);
         s.setPairedQuotes("“”", "\"\"");
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
index 7a850ddf1d..65f0756661 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java
@@ -130,7 +130,7 @@ public final class CoordinateOperationFinderTest extends 
MathTransformTestCase {
     @BeforeClass
     public static void createFactory() throws ParseException {
         factory = new DefaultCoordinateOperationFactory();
-        parser  = new WKTFormat(null, null);
+        parser  = new WKTFormat();
         /*
          * The first keyword in WKT below should be "GeodeticCRS" in WKT 2, 
but we use the WKT 1 keyword ("GEOGCS")
          * for allowing inclusion in ProjectedCRS.  SIS is okay with mixed WKT 
versions, but this is of course not
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
index 5b246296f2..20e103127a 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
@@ -127,7 +127,7 @@ public final class CoordinateOperationRegistryTest extends 
MathTransformTestCase
     @BeforeClass
     public static void createFactory() throws ParseException {
         factory = new DefaultCoordinateOperationFactory();
-        parser  = new WKTFormat(null, null);
+        parser  = new WKTFormat();
         parser.addFragment("NTF",
                 "Datum[“Nouvelle Triangulation Française (Paris)”,\n" +
                 "  Ellipsoid[“Clarke 1880 (IGN)”, 6378249.2, 
293.4660212936269]]");
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
index 185444a0f1..8152637613 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactoryTest.java
@@ -99,7 +99,7 @@ public final class DefaultCoordinateOperationFactoryTest 
extends MathTransformTe
     @BeforeClass
     public static void createFactory() throws ParseException {
         factory = new DefaultCoordinateOperationFactory();
-        parser  = new WKTFormat(null, null);
+        parser  = new WKTFormat();
         parser.addFragment("NTF",
                 "ProjectedCRS[“NTF (Paris) / Lambert zone II”,\n" +
                 "  BaseGeodCRS[“NTF (Paris)”,\n" +
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
index 2ae585cbd4..e766cba968 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
@@ -102,7 +102,7 @@ public final class ConsistencyTest extends TestCase {
     public void debug() throws FactoryException {
         final String code = "EPSG::29871";
         final CoordinateReferenceSystem crs = CRS.forCode(code);
-        final WKTFormat format = new WKTFormat(null, null);
+        final WKTFormat format = new WKTFormat();
         format.setConvention(Convention.WKT2);
         lookup(parseAndFormat(format, code, crs), crs);
     }
@@ -115,10 +115,10 @@ public final class ConsistencyTest extends TestCase {
     @Test
     public void testCoordinateReferenceSystems() throws FactoryException {
         assumeTrue("Extensive tests not enabled.", RUN_EXTENSIVE_TESTS);
-        final WKTFormat v1  = new WKTFormat(null, null);
-        final WKTFormat v1c = new WKTFormat(null, null);
-        final WKTFormat v2  = new WKTFormat(null, null);
-        final WKTFormat v2s = new WKTFormat(null, null);
+        final WKTFormat v1  = new WKTFormat();
+        final WKTFormat v1c = new WKTFormat();
+        final WKTFormat v2  = new WKTFormat();
+        final WKTFormat v2s = new WKTFormat();
         v1 .setConvention(Convention.WKT1);
         v1c.setConvention(Convention.WKT1_COMMON_UNITS);
         v2 .setConvention(Convention.WKT2);
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
index 93c1459240..882f5b8f1f 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
@@ -547,7 +547,7 @@ public class InfoStatements implements Localized, 
AutoCloseable {
      */
     private WKTFormat wktReader() {
         if (wktReader == null) {
-            wktReader = new WKTFormat(null, null);
+            wktReader = new WKTFormat();
             wktReader.setConvention(Convention.WKT1_COMMON_UNITS);
         }
         return wktReader;
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
index 5a43496c70..25d3f42255 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
@@ -96,7 +96,8 @@ public abstract class URIDataStore extends DataStore 
implements StoreResource, R
 
     /**
      * User-specified locale for textual content, or {@code null} for {@link 
Locale#ROOT} (usually English).
-     * This locale is usually <strong>not</strong> used for parsing numbers or 
dates.
+     * This locale is usually for {@link org.opengis.util.InternationalString} 
localization rather than for
+     * parsing numbers or dates, but the exact interpretation is at subclasses 
choice.
      * Subclasses may replace this value by a value read from the data file.
      */
     protected Locale dataLocale;
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
index c3ea161c3c..7ecb16f8b1 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
@@ -19,8 +19,6 @@ package org.apache.sis.storage.wkt;
 import java.util.List;
 import java.util.Arrays;
 import java.util.ArrayList;
-import java.util.Locale;
-import java.util.TimeZone;
 import java.io.Reader;
 import java.io.IOException;
 import java.text.ParsePosition;
@@ -61,18 +59,6 @@ final class Store extends URIDataStore {
      */
     private volatile Reader source;
 
-    /**
-     * The locale for {@link org.opengis.util.InternationalString} localization
-     * or {@code null} for {@link Locale#ROOT} (usually English).
-     * This locale is <strong>not</strong> used for parsing numbers or dates.
-     */
-    private final Locale locale;
-
-    /**
-     * Timezone for dates, or {@code null} for UTC.
-     */
-    private final TimeZone timezone;
-
     /**
      * The geometry library, or {@code null} for the default.
      */
@@ -99,8 +85,6 @@ final class Store extends URIDataStore {
     public Store(final StoreProvider provider, final StorageConnector 
connector) throws DataStoreException {
         super(provider, connector);
         objects  = new ArrayList<>();
-        locale   = connector.getOption(OptionKey.LOCALE);       // For 
`InternationalString`, not for numbers.
-        timezone = connector.getOption(OptionKey.TIMEZONE);
         library  = connector.getOption(OptionKey.GEOMETRY_LIBRARY);
         source   = connector.commit(Reader.class, StoreProvider.NAME);
         listeners.useReadOnlyEvents();
@@ -140,7 +124,7 @@ final class Store extends URIDataStore {
              * definitions.
              */
             final ParsePosition pos = new ParsePosition(0);
-            final StoreFormat parser = new StoreFormat(locale, timezone, 
library, listeners);
+            final StoreFormat parser = new StoreFormat(dataLocale, timezone, 
library, listeners);
             do {
                 final Object obj = parser.parse(wkt, pos);
                 objects.add(obj);
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java
index 2f30c62fb5..b9231e2f89 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java
@@ -475,6 +475,7 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
          * documented in this method javadoc. But actually it is not, since 
the call to
          * DefaultFormat.getInstance(…) will indirectly perform this kind of 
comparison.
          */
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
         final Locale locale = getLocale(Locale.Category.FORMAT);
         if (Number.class.isAssignableFrom(valueType)) {
             if (Locale.ROOT.equals(locale)) {
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/StandardDateFormat.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/StandardDateFormat.java
index b101103cf2..5164fac26c 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/StandardDateFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/StandardDateFormat.java
@@ -51,10 +51,6 @@ import org.apache.sis.util.CharSequences;
  * the time is optional. For this class, "Standard" is interpreted as "close 
to ISO 19162 requirements",
  * which is not necessarily identical to other ISO standards.
  *
- * <p>External users should use nothing else than the parsing and formatting 
methods.
- * The methods for configuring the {@code DateFormat} instances may or may not 
work
- * depending on the branch.</p>
- *
  * <p>The main usage for this class is Well Known Text (WKT) parsing and 
formatting.
  * ISO 19162 uses ISO 8601:2004 for the dates. Any precision is allowed: the 
date could have only the year,
  * or only the year and month, <i>etc</i>. The clock part is optional and also 
have optional fields: can be
@@ -99,7 +95,7 @@ public final class StandardDateFormat extends DateFormat {
             
.optionalStart().appendLiteral(':').appendValue(ChronoField.SECOND_OF_MINUTE, 2)
                                                
.appendFraction(ChronoField.MILLI_OF_SECOND, 3, 3, true)
             .optionalEnd().optionalEnd().optionalEnd()    // Move back to the 
optional block of HOUR_OF_DAY.
-            .optionalStart().appendZoneOrOffsetId()
+            .optionalStart().optionalStart().appendLiteral(' 
').optionalEnd().appendZoneOrOffsetId()
             .toFormatter(Locale.ROOT);
 
     /**
@@ -245,40 +241,6 @@ replace:    if (Character.isWhitespace(c)) {
      */
     public static final int NANOS_PER_SECOND = 1000_000_000;
 
-    /**
-     * Converts the given legacy {@code Date} object into a {@code java.time} 
implementation in given timezone.
-     * The method performs the following choice:
-     *
-     * <ul>
-     *   <li>If the given date has zero values in hours, minutes, seconds and 
milliseconds fields in UTC timezone,
-     *       then the returned implementation will be a {@link LocalDate}, 
dropping the timezone information (i.e.
-     *       the date is considered an approximation). Note that this is 
consistent with ISO 19162 requirement that
-     *       dates are always in UTC, even if Apache SIS allows some 
flexibility.</li>
-     *   <li>Otherwise if the timezone is not {@code null} and not UTC, then 
this method returns an {@link OffsetDateTime}.</li>
-     *   <li>Otherwise this method returns a {@link LocalDateTime} in the 
given timezone.</li>
-     * </ul>
-     *
-     * @param  date  the date to convert, or {@code null}.
-     * @param  zone  the timezone of the temporal object to obtain, or {@code 
null} for UTC.
-     * @return the temporal object for the given date, or {@code null} if the 
given argument was null.
-     */
-    public static Temporal toHeuristicTemporal(final Date date, ZoneId zone) {
-        if (date == null) {
-            return null;
-        }
-        final long time = date.getTime();
-        if ((time % MILLISECONDS_PER_DAY) == 0) {
-            return LocalDate.ofEpochDay(time / MILLISECONDS_PER_DAY);
-        }
-        final Instant instant = Instant.ofEpochMilli(time);
-        if (zone == null) {
-            zone = ZoneOffset.UTC;
-        } else if (!zone.equals(ZoneOffset.UTC)) {
-            return OffsetDateTime.ofInstant(instant, zone);
-        }
-        return LocalDateTime.ofInstant(instant, zone);
-    }
-
     /**
      * Converts the given temporal object into a date.
      * The given temporal object is typically the value parsed by {@link 
#FORMAT}.
@@ -347,11 +309,18 @@ replace:    if (Character.isWhitespace(c)) {
      */
     private DateTimeFormatter format;
 
+    /**
+     * The formatter without timezone. This is usually the same object as 
{@link #format},
+     * unless a timezone has been set in which case this field keep the 
original formatter.
+     * This is used for formatting local dates without the formatter timezone.
+     */
+    private final DateTimeFormatter formatWithoutZone;
+
     /**
      * Creates a new format for a default locale in the UTC timezone.
      */
     public StandardDateFormat() {
-        format = FORMAT;
+        formatWithoutZone = format = FORMAT;
     }
 
     /**
@@ -360,7 +329,8 @@ replace:    if (Character.isWhitespace(c)) {
      * @param locale  the locale of the format to create.
      */
     public StandardDateFormat(final Locale locale) {
-        format = FORMAT.withLocale(locale);             // Same instance as 
FORMAT if the locales are equal.
+        // Same instance as FORMAT if the locales are equal.
+        formatWithoutZone = format = FORMAT.withLocale(locale);
     }
 
     /**
@@ -449,8 +419,9 @@ replace:    if (Character.isWhitespace(c)) {
     }
 
     /**
-     * Formats the given date. If hours, minutes, seconds and milliseconds are 
zero and the timezone is UTC,
-     * then this method omits the clock part (unless the user has overridden 
the pattern).
+     * Formats the given date. If hours, minutes, seconds and milliseconds are 
zero in the timezone of this formatter,
+     * then this method omits the clock part. The timezone is always omitted 
(ISO 19162 does not include timezone in
+     * WKT elements because all dates are required to be in UTC).
      *
      * @param  date        the date to format.
      * @param  toAppendTo  where to format the date.
@@ -459,7 +430,16 @@ replace:    if (Character.isWhitespace(c)) {
      */
     @Override
     public StringBuffer format(final Date date, final StringBuffer toAppendTo, 
final FieldPosition pos) {
-        format.formatTo(toHeuristicTemporal(date, null), toAppendTo);
+        ZoneId zone = format.getZone();
+        if (zone == null) {
+            zone = ZoneOffset.UTC;
+        }
+        final LocalDateTime dt = LocalDateTime.ofInstant(date.toInstant(), 
zone);
+        TemporalAccessor value = dt;
+        if (dt.getHour() == 0 && dt.getMinute() == 0 && dt.getSecond() == 0 && 
dt.getNano() == 0) {
+            value = dt.toLocalDate();
+        }
+        formatWithoutZone.formatTo(value, toAppendTo);
         return toAppendTo;
     }
 
@@ -530,17 +510,4 @@ replace:    if (Character.isWhitespace(c)) {
     public boolean equals(final Object obj) {
         return (obj instanceof StandardDateFormat) && 
format.equals(((StandardDateFormat) obj).format);
     }
-
-    /**
-     * Returns a clone of this format.
-     *
-     * @return a clone of this format.
-     */
-    @Override
-    @SuppressWarnings("CloneDoesntCallSuperClone")
-    public Object clone() {
-        final StandardDateFormat clone = new StandardDateFormat();
-        clone.format = format;
-        return clone;
-    }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
index b3db8bf79b..8220ba595e 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
@@ -19,7 +19,6 @@ package org.apache.sis.storage.shapefile;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.math.BigInteger;
 import java.nio.channels.SeekableByteChannel;
 import java.nio.channels.WritableByteChannel;
 import java.nio.charset.Charset;
@@ -32,14 +31,12 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.OptionalLong;
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
-import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.function.Consumer;
@@ -738,7 +735,7 @@ public final class ShapefileStore extends DataStore 
implements WritableFeatureSe
 
                 //write prj
                 try {
-                    final WKTFormat format = new WKTFormat(Locale.ENGLISH, 
null);
+                    final WKTFormat format = new WKTFormat();
                     format.setConvention(Convention.WKT1_COMMON_UNITS);
                     format.setNameAuthority(Citations.ESRI);
                     format.setIndentation(WKTFormat.SINGLE_LINE);
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
index e057893d7c..b40e00a40e 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
@@ -190,7 +190,7 @@ public class StandardMetadataTree extends MetadataTree {
                     final String text;
                     try {
                         if (source == copyAsWKT) {                             
 // Well Known Text.
-                            final WKTFormat f = new WKTFormat(null, null);
+                            final WKTFormat f = new WKTFormat();
                             text = f.format(obj);
                         } else if (source == copyAsXML) {                      
 // GML or ISO 19115-3:2016.
                             text = XML.marshal(obj);


Reply via email to