Java 如何自定义Joda时间日期格式的数字格式?

Java 如何自定义Joda时间日期格式的数字格式?,java,jodatime,datetime-format,Java,Jodatime,Datetime Format,我想格式化日期,同时格式化中日期中的数字 使用较旧的Java日期格式化API,我可以做到: format = new SimpleDateFormat(pattern, new DateFormatSymbolsAdapter(locale)) {{ numberFormat = new DozenalNumberFormat(); }}; 不幸的是,SimpleDateFormat内部有一些愚蠢的代码,因为我经常格式化值,所以分配对象的速度太快。Joda Ti

我想格式化日期,同时格式化中日期中的数字

使用较旧的Java日期格式化API,我可以做到:

    format = new SimpleDateFormat(pattern, new DateFormatSymbolsAdapter(locale)) {{
        numberFormat = new DozenalNumberFormat();
    }};
不幸的是,
SimpleDateFormat
内部有一些愚蠢的代码,因为我经常格式化值,所以分配对象的速度太快。Joda Time可能没有这个问题(到目前为止,他们的其他课程似乎还不错),所以我正在尝试切换

然而,对于Joda Time的日期格式类,我还不完全清楚如何做到这一点。很多API都以某种方式被锁定,很难进入并执行我想要的操作:

  • DateTimeFormatterBuilder
    方法没有给我指定它的方法
  • DateTimeFormatterBuilder
    没有我可以看到的任意附加
  • DateTimeFormatter
    似乎没有任何明显的截取点
  • 所有有用的东西似乎都被锁在
    InternalPrinter
    及其实现中,它们是包私有的
  • 附加
    DateTimeFormatter
    DateTimePrinter
    等的方法似乎需要大量的框架才能进行定制实现,而我的实现还没有成功。经过一番调查,这似乎是一件好事

当然,一定有办法做到这一点。有人知道吗?我想,也许过去也有人不得不这样做,以使阿拉伯语日期格式正确工作,因为我隐约记得Joda Time在这方面有问题,但也许那是在过去。我可能是第一个尝试这样做的人,因为我想要一个不同的数字基数…

你的问题一点也不清楚,因为你没有具体说明你想做什么,也没有确切说明什么在阻止你。但我会提供一些信息

仅供参考,该项目目前正在进行中,团队建议迁移到类

使用java.time java.time和Joda时间都使用。这意味着你有内在的能力。因此,您可以缓存格式化程序对象并重新使用它们。无需重复实例化

在java.time中,该类可以为您自动本地化。人工语言和格式都可以从计算机自动分配。看见我建议在这些自动本地化格式足够时使用它们

捕获UTC中的当前时刻。调整到一个时区。请注意,时区与区域设置无关。您可能希望在格式化演示文稿时查看中的

生成用于向用户演示的字符串

Locale l = new Locale ( "ar" , "MA" ); // Arabic language, cultural norms of Morocco.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.FULL ).withLocale ( l );
保留对
f
的引用以缓存该对象。无需再次实例化

转储到控制台

String output = zdt.format ( f );
System.out.println ( "zdt.toString(): " + zdt );
System.out.println ( "output: " + output );
zdt.toString():2017-01-01T15:06:34.255-05:00[美国/蒙特利尔]

输出:2017年美国东部时间01:06:34

我不确定阿拉伯文是否能在这里正确复制粘贴。看


关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

该项目现已启动,建议迁移到类

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

从哪里获得java.time类

  • 后来
    • 内置的
    • 标准JavaAPI的一部分,带有捆绑实现
    • Java9添加了一些次要功能和修复
    • 大部分java.time功能都在中向后移植到Java6和Java7
    • 该项目专门为Android采用了ThreeTen Backport(如上所述)

该项目使用其他类扩展了java.time。这个项目是java.time将来可能添加的一个试验场。您可以在这里找到一些有用的类,如、、和。

Joda Time显然无法打印ASCII数字0-9以外的其他数字。我已经调查了Joda文档的所有相关部分,甚至包括类
DateTimeUtils
FormatUtils
。将区域设置为强制使用替代编号系统的区域设置也没有帮助

String s = DateTimeFormat.forPattern("yyyy-MM-dd").withLocale(new Locale("ar"))
  .print(System.currentTimeMillis()));
// output: 2017-01-02 (still ASCII)
最新的CLDR数据()告诉我们,阿拉伯语使用代号为“arab”的替代编号系统(xml标记
defaultNumberingSystem
)。然而,JDK可能并不总是最新的。JDK通常依赖于旧的CLDR版本。但即便如此,据我记忆所及,旧的CLDR版本也没有使用ASCII数字表示阿拉伯语

结论:您不应该将Joda时间用于严肃的i18n工作(这是许多其他细节中的一个,比如固定的每周开始时间等,其中这个库是出了名的糟糕)。如果您仍然坚持使用Joda Time,那么您可以不遗余力地编写自己定制的
DateTimePrinter
。但这并不有趣,正如您在您的应用程序中所注意到的(并且在可能的修复之后仍然没有乐趣,因为它太尴尬了)

让我们看看更好的选择


Java-8

    Locale loc = new Locale("ar");
    System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withDecimalStyle(DecimalStyle.of(loc))
    .format(LocalDate.now()));
    // output: 2017-01-02 (obviously my JDK uses wrong or outdated data)

    System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withDecimalStyle(DecimalStyle.STANDARD.withZeroDigit('\u0660'))
    .format(LocalDate.now()));
    // correct output with hardwired numbering system
因此,我们看到在Java-8上使用该标准比Joda Time更好,但仍然有一些怪癖。正确且唯一灵活的解决方案是使用类

我的库(在版本行为v3.x的Java-6上也可以运行):

我已经编写了一个替代格式和解析引擎,它还可以处理Java-8类型,如
LocalDate
Instant
等。Time4J有自己的本地化资源存储库,独立于JDK,并且实际使用CLDR v30.0.2版。显示两种方式,一种是按区域设置的通用方式,另一种是使用有关编号系统的硬连线假设:

System.out.println(
    ChronoFormatter.ofPattern(
        "yyyy-MM-dd",
        PatternType.CLDR,
        new Locale("ar"),
        PlainDate.axis(TemporalType.LOCAL_DATE)
    ).format(LocalDate.now()));

System.out.println(
    ChronoFormatter.ofPattern(
        "yyyy-MM-dd",
        PatternType.CLDR,
        Locale.ROOT,
        PlainDate.axis(TemporalType.LOCAL_DATE)
    )
    .with(Attributes.NUMBER_SYSTEM, NumberSystem.ARABIC_INDIC)
    .format(LocalDate.now()));
这两种方法都基于零位٠(unicode点0660)生成数字表示。2017年显示为:٢٠١٧


更新:

System.out.println( ChronoFormatter.ofPattern( "yyyy-MM-dd", PatternType.CLDR, new Locale("ar"), PlainDate.axis(TemporalType.LOCAL_DATE) ).format(LocalDate.now())); System.out.println( ChronoFormatter.ofPattern( "yyyy-MM-dd", PatternType.CLDR, Locale.ROOT, PlainDate.axis(TemporalType.LOCAL_DATE) ) .with(Attributes.NUMBER_SYSTEM, NumberSystem.ARABIC_INDIC) .format(LocalDate.now()));

@Test
public void printDate() {
    ChronoFormatter<PlainDate> f =
        ChronoFormatter.setUp(PlainDate.axis(), Locale.ROOT)
            .addFixedInteger(PlainDate.YEAR, 4)
            .addLiteral('-')
            .padNext(2)
            .addInteger(PlainDate.MONTH_AS_NUMBER, 1, 2)
            .addLiteral('-')
            .padNext(2)
            .addInteger(PlainDate.DAY_OF_MONTH, 1, 2)
            .build()
            .with(Attributes.NUMBER_SYSTEM, NumberSystem.DOZENAL)
            .with(Attributes.PAD_CHAR, '0');
    assertThat(
        f.format(PlainDate.of(2017, 10, 11)),
        is("1201-0\u218A-0\u218B"));
}
ChronoFormatter.setUp(
  Moment.axis(TemporalType.JAVA_UTIL_DATE), Locale.getDefault())...