问题
In the java.time classes, how do we specify a formatting pattern for a DateTimeFormatter that allows for either a FULL STOP (.
) (period or dot) or a COMMA (,
) as the delimiter of a fractional second?
For example, the following works for parsing an input date value ending in either .0Z
or ,0Z
in 20090813145607.0Z
and 20090813145607,0Z
.
String input = "20090813145607.0Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );
But for printing, the output contains both, generating a repeated pair of fractional seconds.
20090813145607,0.0Z
So I deduce I my use of [,S][.S]
in the formatting pattern is not the correct way to get this functionality.
DecimalStyle
I tried to use the DateTimeFormatter::withDecimalStyle method passing a DecimalStyle, but that failed to behave as I expected in my experiments. The documentation does not really explain its intended behavior.
This issue is important as the ISO 8601 standard recommends using the COMMA but allows the FULL STOP. Either is commonly used in practice.
Instant
cannot tolerate a comma
Perhaps this tolerance of comma and/or dot as decimal mark is not possible in java.time.
I tried the following code with Java 8 Update 102. The first input with a FULL STOP (dot) succeeds while the second input with a COMMA fails.
String inputDot = "2016-01-02T12:34:56.7Z"; // Succeeds.
Instant instantDot = Instant.parse ( inputDot );
String inputComma = "2016-01-02T12:34:56,7Z"; // Fails. Throws a DateTimeParseException.
Instant instantComma = Instant.parse ( inputComma );
回答1:
On dot vs comma, dots are far more common than commas in my experience. RFC3339 uses a dot only as does XML schema. The dot is no longer "preferred" (according to wikipedia):
A decimal mark, either a comma or a dot (without any preference as stated in resolution 10 of the 22nd General Conference CGPM in 2003,[16] but with a preference for a comma according to ISO 8601:2004)
Given all this, JSR-310 prefers a dot.
The DecimalStyle
class does provide some control, where the DateTimeFormatterBuilder.appendFraction method is used with true
to output the decimal point from DecimalStyle
.
To parse either a dot or comma is not possible. This is tracked as JDK-8132536, which tackles a general "or" concept in parsing.
Instants are parsed using ISO_INSTANT which says that "The localized decimal style is not used".
Thus, the formatters of JSR-310 cannot do what you want.
回答2:
String fmt1 = "uuuuMMddHHmmss,SX";
String fmt2 = "uuuuMMddHHmmss.SX";
DateTimeFormatter f = DateTimeFormatter.ofPattern(fmt1);
TemporalAccessor dateObject = null;
try {
dateObject = f.parse("20090813145607.0Z");
} catch (DateTimeParseException e) {
f = DateTimeFormatter.ofPattern(fmt2);
try {
dateObject = f.parse(input);
} catch (DateTimeParseException e1) {
throw new IllegalArgumentException("invalid format "+input);
}
}
Perhaps use a different RuntimeException subclass, but that should sort you.
回答3:
(Based on my comment in question)
I dbout if "DateTimeFormatter" allow such kind of "or" pattern.
Two workarounds I can think of are
make use multiple formatters. E.g. one for
,
and one for.
, if first one failed to parse, use the second one.pseudo-code
List<DateTimeFormatter> formatters = Arrays.asList( DateTimeFormatter.ofPattern( "uuuuMMddHHmmss.SX" ), DateTimeFormatter.ofPattern( "uuuuMMddHHmmss,SX" )); TemporalAccessor result = null; for (DateTimeFormatter f: formatters) { try { result = f.parse(input); break; } catch(DateTimeParseException ignored) { } } if (result == null) { throw new WhateverException(); }
"Normalize" the input by replacing the 3rd-last char with
.
if it is a,
, and you can use one single formatter. E.g.f = DateTimeFormatter.ofPattern( "uuuuMMddHHmmss.SX" ) result = f.parse(input.matches(",..$") ? input.replaceAll("^(.*),(..)$", "\\1.\\2") : input);
来源:https://stackoverflow.com/questions/38886005/datetimeformatter-to-handle-either-a-full-stop-or-comma-in-fractional-second