如何自定义Joda时间日期格式的数字格式? [英] How can I customise the number format for Joda Time date formatting?

查看:86
本文介绍了如何自定义Joda时间日期格式的数字格式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to format dates, while formatting the numbers in the date in dozenal.

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

With the older Java date formatting API, I am able to do this:

    format = new SimpleDateFormat(pattern, new DateFormatSymbolsAdapter(locale)) {{
        numberFormat = new DozenalNumberFormat();
    }};

不幸的是, SimpleDateFormat 内部有一些愚蠢的代码,出于我的目的,该代码分配对象的速度太快了,因为我经常格式化值.Joda Time可能没有这个问题(到目前为止,其他课程似乎还不错),所以我尝试进行切换.

Unfortunately, SimpleDateFormat internally has some dumb code which is allocating objects too rapidly for my purposes, as I'm formatting values rather frequently. Joda Time might not have this problem (their other classes seem to be fine so far), so I'm attempting to switch.

但是,对于Joda Time的日期格式设置类,尚不完全清楚我该怎么做.许多API的锁定方式使其难以进入和执行我想要的操作:

However, with Joda Time's date formatting classes, it isn't entirely clear how I can do it. A lot of the API is locked down in a way which makes it hard to get in and do what I want:

  • DateTimeFormatterBuilder 方法没有给我指定方法的方法
  • DateTimeFormatterBuilder 没有我可以看到的任意附加内容
  • DateTimeFormatter 似乎没有明显的拦截点
  • 所有有用的东西似乎都被锁定在 InternalPrinter 及其实现(包私有)中
  • 追加 DateTimeFormatter DateTimePrinter 等的方法似乎需要很多框架才能进行自定义实现,而我尚未使实现生效.经过一番调查,看来这是 Joda Time中的错误./li>
  • DateTimeFormatterBuilder methods don't give me a way to specify it
  • DateTimeFormatterBuilder has no arbitrary append that I can see
  • DateTimeFormatter doesn't seem to have any obvious intercept points
  • all the useful stuff seems to be locked inside InternalPrinter and its implementations, which are package private
  • Methods to append DateTimeFormatter, DateTimePrinter and the like appear to require a lot of framework just to make a custom implementation, and I haven't yet made my implementation work. After some investigation, it seems like this is a bug in Joda Time.

当然,必须有某种方法可以做到这一点.有人有什么主意吗?我认为也许有人必须这样做才能使阿拉伯日期格式在过去也能正常工作,正如我模糊地回忆起Joda Time遇到的问题,但这也许是过去的事情.我可能是第一个尝试这样做的人,因为我希望数字使用不同的数字基数...

Surely there has to be some way to do it though. Does anyone have any idea? I think that perhaps someone has had to do it to get Arabic date formatting to work correctly in the past as well, as I vaguely recall Joda Time having problems with that, but maybe that is in the past. I might be the first one trying to do it because I want a different number base for the numbers...

推荐答案

Joda-Time很明显无法打印除ASCII数字0-9之外的其他数字.Joda文档,甚至是 DateTimeUtils FormatUtils 类.并且将语言环境设置为强制使用替代编号系统的语言环境也无济于事.

Joda-Time is obviously not capable of printing other digits than ASCII-digits 0-9. I have investigated all relevant parts of Joda-documentation, even the classes DateTimeUtils and FormatUtils. And setting the locale to a locale which mandates the usage of an alternative numbering system does not help, too.

String s = DateTimeFormat.forPattern("yyyy-MM-dd").withLocale(new Locale("ar"))
  .print(System.currentTimeMillis()));
// output: 2017-01-02 (still ASCII)

最新的CLDR数据(版本v30.02 )告诉我们,阿拉伯语使用代号为"arab"(xml-tag defaultNumberingSystem )的替代编号系统.但是,JDK可能并不总是最新的.JDK通常依赖于旧的CLDR版本.但是据我所知,即使那样,旧的CLDR版本也没有使用ASCII数字表示阿拉伯语.

The newest CLDR-data (version v30.02) tell us that Arabic uses the alternative numbering system with code name "arab" (xml-tag defaultNumberingSystem). However, the JDK might not be always up-to-date. Often the JDK relies on an old CLDR-version. But even then, as far as I remember, also old CLDR-versions didn't use ASCII-digits for Arabic.

结论:您不应该将Joda-Time用于认真的i18n工作(许多其他细节之一,例如固定的工作周开始等,在这个库中,它是非常糟糕的).如果您仍然坚持使用Joda-Time,则可以尝试编写自己的自定义 DateTimePrinter .但这并不有趣,因为您在 Joda-issue (并且在进行可能的修复后仍然不会很有趣,因为它太笨拙了.

Conclusion: You should not use Joda-Time for serious i18n-work (one among many other details like fixed start of week etc. where this library is notoriously bad). If you still insist on using Joda-Time then you can go the hard way to write your own customized DateTimePrinter. But this is not fun as you have also noticed in your Joda-issue (and will still be no fun after possible fix because it is sooo awkward).

所以让我们看一下更好的选择.

So let's look at better alternatives.

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更好,但仍然存在一些古怪之处.正确且只有中途灵活的解决方案可以使用类

So we see that using the standard on Java-8 is better than Joda-Time but still not without quirks. The correct and only half-way-flexible solution makes usage of class DecimalStyle.

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

My library Time4J (also runnable on Java-6 with version line v3.x):

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

I have written an alternative format and parse engine which can also process Java-8-types like LocalDate, Instant etc. Time4J has its own repository for localized resources independent from JDK and actually uses CLDR-version v30.0.2. Showing two ways, either a generic way by locale or using hardwired assumption about numbering system:

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年显示为:٢٠١٧

Both ways produces digit representations based on zero digit ٠ (unicode point 0660). The year 2017 is displayed as: ٢٠١٧

您最近的评论清楚表明,您主要关注如何实现十二种编号系统(实现最新版本v3.27(或Java-8平台上的v4.23)中的十几个系统.对于Android,我刚刚发布了 Time4A-v3.27-2016j .用法示例和最终解决方案:

Your last comments made clear that you are mainly concerned about how to realize the dozenal numbering system (positional number system for base 12). Well, with Java-8, there is not enough flexibility (no DateTimePrinter-interface like in Joda-Time, also no more flexible hook than DecimalStyle which only allows to set the zero decimal digit while dozenals are not decimal). In order to fill the gap (and it was not so much work), I have decided to implement the dozenal system within the newest version v3.27 (or v4.23 on Java-8-platforms). For Android, I have just now released Time4A-v3.27-2016j. Example of usage and final solution:

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

如果您使用的是Android,则还可以考虑使用旧类型的 java.util.Date 选择稍作更改的代码,以实现与遗留代码(例如表达式)的互操作性

If you are working on Android then you might also consider to choose a slightly changed code using the old type java.util.Date for interoperability with legacy code, for example with the expression

ChronoFormatter.setUp(
  Moment.axis(TemporalType.JAVA_UTIL_DATE), Locale.getDefault())...

备注:此解决方案还尽最大努力避免在打印数字时避免额外的数组分配(例如,在许多情况下甚至避免使用 Integer.toString()),尤其是在使用 StringBuilder的情况下作为方法"ChronoFormatter.formatToBuffer()"的第二个参数.到目前为止,与其他库相比,目前在整体性能方面的投入异常高.

Remark: This solution also makes best efforts to avoid extra array allocation when printing numbers (for example even avoiding Integer.toString() in many cases), especially if you use a StringBuilder as second parameter to the method ´ChronoFormatter.formatToBuffer()`. The overall performance effort so far done is unusally high compared with other libraries.

这篇关于如何自定义Joda时间日期格式的数字格式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆