在Java中解析日期时间字符串需要"Locale"吗?

Bas*_*que 10 java datetime date java-time

  • 在什么条件下我需要Locale在Java中解析日期时间字符串?
  • Locale与时区有什么关系?

有时我会看到问题与解答,解决问题的解决方案需要Locale.然而在其他人中却没有提到Locale.

Bas*_*que 23

区域设置和时区不相关

区域设置和时区是与日期时间处理相关的单独的正交问题.

  • 语言环境
    • 语言
      人类语言,如阿拉伯语,法语,波斯语.星期几,月份名称和序数指标的名称文本.例如......它Monday还是Lundi
    • 文化
      通常用于安排构成日期时间值的字符串表示的文本和数字的习语.例如......在短短的形式,是month-date-year,date-month-yearyear-month-date?从长远来看,一周中的哪一天是第一位的吗?月份名称是初始上限还是全部小写?缩写是否具有FULL STOP(PERIOD)字符?
  • 时区
    • 偏移量一个区域内人们使用
      墙上时间UTC(GMT)之间的小时和分钟差异,这是世界调节时钟和时间的主要时间标准.
    • 异常
      对偏移量所做的更改历史记录,当前应用的定义偏移量的规则,包括夏令时(DST)等调整,以及在不久的将来确定的变更计划.

因此,您可以混合使用区域设置和时区.一些例子如下.

  • 一位参加印度浦那会议的法国男子需要在印度的壁垒时间看会议时间表,但更喜欢将"周一"称为"本地法语""Lundi".
    • 法国语言
    • 印度时区
  • 在西雅图工作的一位巴西工程师希望观看芬兰图尔库的现场网络研讨会.她需要知道何时将她的网络浏览器指向网络研讨会.在调整到西雅图时区之后,她需要知道芬兰的开始时间,但是她的母语为葡萄牙语.
    • 用于演示的区域设置("pt","BR")(用于生成文本表示)
    • 芬兰的预定开始时间必须从调整Europe/HelsinkiAmerica/Los_Angeles(西雅图时区).
  • 冰岛的一家报纸可能会报道在俄罗斯发生的事件为两个日期时间,即莫斯科时区,并且为了清晰添加UTC时区.但是这篇文章将使用冰岛语作为文本,包括星期几.
    • 莫斯科时区和冰岛地区
    • UTC时区和冰岛区域

解析/生成作为日期时间值的文本表示的字符串时,仅在以下两种情况下使用Locale:

  • 星期几的名称和/或月份名称(或序号指标,但最好避免这些)
  • 软编码的本地化格式

在第一种情况下,如果您的字符串包含"Monday"/"Lundi"或"March"/"Mars"等字词,则使用Locale来翻译这些字符串.

在第二种情况下,如果您没有明确的格式设置模式,则使用区域设置以了解星期几,日期,月份名称,年份等部分的预期顺序. .例如,说英语的美国人说"十一月十一日",讲法语的加拿大人使用逆序"11 octobre".通过软编码,我们的意思是类似于DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL )硬编码格式DateTimeFormatter.ofPattern("yyyy MM dd, EEE")

那么什么时候不需要Locale?如果您有一个包含所有数字的输入字符串,例如"2015-01-23",并且您将格式硬编码为"yyyy-MM-dd"...

String input = "2015-01-23";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyy-MM-dd")
Run Code Online (Sandbox Code Playgroud)

......然后Locale实际上是无关紧要的.你无法翻译,没有"星期一"或"伦迪".并且不要求使用需要区域设置的本地化格式化程序来了解日期是在月份之前还是之后,以及其他此类详细信息.

请注意,您仍然可以在这种情况下指定区域设置.实际上,我建议你养成总是指定Locale(和时区)的习惯.

隐含的区域设置和时区

那么为什么你在没有任何语言环境的StackOverflow上看到这么多与日期时间相关的问题和答案呢?因为如果省略,JVM的当前默认语言环境将自动且无提示地应用.

因此,如果您在JVM上运行英文文本的字符串设置为美国语言环境,那么没问题.但是不推荐这种对隐式语言环境的依赖.如果在运行时调用期间任何应用程序的任何线程中的任何代码Locale.setDefault,并影响该JVM中的所有其他代码.然后你的代码抛出异常.最好养成明确指定预期/期望区域设置的习惯.

时区的建议相同.如果省略,则会自动且无提示地应用JMV的当前默认时区.同样,运行时期间任何应用程序的任何线程中的任何代码都可以调用TimeZone.setDefault,并影响该JVM中的所有其他代码.然后您的代码抛出异常或意外行为.

在运行时的惊喜变化应该是足够的理由,养成始终指定语言环境和时区的习惯.但另一个好处是它还使您的代码自我记录.此外,在编程时有意识地指定区域设置和时区可能会提醒您错误或未经证实的假设.

示例场景

想象一下魁北克的商人.她向土耳其的一位客户确认他将在中午时分发货.因此,她使用壁挂时间创建一个物体,在土耳其接受交付.

ZoneId zoneIdIstanbul = ZoneId.of( "Europe/Istanbul" );
ZonedDateTime zdtIstanbul = ZonedDateTime.of( 2015, 10, 11, 12, 30, 00, 0, zoneIdIstanbul );  // Half-past noon in Turkey.
Run Code Online (Sandbox Code Playgroud)

为方便客户,她使用土耳其语和海关格式化文本.她定义了一个格式化程序对象来处理日期时间值的文本表示的生成.我们还可以在生成文本表示时为要应用的格式化程序分配时区.但是ZonedDateTime对象已经分配了时区,因此格式化程序将在该时区上进行拾取.

Locale locale_tr_TR = new Locale( "tr", "TR" );
DateTimeFormatter formatter_tr_TR = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale_tr_TR );
String outputTurkish = formatter_tr_TR.format( zdtIstanbul );
Run Code Online (Sandbox Code Playgroud)

我们的商人知道客户在芬兰使用物流协调员,所以她用芬兰语打印相同的日期时间值.所以我们有一个芬兰语区域的土耳其时区.

Locale locale_fi_FI = new Locale( "fi", "FI" );
DateTimeFormatter formatter_fi_FI = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale_fi_FI );
String outputFinnish = formatter_fi_FI.format( zdtIstanbul );
Run Code Online (Sandbox Code Playgroud)

对于她自己来说,她需要一个在自己的墙上时间预期交付的字符串,这样她就可以设置警报以提醒她检查成功完成.她本地读法语,而不是土耳其语.

所以下一个代码的不同之处在于我们需要调整时区语言环境.时间轴中的相同时刻,即预期交付的日期时间,但在文本中表示不同.注意这次我们如何withZone在链的末尾添加一个额外的调用来创建一个格式化程序,我们指定一个时区调整来覆盖ZonedDateTime对象的指定区域.

Locale locale_fr_CA = Locale.CANADA_FRENCH;
ZoneId zoneId_Montréal = ZoneId.of( "America/Montreal" );
DateTimeFormatter formatter_fr_CA_Adjusted = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale_fr_CA ).withZone( zoneId_Montréal );
String outputQuébec = formatter_fr_CA_Adjusted.format( zdtIstanbul );
Run Code Online (Sandbox Code Playgroud)

最后,为了我们讲英语的StackOverflow.com读者,让我们用英语做一个版本.但请注意,我们回收魁北克格式化程序,保留已设置的时区,但将区域设置替换为美国的区域设置.(技术上不是回收,但可以这么说.使用不可变对象意味着使用基于旧对象的值来实例化新对象.)

Locale locale_en_US = Locale.US;
DateTimeFormatter formatter_US_Unadjusted = formatter_fr_CA_Adjusted.withLocale( locale_en_US );
String output_US_Unadjusted = formatter_US_Unadjusted.format( zdtIstanbul );
Run Code Online (Sandbox Code Playgroud)

让我们看看这些值的输出.转储到控制台.

首先,我们隐式调用对象toString上的方法ZonedDateTime.默认情况下,此方法使用ISO 8601定义的标准格式之一.但是java.time通过在方括号中附加时区的名称来扩展该格式[Europe/Istanbul].在交换数据时,请使用这些明确的标准格式,而不是任何更人性化的格式.

System.out.println( "zdtIstanbul : " + zdtIstanbul );
System.out.println( "outputTurkish : " + outputTurkish );
System.out.println( "outputFinnish : " + outputFinnish );
System.out.println( "outputQuébec : " + outputQuébec );
System.out.println( "output_US_Unadjusted : " + output_US_Unadjusted );
Run Code Online (Sandbox Code Playgroud)

输出结果告诉我们,土耳其的午餐时间表示我们在魁北克省的女士发出的凌晨5:30的警报.

zdtIstanbul : 2015-10-11T12:30+03:00[Europe/Istanbul]
outputTurkish : 11 Ekim 2015 Pazar 12:30:00 EEST
outputFinnish : sunnuntai, 11. lokakuuta 2015 12.30.00 EEST
outputQuébec : dimanche 11 octobre 2015 5 h 30 EDT
output_US_Unadjusted : Sunday, October 11, 2015 5:30:00 AM EDT
Run Code Online (Sandbox Code Playgroud)