我想格式化日期,同时格式化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有这个问题,但也许这就是过去。我可能是第一个尝试这样做的人,因为我想要一个不同的数字基数......
答案 0 :(得分:2)
Joda-Time显然无法打印除ASCII数字0-9之外的其他数字。我已经调查了Joda文档的所有相关部分,甚至是类DateTimeUtils
和{ {1}}。将语言环境设置为强制使用替代编号系统的语言环境也无济于事。
FormatUtils
最新的CLDR数据(version v30.02)告诉我们,阿拉伯语使用代号为“arab”(xml-tag String s = DateTimeFormat.forPattern("yyyy-MM-dd").withLocale(new Locale("ar"))
.print(System.currentTimeMillis()));
// output: 2017-01-02 (still ASCII)
)的替代编号系统。但是,JDK可能并不总是最新的。 JDK通常依赖于旧的CLDR版本。但即便如此,据我所知,旧的CLDR版本也没有使用阿拉伯语的ASCII数字。
结论:你不应该使用Joda-Time来进行严肃的i18n工作(在其他许多细节中,例如固定的周开始等等,其中这个库非常糟糕)。如果您仍然坚持使用Joda-Time,那么您可以通过艰难的方式编写自己的自定义defaultNumberingSystem
。但这并不好玩,因为你在Joda-issue中也注意到了(并且在可能的修复之后仍然没有乐趣,因为它太笨拙了。)
让我们看看更好的选择。
<强>的Java-8 强>
DateTimePrinter
所以我们看到在Java-8上使用标准比Joda-Time更好但仍然没有怪癖。正确且唯一中途灵活的解决方案是使用类DecimalStyle。
我的库 Time4J (也可以在版本为v3.x的Java-6上运行):
我编写了一个替代格式和解析引擎,它也可以处理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
,LocalDate
等.Time4J有自己的独立于JDK的本地化资源库,实际上使用了CLDR -version v30.0.2。显示两种方式,通过区域设置的通用方式或使用关于编号系统的硬连线假设:
Instant
两种方式都基于零位0(unicode点0660)产生数字表示。 2017年显示为:2017
您的上一条评论明确表示您主要关注如何实现 dozenal编号系统(positional number system for base 12)。好吧,使用Java-8,没有足够的灵活性(没有{JAME-Time中的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()));
- 接口,也没有比DateTimePrinter
更灵活的钩子,只允许设置零十进制数字而dozen是不是小数)。为了填补空白(并没有那么多工作),我决定implement最新版本v3.27(或Java-8平台上的v4.23)中的doalal系统。对于Android,我刚刚发布了Time4A-v3.27-2016j。使用示例和最终解决方案:
DecimalStyle
如果您使用的是Android,那么您可能还会考虑使用旧类型@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"));
}
选择略有更改的代码,以便与旧代码进行互操作,例如使用表达式
java.util.Date
备注:此解决方案还尽最大努力在打印数字时避免额外的数组分配(例如,在许多情况下甚至避免使用ChronoFormatter.setUp(
Moment.axis(TemporalType.JAVA_UTIL_DATE), Locale.getDefault())...
),尤其是当您使用Integer.toString()
作为方法的第二个参数时'ChronoFormatter.formatToBuffer()`。到目前为止,总体性能工作与其他库相比非常高。
答案 1 :(得分:1)
你的问题根本不清楚,因为你没有真正指明你想要做什么,也没有确切地说是什么阻止了你。但我会提供一些信息。
仅供参考,Joda-Time项目现在位于maintenance mode,团队建议迁移到java.time课程。
java.time和Joda-Time都使用immutable objects。这意味着您可以内置thread-safety。因此,您可以缓存格式化程序对象并重新使用它们。无需重复实例化。
在java.time中,java.time.format.DateTimeFormatter
类可以自动为您进行本地化。可以从Locale
自动分配人类语言和格式。见this list of supported locales in Java 8。我建议在它们足够时使用这些自动本地化格式。
以UTC格式捕获当前时刻。调整为时区。请注意,时区的 nothing 与区域设置有关。在格式化为wall-clock time时,您可能希望在Québec中看到Moroccan user reading Arabic。
Instant instant = Instant.now ();
ZonedDateTime zdt = instant.atZone ( ZoneId.of ( "America/Montreal" ) );
生成一个String以便呈现给用户。
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 [美国/蒙特利尔]
输出:01يناير,2017 EST 03:06:34م
我不确定阿拉伯语是否会在此处正确复制粘贴。见live code in IdeOne.com。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和&amp; SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。