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 <[email protected]>
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);