【问题标题】:SimpleDateFormat Illegal pattern character error with localized patternSimpleDateFormat 具有本地化模式的非法模式字符错误
【发布时间】:2016-06-20 11:25:43
【问题描述】:

我正在尝试理解一些 SimpleDateFormat 代码。特别是我试图在 SimpleDateFormat 中使用本地化模式字符串。来自javadoc

SimpleDateFormat 还支持本地化的日期和时间模式字符串。在这些字符串中,上述模式字母可以替换为其他依赖于语言环境的模式字母。

它还指定了一个SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) 构造函数:

使用给定的模式和日期格式符号构造一个 SimpleDateFormat。

然而,尽管getLocalPatternChars() 实例呈现预期的模式字符,SimpleDateFormat 的构造函数拒绝包含这些字符的模式:

public void run() {
    Locale loc = new Locale("de", "de");
    DateFormatSymbols dfs = new DateFormatSymbols(loc);
    String sym = dfs.getLocalPatternChars();
    System.out.println(sym);
    SimpleDateFormat datefmt = new SimpleDateFormat("tt.MM.uuuu", dfs);
}

产生输出:

GuMtkHmsSEDFwWahKzZ
Exception in thread "main" java.lang.IllegalArgumentException: Illegal pattern character 't'
    at java.text.SimpleDateFormat.compile(SimpleDateFormat.java:845)
    ...

如果我将最后一行替换为“... new SimpleDateFormat("tt.MM.uuuu", loc);”,我会得到相同的输出。

另一方面,如果我使用任何 Anglicized 模式字符串创建 SimpleDateFormat 实例,然后调用“applyLocalizedPattern("tt.MM.uuuu")”,则接受本地化模式。

所以看起来不能在 SimpleDateFormat 的构造函数中使用本地化的模式字符串,并且需要这个两步初始化。这是故意行为吗?

【问题讨论】:

  • 构造函数没有调用translatePatternapplyLocalizedPattern 这样做),所以这要么是一个错误,要么是一个错误的JavaDoc,它没有清楚地解释构造函数的用法。
  • @Tom 是对的。构造函数的模式参数仅引用未本地化的日期时间模式字符。

标签: java localization simpledateformat


【解决方案1】:

不幸的是,关于如何处理本地化模式的文档非常糟糕。所以我研究了源代码并进行了自己的调查。结果:

接受模式字符串的SimpleDateFormat 的构造函数仅引用未本地化的模式字符,其定义在SimpleDateFormat 类的javadoc 标头中给出。这些未本地化的模式字符也在DateTimeFormatSymbols 中定义为常量:

/**
 * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
 * All locales use the same these unlocalized pattern characters.
 */
static final String  patternChars = "GyMdkHmsSEDFwWahKzZYuXL";

要使用本地化模式需要三个步骤(例如您认为是德语的“tt.MM.uuuu” - 但不是德语,而应该是“TT.MM. JJJJ" - 错误 JDK 资源的示例):

  1. 通过DateFormatSymbols.setLocalPatternChars(...) 定义本地化模式字符。
  2. 在您的SimpleDateFormat-object 上使用自定义的日期格式符号。
  3. 通过SimpleDateFormat.applyLocalizedPattern(...) 应用本地化的日期时间模式

然后本地化的模式将被翻译成内部和官方的模式字符定义。

使用示例(使用正确的德语模式 TT.MM.JJJJ):

SimpleDateFormat sdf = new SimpleDateFormat(); // uses default locale (here for Germany)
System.out.println(sdf.toPattern()); // dd.MM.yy HH:mm
System.out.println(sdf.toLocalizedPattern()); // tt.MM.uu HH:mm

DateFormatSymbols dfs = DateFormatSymbols.getInstance(Locale.GERMANY);
dfs.setLocalPatternChars("GJMTkHmsSEDFwWahKzZYuXL");
sdf.setDateFormatSymbols(dfs);
sdf.applyLocalizedPattern("TT.MM.JJJJ");

System.out.println(sdf.toPattern()); // dd.MM.yyyy
System.out.println(sdf.toLocalizedPattern()); // TT.MM.JJJJ
System.out.println(sdf.format(new Date())); // 20.06.2016

旁注:我已将字符串“GyMdkHmsSEDFwWahKzZYuXL”中的相应模式字符 y 和 d 更改为 J 和 T,以进行本地化定义。

遗憾的是JDK资源显然不可靠,所以我个人的看法是,整个功能只能以一种尴尬的方式使用,在实践中并不是很有用。

【讨论】:

  • 我还在 CLDR 数据中搜索了这些本地化的日期时间模式字符,例如“GyMdkHmsSEDFwWahKzZYuXL”,但尚未找到。所以我不清楚 Sun/Oracle 是如何获得这些数据的。是他们自己的猜测吗?也许有人更了解本地化数据的来源?
  • 啊,好吧,旧版本的 CLDR-data 知道元素 。它已被弃用并且不再填充(在 CLDR v29 中)。所以JDK似乎使用旧数据。另请参阅ICU-ticket
【解决方案2】:

虽然我同意@MenoHochschild 对问题的分析,但提出的解决方案似乎比必要的复杂。 SimpleDateFormat 上有一个名为 applyLocalizedPattern 的方法,我相信它会达到相同的结果。

String localizedPattern = ... // whatever localized pattern you have e.g. TT.MM.JJJJ
Locale locale = ... // whatever locale you are expecting the localized symbols to be, e.g. GERMAN

// Start with empty pattern (always valid)
SimpleDateFormat df = new SimpleDateFormat("", locale);

// Set the localized pattern (not possible via constructor)
df.applyLocalizedPattern(localizedPattern);

// Now, do whatever you want with that DateFormat object
String canonicalPattern = df.toPattern();

只要localizedPatternlocale 彼此一致,这种技术就应该适用于这个用例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 2017-11-19
    • 2013-05-14
    相关资源
    最近更新 更多