tl;博士
- 不同的 Java 实现可能在其本地化规则上有所不同。
- 不同版本的 Java 实现的本地化规则可能会有所不同。
对于 Oracle JDK 和 OpenJDK 项目,版本 9 及更高版本在其自己的规则集和Unicode CLDR 定义的规则集之间切换(请参阅Wikipedia)。见Release Notes,见OpenJDK JEP 252。
运行此代码:
Month.JULY.getDisplayName( TextStyle.FULL , new Locale( "pt" ) )
在 Java 8、Oracle JDK 中,默认情况下使用其自己的本地化规则,我们得到 initial-cap。
朱罗
在 Java 10、Oracle JDK 中,默认情况下使用 Unicode CLDR 规则,我们得到小写。
胡歌
在 Java 10 中,Oracle JDK 在设置 VM 选项 -Djava.locale.providers=COMPAT,SPI 以恢复到传统行为而不是使用 Unicode CLDR 后,我们得到 initial-cap。
朱罗
详情
- 文化规范定义的格式规则可能因JVM 实现和版本而异。
-
IdeOne.com 中的JVM 拒绝本地化。只有美国英语。 See proof。
- 您使用了错误的类。 使用 java.time 进行所有日期时间处理。
代码。
ZonedDateTime // Use modern class for a moment seen from a particular time zone.
.now() // Capture the current moment, using the JVM’s current default time zone. Better to specify a zone explicitly.
.format( // Generate a `String` representing the value of our `ZonedDateTime` object.
DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL )
.withLocale( new Locale( "pt" , "BR" ) )
) // Returns a `String` object.
2018 年 7 月 11 日 17:57:36 新西兰标准时间
文化规范
决定诸如月份名称大写之类的问题取决于文化规范。当然,这些规范可能会有所不同,理性的人可能会不同意。
但在某些时候,必须做出决定。 Java 实现必须有一组规则来制定这些本地化规则。
Unicode CLDR
也许您使用的是 Java 8 或更早版本。我在这个答案中的代码是用 Java 10 生成的。
一个重要的区别是,从 Java 9 开始,对于 Oracle JDK 和 OpenJDK 项目,localization rules changed 的默认源使用Unicode CLDR(请参阅Wikipedia)。在 Java 8 及更早版本中,每个 JVM 都提供了自己的一套规则。
另外,请注意 Unicode CLDR 会不时更新,并且某些规则可能会更改。
因此,根据使用的 Java 版本以及使用的 Java 实现,您可能会看到不同的本地化结果。
演示背景
也许这里的问题是上下文之一。在某些文化中,月份名称的大写等格式规则会因月份是单独呈现还是嵌入日期中而有所不同。
让我们试试 translate.Google.com:
-
July 产生 Julho
-
Early in the month of July 产生 no início do mês de julho
-
12th of July, 2018 产生 12 de julho de 2018
所以谷歌似乎因上下文而异,但我不确定第一个案例是初始上限是怎么回事。
可以使用Month::getDisplayName 方法中使用的TextStyle 枚举来指示此上下文。该枚举提供…STANDALONE 变体。
Month.JULY.getDisplayName(
TextStyle.FULL ,
new Locale( "pt" )
)
胡歌
Month.JULY.getDisplayName(
TextStyle.FULL_STANDALONE ,
new Locale( "pt" )
)
胡歌
所以不,使用 Java 10 时,上下文似乎不是问题。
java.time
您正在使用现在被 java.time 类取代的麻烦的旧类。
使用java.time.Instant 代替java.util.Date。两者都代表 UTC 中的一个时刻。现代的Instant 类使用更精细的纳秒分辨率而不是毫秒。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
instant.toString(): 2018-07-11T05:57:36.446917Z
从 UTC 调整到该地区的人们使用您想要的特定挂钟时间的时区。应用 ZoneId 以获取 ZonedDateTime 对象。时区与语言环境完全正交。一个与当前的内容有关,另一个仅影响用于生成字符串以表示该内容的本地化。例如,您可以使用带有葡萄牙语言环境的日本时区。
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
现在,我们要生成代表那个时刻的字符串。首先,生成标准ISO 8601 格式的String,通过在方括号中附加时区名称来扩展。
String output = zdt.toString() ; // Generate ISO 8601 format string.
2018-07-11T17:57:36.446917+12:00[太平洋/奥克兰]
您想要本地化生成的String 对象。使用DateTimeFormatter 控制要生成的String 的格式。指定 FormatStyle 来控制多长时间或缩写。
Locale l_pt = new Locale( "pt" ) ;
Locale l_ptBR = new Locale( "pt" , "BR" ) ;
Locale l_FR = Locale.FRANCE ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ) ;
String output_pt = zdt.format( f.withLocale( l_pt ) ) ;
String output_ptBR = zdt.format( f.withLocale( l_ptBR ) ) ;
String output_FR = zdt.format( f.withLocale( l_FR ) ) ;
quarta-feira,2018 年 7 月 11 日 17:57:36 Horário Padrão da Nova Zelândia
quarta-feira,2018 年 7 月 11 日 17:57:36 Horário Padrão da Nova Zelândia
mercredi 11 juillet 2018 à 17:57:36 heure normale de la Nouvelle-Zélande
为了好玩,让我们试试FormatStyle.LONG 而不是FULL。
2018 年 7 月 11 日 17:57:36 新西兰标准时间
2018 年 7 月 11 日 17:57:36 新西兰标准时间
11 juillet 2018 à 17:57:36 NZSTe
IdeOne 拒绝本地化
我喜欢使用 IdeOne.com 来演示 Java 代码。不幸的是,它的 JVM 拒绝使用除美国英语之外的任何 Locale。所以,不要使用上面的代码。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。
时间简史
在这个答案上回复 cmets…
时间线:
-
Sun Microsystems 启动 Java 1.0,其中包含一些由 IBM/Taligent 捐赠的日期时间类(
java.util.Date 等)。不幸的是,它们设计不佳且存在缺陷,令人困惑且麻烦。
- Sun 在 Java 1.1 中添加了更多类(
java.util.Calendar 等),以尝试改进日期时间处理。但这些也被证明设计不佳且令人困惑,而不是真正的改进。
- 几年后,一个人 Stephen Colebourne 创立了 Joda-Time 项目,以创建一个综合复杂的日期时间处理库,这在 IT 行业中尚属首次。巨大的成功。最受欢迎的 Java 库之一,经常添加到许多开发人员的项目中。
- Sun,然后是 Oracle,以及 JCP community 终于认识到,早期 Java 版本附带的旧日期时间类是不够的。这一认可为同一个 Stephen Colebourne 推出 JSR 310 开启了大门,这是一个全新的日期时间 API 规范,作为 Java 平台的一部分。这个项目基于 Joda-Time 的概念,但是是一个全新的重写。新的重写建立在创建 Joda-Time 的经验之上,询问“如果我们当时知道我们现在所知道的,我们将如何设计 Joda-Time?”。
- JSR 310 的实现被构建为 java.time 包,将包含在即将推出的 Java 8 中。
- 为确保更广泛地接受 java.time API,Stephen Colebourne 启动了开源 ThreeTen-Backport 项目,模仿了与 java.time 中几乎相同的 API但能够在 Java 6 和 Java 7 上运行。java.time 功能并非全部,而是大部分。目标是使早期 Java 的开发人员能够开始使用 API。然后,当迁移到更高版本的 Java 时,他们只需将
import 语句从 org.threeten.bp.* 更改为 java.time.*。
- 随着 Java 8 的成功发布,Stephen Colebourne 等人。将 Joda-Time 项目转换为维护模式,仍然积极更新
tzdata 更改和错误修复,但没有进一步的功能工作要做。他们建议从 Joda-Time 迁移到 java.time 类。
- Stephen Colebourne 还启动了开源 ThreeTen-Extra 项目以处理可能最终提交或可能不会提交到 JSR 310 更高版本的附加功能(成为附加的 java.time 类) .同时,这些额外的功能,对 java.time 功能的扩展,作为库提供给那些认为它们有用的开发人员。