Joda-Time忽略en-US服务器上的区域设置

时间:2014-11-18 17:41:22

标签: java jodatime

我使用Joda-Time将字符串日期转换为时间戳。 在我的pt-BR机器上一切正常。但是,在en-US服务器上,Joda会忽略自定义区域设置。

在我的笔记本电脑上(Windows 8 pt-BR):

Locale.setDefault(new Locale("pt", "BR"));
DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("dd-MMM-yy");
dateFormatter.parseLocalDate("13-Out-14").toDateTimeAtStartOfDay().getMillis();

此代码运行完美。葡萄牙语10月份的字是Outubro。

Locale.setDefault(new Locale("en", "US"));
DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("dd-MMM-yy");
dateFormatter.parseLocalDate("13-Oct-14").toDateTimeAtStartOfDay().getMillis();

上面的代码也可以正常工作。 JVM识别新的语言环境并转换日期。 但是,当我尝试在Windows Server 2012 en-US上运行第一段代码时,我得到了一个例外。

java.lang.IllegalArgumentException: Invalid format: "13-Out-14" is malformed at "Out-14"
  at org.joda.time.format.DateTimeFormatter.parseLocalDateTime(DateTimeFormatter.java:854)
  at org.joda.time.format.DateTimeFormatter.parseLocalDate(DateTimeFormatter.java:798)
  at br.com.luminiti.pro.service.DataUtil.dataStringToLong(DataUtil.java:155)

如果我将Out更改为Oct,它会毫无问题地转换。所以似乎JVM正在使用机器本身的语言。

如何解决此问题?

谢谢!

1 个答案:

答案 0 :(得分:3)

大写O是问题

10月份葡萄牙语的预期缩写名称为out,而不是Out

要在Joda-Time 2.5中演示此代码:

System.out.println( "October in Portuguese: " + DateTimeFormat.forPattern( "dd-MMM-yy" ).withLocale( locale_ptBR ).print( new DateTime( 2014 , 10 , 13 , 0 , 0 , 0 ) ) );

...输出小写o

October in Portuguese: 13-out-14

➥所以,将输入字符串从13-Out-14更改为13-out-14,您的代码就可以了。

月份名称缩写

查看Joda-Time预期缩写的月份名称:

Locale locale_ptBR = new Locale( "pt" , "BR" );  // Portuguese in Brazil.
System.out.println( "Short months for locale : " + locale_ptBR + Arrays.toString( org.joda.time.DateTimeUtils.getDateFormatSymbols( locale_ptBR ).getShortMonths() ) );

Locale locale_frCA = Locale.CANADA_FRENCH;  // Québec.
System.out.println( "Short months for locale : " + locale_frCA + Arrays.toString( org.joda.time.DateTimeUtils.getDateFormatSymbols( locale_frCA ).getShortMonths() ) );

Locale locale_enUS = Locale.US;  // United States.
System.out.println( "Short months for locale : " + locale_enUS + Arrays.toString( org.joda.time.DateTimeUtils.getDateFormatSymbols( locale_enUS ).getShortMonths() ) );

在为美国(美国)配置的Mac(Mountain Lion)上使用Java 8 Update 25中的Joda-Time 2.5运行时。

Short months for locale : pt_BR [jan, fev, mar, abr, mai, jun, jul, ago, set, out, nov, dez, ]
Short months for locale : fr_CA [janv., févr., mars, avr., mai, juin, juil., août, sept., oct., nov., déc., ]
Short months for locale : en_US [Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, ]

您可以看到它们如何以大写/小写,字符数和标点符号(.)变化。

本回答的其余部分解决了您的问题代码的其他问题。

避免设置默认区域设置

调用Locale::setDefault是一种冒险的错误做法,因为它会影响所有所有 所有线程中运行的Java代码在此整个 Java虚拟机(JVM)中运行的应用程序。您不仅要破坏其他代码使用原始默认语言环境的可能意图,而是在运行时运行。您正在更改,而其他代码执行。不好。

指定区域设置

不要覆盖默认的区域设置,而是指定所需的区域设置。调用DateTimeFormatter::withLocale告诉格式化程序在解析或生成字符串时应用特定的Locale。

指定时区

问题中的代码未能解释时区问题。时区对于解析和确定日期至关重要。在下面的示例代码中,尝试交换应用于格式化程序的时区,以查看非常不同的结果。

如果省略,则会应用JVM的当前默认时区。对当前默认值的这种隐式使用意味着您的代码的行为可能会在运行时在各种计算机上发生变化。

要指定时区,请致电withZone

使用正确的time zone names。切勿使用既不标准也不唯一的3或4个字母代码。

无需本地日期

在这种特殊情况下,不需要LocalDate。这样做应该有效,但将其保留在DateTime个对象中可能会更简单。

示例代码

Joda-Time 2.5中的示例代码。

DateTimeZone zoneSaoPaulo = DateTimeZone.forID( "America/Sao_Paulo" );
DateTimeZone zoneNewYork = DateTimeZone.forID( "America/New_York" );
DateTimeZone zoneLosAngeles = DateTimeZone.forID( "America/Los_Angeles" );

Locale locale_ptBR = new Locale( "pt" , "BR" );
Locale locale_enUS = new Locale( "en" , "US" );

DateTimeFormatter formatter_ptBR = DateTimeFormat.forPattern( "dd-MMM-yy" ).withLocale( locale_ptBR ).withZone( zoneSaoPaulo );
DateTimeFormatter formatter_enUS = DateTimeFormat.forPattern( "dd-MMM-yy" ).withLocale( locale_enUS ).withZone( zoneSaoPaulo );  // Try swapping out this time zone to see very different results.

DateTime dateTime_ptBR = formatter_ptBR.parseDateTime( "13-out-14" ).withTimeAtStartOfDay();  // Month must be lowercase for Portuguese, "out" not "Out".
DateTime dateTime_enUS = formatter_enUS.parseDateTime( "13-Oct-14" ).withTimeAtStartOfDay();  // The call to "withTimeAtStartOfDay" is not necessary as it is the default when parsing date-only. I would include it to be self-documenting of our intention.

long millis_ptBR = dateTime_ptBR.getMillis();
long millis_enUS = dateTime_enUS.getMillis();

转储到控制台。

System.out.println( "dateTime_ptBR : " + dateTime_ptBR );
System.out.println( "dateTime_ptBR : " + formatter_ptBR.print( dateTime_ptBR ) );
System.out.println( "dateTime_ptBR : " + DateTimeFormat.forStyle( "FF" ).withLocale( locale_ptBR ).print( dateTime_ptBR ) );
System.out.println( "millis_ptBR : " + millis_ptBR );

System.out.println( "dateTime_enUS : " + dateTime_enUS );
System.out.println( "dateTime_enUS : " + formatter_enUS.print( dateTime_enUS ) );
System.out.println( "dateTime_enUS : " + DateTimeFormat.forStyle( "FF" ).withLocale( locale_enUS ).print( dateTime_enUS ) );
System.out.println( "millis_enUS : " + millis_enUS );

System.out.println( "UTC : " + dateTime_ptBR.withZone( DateTimeZone.UTC ) );

跑步时。

dateTime_ptBR : 2014-10-13T00:00:00.000-03:00
dateTime_ptBR : 13-out-14
dateTime_ptBR : Segunda-feira, 13 de Outubro de 2014 00h00min00s BRT
millis_ptBR : 1413169200000

dateTime_enUS : 2014-10-13T00:00:00.000-03:00
dateTime_enUS : 13-Oct-14
dateTime_enUS : Monday, October 13, 2014 12:00:00 AM BRT
millis_enUS : 1413169200000

UTC : 2014-10-13T03:00:00.000Z