`DateTimeFormatter` 以小数秒处理句号或逗号

`DateTimeFormatter` to handle either a FULL STOP or COMMA in fractional second

java.time classes, how do we specify a formatting pattern for a DateTimeFormatter 中允许使用句号 (.)(句点或点)或逗号 (,) 作为小数秒的分隔符?

例如,以下内容用于解析 20090813145607.0Z20090813145607,0Z 中以 .0Z,0Z 结尾的输入日期值。

String input = "20090813145607.0Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );

但对于打印,输出包含两者,生成重复的小数秒对。

20090813145607,0.0Z

所以我推断我在格式化模式中使用 [,S][.S] 并不是获得此功能的正确方法。

DecimalStyle

我尝试使用 DateTimeFormatter::withDecimalStyle method passing a DecimalStyle,但它没有像我在实验中预期的那样运行。该文档并未真正解释其预期行为。

这个问题很重要,因为 ISO 8601 标准建议使用逗号但允许句号。两者都在实践中很常用。

Instant 不能容忍逗号

也许在 java.time 中不可能像 decimal mark 那样容忍逗号 and/or 点。

我用 Java 8 Update 102 尝试了以下代码。第一个带句号(点)的输入成功,而第二个带逗号的输入失败。

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 );
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);
    } 
} 

也许使用不同的 RuntimeException 子类,但这应该适合你。

(基于我的相关评论)

我不知道 "DateTimeFormatter" 是否允许这种 "or" 模式。

我能想到的两个解决方法是

  1. 使用多个格式化程序。例如。一个用于 ,,一个用于 .,如果第一个解析失败,则使用第二个。

    伪代码

    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();
    }
    
  2. "Normalize" 通过将倒数第 3 个字符替换为 . 输入 ,,您可以使用一个格式化程序。例如

    f = DateTimeFormatter.ofPattern( "uuuuMMddHHmmss.SX" )
    result = f.parse(input.matches(",..$")
                     ? input.replaceAll("^(.*),(..)$", "\1.\2")
                     : input);
    

关于点与逗号,根据我的经验,点比逗号更常见。 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)

鉴于所有这些,JSR-310 更喜欢一个点。

DecimalStyle class 确实提供了一些控制,其中 DateTimeFormatterBuilder.appendFraction 方法与 true 一起使用以从 DecimalStyle 输出小数点。

无法解析点或逗号。这被跟踪为 JDK-8132536,它解决了解析中的一般 "or" 概念。

Instants 使用 ISO_INSTANT 解析,表示 "The localized decimal style is not used".

因此,JSR-310 的格式化程序无法执行您想要的操作。