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)
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.
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
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.
My library Time4J (also runnable on Java-6 with version line v3.x):
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()));
Both ways produces digit representations based on zero digit ٠ (unicode point 0660). The year 2017 is displayed as: ٢٠١٧
Update:
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"));
}
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())...
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.