DateTimeFormatter月份模式字母“L”失败

时间:2015-05-28 23:54:09

标签: java date java-8 java-time

我注意到java.time.format.DateTimeFormatter无法按预期解析。见下文:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Play {
  public static void tryParse(String d,String f) {
    try { 
      LocalDate.parse(d, DateTimeFormatter.ofPattern(f)); 
      System.out.println("Pass");
    } catch (Exception x) {System.out.println("Fail");}
  }
  public static void main(String[] args) {
    tryParse("26-may-2015","dd-L-yyyy");
    tryParse("26-May-2015","dd-L-yyyy");
    tryParse("26-may-2015","dd-LLL-yyyy");
    tryParse("26-May-2015","dd-LLL-yyyy");
    tryParse("26-may-2015","dd-M-yyyy");
    tryParse("26-May-2015","dd-M-yyyy");
    tryParse("26-may-2015","dd-MMM-yyyy");
    tryParse("26-May-2015","dd-MMM-yyyy");
  }
}

只有tryParse("26-May-2015","dd-MMM-yyyy");的最后一次尝试才会“通过”。根据文档LLL应该能够解析文本格式。另请注意大写“M”与小写“m”的细微差别。

这真的很烦人,因为我无法默认解析Oracle DB默认格式化的字符串

SELECT TO_DATE(SYSDATE,'DD-MON-YYYY') AS dt FROM DUAL;

同样,对于以下程序:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Play {
  public static void output(String f) {
    LocalDate d = LocalDate.now();
    Locale l = Locale.US;
    // Locale l = Locale.forLanguageTag("ru");
    System.out.println(d.format(DateTimeFormatter.ofPattern(f,l)));
  }
  public static void main(String[] args) {
    output("dd-L-yyyy");
    output("dd-LLL-yyyy");
    output("dd-M-yyyy");
    output("dd-MMM-yyyy");
  }
}

我得到以下输出:

28-5-2015
28-5-2015
28-5-2015
28-May-2015

显然,L格式说明符不会对文本进行任何处理,对我来说似乎是数字...

但是,如果我将区域设置更改为Locale.forLanguageTag("ru"),我会得到以下输出:

28-5-2015
28-Май-2015
28-5-2015
28-мая-2015

一切都很有意思,你不同意吗?

我的问题是:

  • 我认为每个人应该工作是否合理?
  • 我们至少应该提交其中一些作为错误吗?
  • 我是否误解了L模式说明符的用法。

从我认为“重要”的文档中引用一部分:

  

文字:文字样式是根据图案的数量确定的   使用的字母。少于4个模式字母将使用简短形式。   正好4个模式字母将使用完整形式。完全是5种模式   字母将使用缩小的形式。 模式字母'L','c'和'q'   指定文本样式的独立形式

     

数字:如果字母数为1,则使用输出值   最小位数和无填充。否则,算了   数字用作输出字段的宽度,带有值   必要时填零。以下模式字母有   对字母数量的限制。只有一个'c'和'F'字母   可以指定。最多两个字母'd','H','h','K','k','m',   并且's'可以指定。最多可以指定三个字母'D'。

     

数字/文字: 如果图案字母数为3或更大,请使用   上面的文字规则。否则请使用上面的数字规则。

更新

我向Oracle提交了两份提交内容:

  • 请求LLL(长格式文本)问题的修正:JDK-8114833(原始oracle评论ID:JI-9021661)
  • 请求增强小写月份解析问题:审核ID:0(这也是一个错误吗?)

3 个答案:

答案 0 :(得分:17)

“独立”月份名称

我相信' L'适用于本月使用不同单词的语言与日期中使用的语言。例如:

Locale russian = Locale.forLanguageTag("ru");

asList("MMMM", "LLLL").forEach(ptrn -> 
    System.out.println(ptrn + ": " + ofPattern(ptrn, russian).format(Month.MARCH))
);

输出:

MMMM: марта
LLLL: Март

不应该有任何理由使用' L'而不是' M'在解析日期时。

我尝试了以下操作来查看哪些语言环境支持独立月份名称格式:

Arrays.stream(Locale.getAvailableLocales())
    .collect(partitioningBy(
                loc -> "3".equals(Month.MARCH.getDisplayName(FULL_STANDALONE, loc)),
                mapping(Locale::getDisplayLanguage, toCollection(TreeSet::new))
    )).entrySet().forEach(System.out::println);

以下语言从' LLLL'获得特定于语言环境的独立月份名称:

加泰罗尼亚语,中文,克罗地亚语,捷克语,芬兰语,希腊语,匈牙利语,意大利语,立陶宛语,挪威语,波兰语,罗马尼亚语,俄语,斯洛伐克语,土耳其语,乌克兰语

所有其他语言得到" 3"作为March的独立名称。

答案 1 :(得分:4)

根据javadocs:

  

模式字母' L',' c'和' q'指定文本样式的独立形式。

然而,我无法找到关于#34;独立"形式应该是。在查看代码时,我看到使用' L'根据那个javadoc选择TextStyle.SHORT_STANDALONE

  

独立使用的简短文本,通常是缩写。例如,星期一星期一可能输出"星期一"。

然而,这并不是它的工作方式。即使有三个字母,我也可以从这段代码中获得数字输出:

DateTimeFormatter pattern = DateTimeFormatter.ofPattern ("dd-LLL-yyyy");
System.out.println (pattern.format (LocalDate.now ()));

修改

经过进一步调查,似乎(尽可能接近我所知道的)#34;独立"这些代码的版本适用于您希望加载自己的与语言环境无关的数据,可能是使用DateTimeFormatterBuilder。因此,默认情况下DateTimeFormatter没有为TextStyle.SHORT_STANDALONE加载条目。

答案 2 :(得分:1)

虽然其他答案在模式字母L和日期解析方面提供了很好的信息,但我想补充一点,您应该完全避免这个问题。不要从数据库中获取日期(和时间)作为字符串。而是使用适当的datetime对象。

    String sql = "select sysdate as dt from dual;"
    PreparedStatement stmt = yourDatabaseConnection.prepareStatement(sql);
    ResultSet rs = stmt.executeQuery();
    if (rs.next()) {
        LocalDateTime dateTime = rs.getObject("dt", LocalDateTime.class);
        // do something with dateTime
    }

(未经测试,因为我手头没有Oracle数据库。请原谅任何错字。)