java-如何自定义Joda时间日期格式的数字格式?
作者:互联网
我想格式化日期,同时格式化dozenal中日期中的数字.
使用较旧的Java日期格式API,我可以执行以下操作:
format = new SimpleDateFormat(pattern, new DateFormatSymbolsAdapter(locale)) {{
numberFormat = new DozenalNumberFormat();
}};
不幸的是,SimpleDateFormat内部有一些愚蠢的代码,出于我的目的,这些代码分配对象的速度太快了,因为我经常格式化值. Joda Time可能没有这个问题(到目前为止,他们的其他课程似乎还不错),所以我试图进行切换.
但是,对于Joda Time的日期格式设置类,还不清楚如何实现.许多API都以某种方式被锁定,这使它很难进入和执行我想要的操作:
> DateTimeFormatterBuilder方法没有给我指定方法的方法
> DateTimeFormatterBuilder没有我可以看到的任意附加
> DateTimeFormatter似乎没有明显的拦截点
>所有有用的东西似乎都被锁定在InternalPrinter及其实现中,它们是包私有的
>追加DateTimeFormatter,DateTimePrinter之类的方法似乎需要大量框架才能实现自定义实现,而我尚未实现实现.经过调查,似乎是a bug in Joda Time.
当然,一定有某种方法可以做到这一点.有人有什么主意吗?我认为也许有人必须这样做才能使阿拉伯日期格式在过去也能正常工作,正如我隐约记得Joda 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数据(version v30.02)告诉我们,阿拉伯语使用代号为“ arab”(xml-tag defaultNumberingSystem)的替代编号系统.但是,JDK可能并不总是最新的.通常,JDK依赖于旧的CLDR版本.但是即使到那时,据我所记得,旧的CLDR版本也没有使用ASCII数字表示阿拉伯语.
结论:您不应将Joda-Time用于繁重的i18n工作(其中许多其他细节之一,例如固定的星期开始时间,等等,在该库中,它是非常糟糕的).如果您仍然坚持使用Joda-Time,则可以尝试编写自己的自定义DateTimePrinter.但是,这并不是很有趣,因为您在Joda-issue中也注意到了这一点(并且在进行可能的修复后仍然会很有趣,因为它太笨拙了).
因此,让我们看一下更好的选择.
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更好,但仍然存在一些怪癖.正确且只有中途灵活的解决方案使用了DecimalStyle类.
我的库Time4J(也可以在Java-6版本版本v3.x上运行):
我已经编写了另一种格式和解析引擎,该引擎还可以处理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年显示为:٢٠١٧
更新:
您的最后一条评论清楚地表明,您主要关注如何实现批量编号系统(positional number system for base 12).好吧,对于Java-8,没有足够的灵活性(没有像Joda-Time那样的DateTimePrinter-interface,也没有比DecimalStyle更加灵活的钩子,DecimalStyle仅允许设置零个十进制数字,而十进制不是十进制).为了填补空白(并且工作量不那么大),我决定在最新版本v3.27(或Java-8平台上的v4.23)中的implement集成系统.对于Android,我刚刚发布了Time4A-v3.27-2016j.用法示例和最终解决方案:
@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选择稍作更改的代码,以与遗留代码(例如表达式)互操作.
ChronoFormatter.setUp(
Moment.axis(TemporalType.JAVA_UTIL_DATE), Locale.getDefault())...
备注:此解决方案还尽最大努力避免在打印数字时避免多余的数组分配(例如,在许多情况下甚至避免Integer.toString()),尤其是当您将StringBuilder用作方法“ ChronoFormatter.formatToBuffer()”的第二个参数时. .到目前为止,与其他库相比,总体性能方面的工作异常高.
标签:jodatime,datetime-format,java 来源: https://codeday.me/bug/20191111/2023078.html