带有详细时区的Java DateTimeFormatterBuilder

时间:2017-10-20 02:52:54

标签: java java-8 timezone java-time datetime-parsing

假设我有一个日期,例如:

  

2013年11月30日19:00:00.001930000东部标准时间

我尝试使用DateTimeFormatterBuilder解析输入,但我无法弄清楚要为通用类型ZoneIdSet放置什么null {1}}下面。

String basePattern = "MMM dd, yyyy HH:mm:ss";
new DateTimeFormatterBuilder()
        .appendPattern(basePattern)
        .appendFraction(ChronoField.NANO_OF_SECOND,0,9, true)
        .appendZoneText(TextStyle.FULL, null)
        .toFormatter();

1 个答案:

答案 0 :(得分:6)

伪区

正如class documentation简要解释的那样,主流媒体中常见的3-4个字母表示时区并不是官方时区。这些伪区域不是标准化的,甚至不是唯一的!许多代码在全球范围内重复使用。例如,IST是印度标准时间和爱尔兰标准时间。 CST既是中国标准时间又是Central Standard Time(北美)。

切勿使用这些伪区域。以continent/region格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland

解决歧义

如果您的输入确实有这些伪区域,则缩写格式,您必须处理歧义。默认情况下,格式化程序构建器将尝试通过考虑格式化程序的Locale来解决歧义。对于CST如果LocaleLocale.CHINA,则CST可能意味着中国标准时间而非中央标准时间。

不幸的是,这是一种粗暴的做法。 Locale和时区的问题是正交的。您可以让一个说汉语的用户处理芝加哥的交付数据,在这种情况下,Locale可能是中国,但数据中的CST表示Central Standard Time。因此,在这种情况下,您可以指定格式化程序在尝试解析CST以覆盖考虑{Locale的默认值时要考虑的一个或多个时区,例如America/ChicagoAmerica/Winnipeg。 1}}。

Set< ZoneID > zones = new TreeSet<>() ;
zones.add( ZoneId.of( "America/Chicago" ) ;
zones.add( ZoneId.of( "America/Manitoba" ) ;
…
.appendZoneText( TextStyle.SHORT , zones ) 
…

以下是一个完整的示例,将我的macOS MacBook上的CST解析为中央标准时间设置为默认时区America/Los_Angeles和默认语言环境Locale.US。请注意,我们只将一个参数传递给appendZoneText(没有传递Set

String input = "Nov 30, 2013 19:00:00.001930000 CST";  
String basePattern = "MMM dd, yyyy HH:mm:ss";
DateTimeFormatter f = new DateTimeFormatterBuilder( )
        .appendPattern( basePattern )
        .appendFraction( ChronoField.NANO_OF_SECOND , 0 , 9 , true )
        .appendPattern( " " )
        .appendZoneText( TextStyle.SHORT  )
        .toFormatter( );

ZonedDateTime zdt = ZonedDateTime.parse( input , f );

System.out.println( "input: " + input );
System.out.println( "zdt.toString(): " + zdt );
  

输入日期:2013年11月30日19:00:00.001930000 CST

     

zdt.toString():2013-11-30T19:00:00.001930-06:00 [美国/芝加哥]

让我们通过SetZoneId个对象来覆盖该行为,暗示CST表示China Standard Time。在这里,我们传递SetZoneId个对象。我们使用相同的输入来获得非常不同的输出。

Set < ZoneId > zones = new HashSet <>( );
zones.add( ZoneId.of( "Asia/Shanghai" ) ) ;

String input = "Nov 30, 2013 19:00:00.001930000 CST";  
String basePattern = "MMM dd, yyyy HH:mm:ss";
DateTimeFormatter f = new DateTimeFormatterBuilder( )
        .appendPattern( basePattern )
        .appendFraction( ChronoField.NANO_OF_SECOND , 0 , 9 , true )
        .appendPattern( " " )
        .appendZoneText( TextStyle.SHORT , zones )
        .toFormatter( );

ZonedDateTime zdt = ZonedDateTime.parse( input , f );

System.out.println( "input: " + input );
System.out.println( "zdt.toString(): " + zdt );
  

输入日期:2013年11月30日19:00:00.001930000 CST

     

zdt.toString():2013-11-30T19:00:00.001930 + 08:00 [亚洲/上海]

现在,在您的情况下,您拥有伪区域的全名而不是缩写。所以可能没有歧义。所以你可能会逃脱overloaded method not taking a second argument

.appendZoneText( TextStyle.FULL ) 

示例:

String input = "Nov 30, 2013 19:00:00.001930000 Eastern Standard Time";  
String basePattern = "MMM dd, yyyy HH:mm:ss";
DateTimeFormatter f = new DateTimeFormatterBuilder( )
        .appendPattern( basePattern )
        .appendFraction( ChronoField.NANO_OF_SECOND , 0 , 9 , true )
        .appendPattern( " " )
        .appendZoneText( TextStyle.FULL )
        .toFormatter( );

ZonedDateTime zdt = ZonedDateTime.parse( input , f );

System.out.println( "input: " + input );
System.out.println( "zdt.toString(): " + zdt );
  

输入日期:2013年11月30日19:00:00.001930000东部标准时间

     

zdt.toString():2013-11-30T19:00:00.001930-05:00 [America / New_York]

然而, 对于传递SetZoneId个对象也很有用。 Set用于将时区分配给实例化的ZonedDateTime对象。请注意,在上面的输出中默认分配了America/New_York。但是,伪区“东部标准时间”也暗示了许多其他时区,例如巴哈马,America/Nassau和墨西哥的坎昆,等等。

然而,选择应用集合中的哪个元素对我来说是一个谜。我尝试使用SortedSet思考可以选择在Set的自然顺序中找到的第一个。唉,ZoneId没有实现Comparable界面,因此无法使用SortedSet,例如TreeSet

Set < ZoneId > zones = new HashSet <>( );
zones.add( ZoneId.of( "America/Detroit" ) );
zones.add( ZoneId.of( "America/New_York" ) );
zones.add( ZoneId.of( "America/Nassau" ) );
zones.add( ZoneId.of( "America/Cancun" ) );

String input = "Nov 30, 2013 19:00:00.001930000 Eastern Standard Time";  
String basePattern = "MMM dd, yyyy HH:mm:ss";
DateTimeFormatter f = new DateTimeFormatterBuilder( )
        .appendPattern( basePattern )
        .appendFraction( ChronoField.NANO_OF_SECOND , 0 , 9 , true )
        .appendPattern( " " )
        .appendZoneText( TextStyle.FULL , zones )
        .toFormatter( );

ZonedDateTime zdt = ZonedDateTime.parse( input , f );

System.out.println( "input: " + input );
System.out.println( "zdt.toString(): " + zdt );
  

输入日期:2013年11月30日19:00:00.001930000东部标准时间

     

zdt.toString():2013-11-30T19:00:00.001930-06:00 [America / Cancun]