Localized date-time formats #253
Replies: 8 comments 25 replies
-
Many thanks for opening this 🙏 . As far as I'm concerned, I'm mostly interested in the About the API, I kind of like the Java A few comments:
I'm not sure what // declaration
fun LocalDateTime.buildLocalized(block: (FormatContext) -> Unit)
// usage
val displayed: String = localDateTime.buildLocalized { /* ... */ } Maybe even make the locale mandatory? fun LocalDateTime.buildLocalized(locale: Locale, block: (FormatContext) -> Unit)
I'd be ok with removing the val displayed: String = localDateTime.buildLocalized(FR_BE) {
day()
month()
// ...
} Also, swift has an "easy" API with just localDateTime.formatted(locale = EN_US, date = Complete, time = Omitted) // Sunday, January 17, 2021
localDateTime.formatted(locale = EN_US, date = Omitted, time = Complete) // 4:03:12 p.m. CST
localDateTime.formatted(locale = EN_US, date = Long, time = Shortened) // January 17, 2021, 4:03 PM
// etc... In all cases, looking forward to this 😃 ! |
Beta Was this translation helpful? Give feedback.
-
What we regularly use and is problematic (in our Android world) are custom date-time skeletons. The problematic part is that joda/java time depends on the platform support/implementation of Unicode. We ended up copying over the patterns for specific skeletons to workaround old Android issues. (E.g.
This is an important aspect of the API, until now, the majority of Java-world APIs (I'd say) prefer passing a pattern instead of a skeleton. Maybe using another prefix could help. LocalDateTimeFormat.buildLocalized {
withFullMonthName()
withDay(minDigits = 2)
withHour()
withMinute()
}
I'd ask a bit differently - is userland code expected to cache formatters on its own? Or is the skeleton->pattern resolution operation worth caching? But e.g. in Jetpack Compose world, both approaches can be easily done: @Composable
fun LocalDate.formatLocalized(): String {
val locale = LocalContext.current.getLocale()
val formatter = remember(locale) { LocalDateFormat.buildLocalized(locale) { ... } }
return format(formatter)
}
// vs
val formatter = LocalDateFormat.buildLocalized { ... }
@Composable
fun LocalDate.formatLocalized(): String {
return format(formatter, LocalContext.current.getLocale())
} My questions:
|
Beta Was this translation helpful? Give feedback.
-
Not saying that they have to be in scope, but for completeness: Have relative formats ("in 20 minutes") been considered? For an API where the order of fields does not matter, I'd prefer something like this: LocalDateTimeFormat.withLocalized {
fullMonthName()
day(minDigits = 2)
hour()
minute()
} That means:
"Smart" parsing is still a thing as part of the Robustness principle, or Postel's Law: "be conservative in what you send, be liberal in what you accept". I'm not arguing that sloppy machine-machine input should be accepted without warning, but in today's API world, having a lenient fallback for input data can be valuable. |
Beta Was this translation helpful? Give feedback.
-
Wanted to repeat what I mentioned in this pull request. Please consider supporting other calendar systems too (like Solar calendar). JavaScript as well supports formatting dates based on the calendar of the given locale: let formatted = new Date().toLocaleDateString('fa'); See https://stackoverflow.com/q/35570884/8583692 Also, please take into account that some locales use different symbols (different Unicode characters) to represent digits and numbers. For example:
And, for the 2nd and 3rd example, sometimes users may want to deviate from the standard (which is eastern) and have western digits or to mix and match them when formatting. Java correctly formats numbers (for example when using Relevant:
|
Beta Was this translation helpful? Give feedback.
-
If possible, I think if we can still support java DateTimeFormatter Also, we have a use case that locale and format pattern is from server/api calls not local pre-defined, so it is not able to set up formatter with pattern like
we must getting the formatter like
Reference: |
Beta Was this translation helpful? Give feedback.
-
It seems wierd that we will have locale aware formatting of dates and times, when we have no locale concept in language or standard library or other kotlinx library. We even don't have locale aware number formatting, which for me is the first step. Other thing is bundling CLDR data with datetime library seems wierd too. What about only including locales that the app is going to use? From my point of view, we should create something like
What do you think? |
Beta Was this translation helpful? Give feedback.
-
In my use case, I need also the localized symbols as-such, e.g. the weekday names, or the short weekday names etc. additionally to normal date formatting. Additionally, I need them not always in the current system locale, but in a specified locale, e.g. get the weekday names in Polish. |
Beta Was this translation helpful? Give feedback.
-
Hello! I think it's also important to support platform-specific formatting configurations and native system defaults. For example, Android has android.text.format.DateFormat with factory methods that provide instances of java.text.DateFormat tuned appropriately to the user's system formatting preferences, including not just locale, but also 12 hour vs. 24 hour clock. We need a way to honor these preferences! |
Beta Was this translation helpful? Give feedback.
-
This is a discussion about the potential shape of the API dedicated to
locale-aware formats.
Problem statement
The use cases for date-time formatting are:
A significant source of mistakes when using the formats are confusing between these two use cases:
May 29, 10:53 AM
: the month name is wrong, and the time of day is not in the 24-hour format.Fri, 21 Nov 1997 09:55:06 -0600
is a correct RFC 2822 string, butпт., 21 нояб. 1997 09:55:06 -0600
is not.A good date-time formatting API should make it easy to write correct code for both of the use cases, which means avoiding the specified pitfalls.
Some prior art
C, Python, etc.:
strftime
%x
and%X
for "preferred date format" and "preferred time format," respectively. No way to specify "I don't want to output seconds" or "output without the weekday."Searching https://github.com/search?q=strftime+Tue+rfc+822+lang%3AC%2B%2B+&type=code, we can find several mistakes of the second kind and, occasionally, some explicit string manipulation to work around them:
https://github.com/killgcd/chromego/blob/1775962c565688a70cefc1a814a2654212e5942f/ChromeGo/XX-Net/code/default/python27/1.0/lib/rfc822.py#L971
Go
Some predefined locale-aware formats exist but don't seem widely used: https://grep.app/search?q=ShortFormatsByLocale, which invites the issue of
18:55
vs.06:55 PM
for different locales.monday
library.JavaScript
Date.parse
typically supports both ISO 8601 and RFC 2822: https://stackoverflow.com/a/30421037/20886821. It looks like JS programmers just put their strings intoDate.parse
, and it usually does what they want it to. Its behavior other than parsing ISO 8601 is not guaranteed, though, so maybe this is a considerable footgun: see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#fall-back_to_implementation-specific_date_formats For formatting though, it looks like the internationalization API can get you pretty far: https://stackoverflow.com/a/63490548/20886821Java
However, the correct format is not always chosen. If the locale specifies that the dates should use a particular chronology, this will get silently ignored by default (from https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/time/format/DateTimeFormatter.html):
For example, in the locale
ja_JP_JP_#u-ca-japanese
, despite the calendar being explicitly set to the Japanese one, the chronology used is ISO. To correctly handle this case, one must callwithChronology(Chronology.ofLocale(locale))
on the chosen date-time formatter. However, it is not clear whether this is important. There aren't many locales that include a chronology (despite it being possible in theory to mix and match locales and chronologies freely): https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS43LjIxIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiaW1wb3J0IGphdmEudGltZS4qXG5pbXBvcnQgamF2YS50aW1lLmZvcm1hdC4qXG5pbXBvcnQgamF2YS51dGlsLipcblxuZnVuIG1haW4oKSB7XG4gICAgcHJpbnRsbihMb2NhbGUuZ2V0QXZhaWxhYmxlTG9jYWxlcygpLnRvTGlzdCgpLmZpbHRlciB7XG4gICAgICAgIGl0LnRvU3RyaW5nKCkuY29udGFpbnMoXCJjYS1cIilcbiAgICB9KVxufSJ9 Random sampling of Japanese websites didn't yield any of them using the Imperial calendar. It is possible that using the incorrect chronology is not an actual issue.That the field format is correct does not mean that the format as a whole is. For example, if the format specifies the time with AM/PM markers, this does not get automatically corrected. To deal with this, Java provides
DateTimePattern.ofLocalizedDateTime
,DateTimePattern.ofLocalizedDate
, andDateTimePattern.ofLocalizedDate
, which return a particular magical formatter than automatically adapts the set and order of fields to the locale. Unfortunately, it's not very flexible, as there are only four variants of the output: "short", "medium", "long", and "full". The current date is formatted with them in theen_US
locale as these strings, respectively:2/28/23
,Feb 28, 2023
,February 28, 2023
, andTuesday, February 28, 2023
. As you can see, there is no option like "I only need the month and the day" or "I need the short weekday name", which leads to people resorting to hardcoded non-localized formats.Examples of discussions of this:
Swift, Objective-C
There are two APIs for date/time formatting.
https://developer.apple.com/documentation/foundation/dateformatter allows the usual format strings, but forces eone to set the locale before it can be used. This way, the programmer gets to choose whether they should use the POSIX locale (https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_03_05), or some specific locale, or the current system locale.
To deal with the issue of formats themselves being dependent on the locale, there's another API: https://developer.apple.com/documentation/foundation/date/formatstyle
It allows essentially listing the components to format in a builder-like manner. From the linked documentation:
The order of the fields is not important: as we can see, it gets freely reordered to meet the needs of a particular locale.
Most likely, the API used internally to support this is https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetimepatterngenerator
These formats also allow parsing them "in a roundtrip manner".
Objective-C supports the same functionality, but with a slightly different API: https://developer.apple.com/documentation/foundation/nsdateformatter See
setLocalizedDateFormatFromTemplate
, which is essentially the same, but in a pseudo-format-string form.Proposal
We can separate the APIs for localized and non-localized parsing and formatting.
Non-localized formatting API
The API for non-localized parsing and formatting includes simple format strings that do not allow specifying month names, weekday names, AM/PM markers, or any other locale-aware fields.
These fields are occasionally useful for machine-machine communication as well: for example, the RFC 2822 format uses English month and weekday names, and when parsing preset localized output (for example, logs), there's typically no choice. For cases when the established formats actually require localized strings, support functions like
appendMonthName(names: List<String>)
in the builder API.This way, there is the option to deal with any strings life throws at you, but given that all the strings used for the format will be user-supplied, it will be completely clear that they are not localized.
Localized formatting API
We should provide and API like the one Swift and Objective-C have for specifying the set of fields to output. We should make the formats obtained in this manner impossible to use for parsing anything: for now, there are no clear use cases for this, as localized patterns can change with time as the CLDR (https://cldr.unicode.org/index, the source of knowledge typically used to select correct localized formats) gets updated.
One option is for the API to look like this:
Another way is to do something similar to what Swift does:
The second option aligns more with the non-localized format builder API, leading to more uniformity. On the other hand, this uniformity can also be problematic: people may get confused about which builder they should copy-paste the code from the Internet into.
Also, the second option doesn't communicate clearly that the order of the fields will ultimately not matter, whereas the first option has "field set" in its name.
There is also the option to use format strings to define field sets, like Objective-C does, but this seems to be the worst of the two worlds: it's confusing which format strings can go where, and it's unclear that the field order does not matter.
Open questions
[Mon, Tue, Wed, Thu, Fri, Sat, Sun]
so that people don't have to redefine it on their own constantly, but just having it somewhere as a constant is completely non-discoverable. Maybe something likeappendPosixShortWeekdayName()
would be welcome.DateTimeFormatter.ofLocalizedDate
or Swift'sDate.FormatStyle
?Beta Was this translation helpful? Give feedback.
All reactions