英文:
How to force LocalDateTime Month to be 3 letters long
问题
对于英语语言,这很容易:
val englishFormatter = DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH)
for (month in 1..12) {
println(LocalDateTime.of(0, month, 1, 0, 0)
.format(englishFormatter))
}
结果如预期:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
对于德语语言(与上述相同,只是使用 Locale.GERMAN
),结果是意外的:
Jan. Feb. März Apr. Mai Juni Juli Aug. Sep. Okt. Nov. Dez.
虽然所有的月份都常见于德语,但 "März"、"Juni" 和 "Juli" 没有被缩写("Mai" 不需要缩写)。此外,大多数月份包含超过3个字母(注意句点!)
是否有一种方法来缩短它们呢?
例如:
März → Mrz.
Juni → Jun.
Juli → Jul.
顺便说一下:这段代码是用Kotlin编写的,但Kotlin使用了Java的LocalDateTime
。因此标记为Java。
编辑:我在Android 7.0上运行这段代码。
英文:
My goal is to use LocalDateTime
and display a month with exactly 3 letters.
For the English language, this is easy:
val englishFormatter = DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH)
for (month in 1..12) {
println(LocalDateTime.of(0, month, 1, 0, 0)
.format(englishFormatter))
}
The result is as expected:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
For the German language (as above, only with Locale.GERMAN
), the result is unexpected:
Jan. Feb. März Apr. Mai Juni Juli Aug. Sep. Okt. Nov. Dez.
While the abbreviations are all common in german, "März", "Juni" and "Juli" haven't been shortened ("Mai" doesn't need to be shortened). Also, most month contain more than 3 letters (notice the dot!)
Is there a way to shorten those, two?
e.g.
>März ⇒ Mrz.
Juni ⇒ Jun.
Juli ⇒ Jul.
Btw: the code is in Kotlin, but Kotlin is using Java's LocalDateTime
. Therefore tagged Java
Edit: i'm running this code on Android 7.0
答案1
得分: 1
Sure, here's the translated code portion:
import java.time.LocalDateTime
import java.time.Month
import java.time.format.TextStyle
import java.util.Locale
import java.time.format.DateTimeFormatter
fun main() {
var localDateTime = LocalDateTime.now()
println(getAbbreviatedMonth(localDateTime, Locale.GERMAN))
println(localDateTime.format(DateTimeFormatter.ofPattern("MMM", Locale.GERMAN)))
}
fun getAbbreviatedMonth(localDateTime: LocalDateTime, locale: Locale): String {
return localDateTime.getMonth()
.getDisplayName(TextStyle.SHORT, locale)
}
And the translated output:
八月
八月
英文:
You can have the desired / expected abbreviations like this:
import java.time.LocalDateTime
import java.time.Month
import java.time.format.TextStyle
import java.util.Locale
fun main() {
for (m in 1..12) {
val month = Month.of(m)
println(month.getDisplayName(TextStyle.SHORT, Locale.GERMAN))
}
}
the output comes as
Jan
Feb
Mär
Apr
Mai
Jun
Jul
Aug
Sep
Okt
Nov
Dez
LocalDateTime
has a method getMonth()
which returns a Month
object, that means you can get the month of a LocalDateTime
and build the desired String
, maybe with a small fun
like the following one
fun getAbbreviatedMonth(localDateTime: LocalDateTime, locale: Locale): String {
return localDateTime.getMonth()
.getDisplayName(TextStyle.SHORT, locale)
}
or even without a Locale
as argument and a hard-coded Locale.GERMAN
.
Don't really know why the German abbreviations are boiled down to 4 characters leaving month names with 4 or less characters as they are and abbreviate the remaining ones to three letters followed by a dot when you use a DateTimeFormatter
with the pattern "MMM"
on your system, in the Kotlin Playground, it doesn't! The following code produces two equal lines of output:
import java.time.LocalDateTime
import java.time.Month
import java.time.format.TextStyle
import java.util.Locale
import java.time.format.DateTimeFormatter
fun main() {
var localDateTime = LocalDateTime.now()
println(getAbbreviatedMonth(localDateTime, Locale.GERMAN))
println(localDateTime.format(DateTimeFormatter.ofPattern("MMM", Locale.GERMAN)))
}
fun getAbbreviatedMonth(localDateTime: LocalDateTime, locale: Locale): String {
return localDateTime.getMonth()
.getDisplayName(TextStyle.SHORT, locale)
}
which are (execution on 2020-08-12)
Aug
Aug
答案2
得分: 1
以下是翻译好的部分:
"Controlling exactly what month abbreviations Java gives you is hard, I don’t think you will want to bother. Java gets its locale data from up to four sources, and those sources generally come in versions. So even if you have managed to get your results exactly right, they may be different in the next Java version. I suggest that you choose between two options:
- Live with what you get. It’s well worked through and not unreasonable. Will any German have any trouble reading and understanding the abbreviations you mention?
- If you have very specific requirements for the abbreviations, such as
Mrz
rather thanMär
, hardcode the abbreviations you require, and then you know they will stay that way no matter if the locale provider and/or the locale data version changes.
As a compromise between the two you may try to select the locale data provider through defining the system property java.locale.providers
. As I said, the locale data you get from your provider may change in a future version.
If you want to hardcode your own preferred abbreviations, you can still build a DateTimeFormatter
that uses your abbreviations. For a simple demonstration in Java:
Map<Long, String> monthAbbreviations = Map.ofEntries(
Map.entry(1L, "Jan"), Map.entry(2L, "Feb"), Map.entry(3L, "Mrz"),
Map.entry(4L, "Apr"), Map.entry(5L, "Mai"), Map.entry(6L, "Jun"),
Map.entry(7L, "Jul"), Map.entry(8L, "Aug"), Map.entry(9L, "Sep"),
Map.entry(10L, "Okt"), Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.MONTH_OF_YEAR, monthAbbreviations)
.toFormatter(Locale.GERMAN);
String allAbbreviations = Arrays.stream(Month.values())
.map(formatter::format)
.collect(Collectors.joining(" "));
System.println(allAbbreviations);
Output is:
> Jan Feb Mrz Apr Mai Jun Jul Aug Sep Okt Nov Dez
The locale data providers are, from the documentation of LocaleServiceProvider
:
> Java Runtime Environment provides the following four locale providers:
>
> - "CLDR": A provider based on Unicode Consortium's CLDR Project.
> - "COMPAT": represents the locale sensitive services that is compatible with the prior JDK releases up to JDK8 (same as JDK8's
> "JRE").
> - "SPI": represents the locale sensitive services implementing the subclasses of this LocaleServiceProvider class.
> - "HOST": A provider that reflects the user's custom settings in the underlying operating system. This provider may not be available,
> depending on the Java Runtime Environment implementation.
> - "JRE": represents a synonym to "COMPAT". This name is deprecated and will be removed in the future release of JDK."
英文:
Controlling exactly what month abbreviations Java gives you is hard, I don’t think you will want to bother. Java gets its locale data from up to four sources, and those sources generally come in versions. So even if you have managed to get your results exactly right, they may be different in the next Java version. I suggest that you choose between two options:
- Live with what you get. It’s well worked through and not unreasonable. Will any German have any trouble reading and understanding the abbreviations you mention?
- If you have very specific requirements for the abbreviations, such as
Mrz
rather thanMär
, hardcode the abbreviations you require, and then you know they will stay that way no matter if the locale provider and/or the locale data version changes.
As a compromise between the two you may try to select the locale data provider through defining the system property java.locale.providers
. As I said, the locale data you get from your provider may change in a future version.
If you want to hardcode your own preferred abbreviations, you can still build a DateTimeFormatter
that uses your abbreviations. For a simple demonstration in Java:
Map<Long, String> monthAbbreviations = Map.ofEntries(
Map.entry(1L, "Jan"), Map.entry(2L, "Feb"), Map.entry(3L, "Mrz"),
Map.entry(4L, "Apr"), Map.entry(5L, "Mai"), Map.entry(6L, "Jun"),
Map.entry(7L, "Jul"), Map.entry(8L, "Aug"), Map.entry(9L, "Sep"),
Map.entry(10L, "Okt"), Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.MONTH_OF_YEAR, monthAbbreviations)
.toFormatter(Locale.GERMAN);
String allAbbreviations = Arrays.stream(Month.values())
.map(formatter::format)
.collect(Collectors.joining(" "));
System.out.println(allAbbreviations);
Output is:
> Jan Feb Mrz Apr Mai Jun Jul Aug Sep Okt Nov Dez
The locale data providers are, from the documentation of LocaleServiceProvider
:
> Java Runtime Environment provides the following four locale providers:
>
> - "CLDR": A provider based on Unicode Consortium's CLDR Project.
> - "COMPAT": represents the locale sensitive services that is compatible with the prior JDK releases up to JDK8 (same as JDK8's
> "JRE").
> - "SPI": represents the locale sensitive services implementing the subclasses of this LocaleServiceProvider class.
> - "HOST": A provider that reflects the user's custom settings in the underlying operating system. This provider may not be available,
> depending on the Java Runtime Environment implementation.
> - "JRE": represents a synonym to "COMPAT". This name is deprecated and will be removed in the future release of JDK.
Link
- Question about German month names and locale data sources: SimpleDateFormat with German Locale - Java 8 vs Java 10+
- Documentation of
LocaleServiceProvider
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论