Java 带有详细时区的 DateTimeFormatterBuilder
Java DateTimeFormatterBuilder with Verbose Time Zone
假设我有这样一个日期:
Nov 30, 2013 19:00:00.001930000 Eastern Standard Time
我正在尝试使用下面的 null
DateTimeFormatterBuilder
, but I can't figure out what to put for the Set
of generic type ZoneId
来解析输入。
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();
伪区
作为 class documentation explains briefly, the 3-4 letters commonly seen in mainstream media to indicate a time zone are not actually official time zones. These pseudo-zones are not standardized, and are not even unique! Many codes are re-used around the globe. For example, IST
is both India Standard Time and Irish Standard Time. And CST
is both China Standard Time and Central Standard Time(在北美)。
切勿使用这些伪区域。指定 proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
或 Pacific/Auckland
.
解决歧义
如果您的输入确实有这些伪区域,在缩写格式中,您必须处理歧义。默认情况下,格式化程序构建器将尝试通过考虑格式化程序的 Locale
来解决歧义。在 CST
的情况下,如果 Locale
是 Locale.CHINA
,那么 CST
可能表示中国标准时间而不是中部标准时间。
不幸的是,这是一种粗略的方法。 Locale
和时区的问题是正交的。您可以让讲中文的用户处理在芝加哥交付的数据,在这种情况下,格式化程序在尝试解析 CST
以覆盖考虑 Locale
的默认值时会考虑 Locale
may be China but the CST
found in the data means Central Standard Time. So in such cases, you can specify one or more time zones such as America/Chicago
and America/Winnipeg
.
Set< ZoneID > zones = new TreeSet<>() ;
zones.add( ZoneId.of( "America/Chicago" ) ;
zones.add( ZoneId.of( "America/Manitoba" ) ;
…
.appendZoneText( TextStyle.SHORT , zones )
…
这是一个完整的示例,将 CST
解析为我的 macOS MacBook 上的中央标准时间设置为默认时区 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 );
input: Nov 30, 2013 19:00:00.001930000 CST
zdt.toString(): 2013-11-30T19:00:00.001930-06:00[America/Chicago]
让我们传递 ZoneId
个对象的 Set
来覆盖该行为,暗示 CST
意味着 China Standard Time
。这里我们做传递Set
个ZoneId
个对象。我们使用相同的输入来获得截然不同的输出。
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 );
input: Nov 30, 2013 19:00:00.001930000 CST
zdt.toString(): 2013-11-30T19:00:00.001930+08:00[Asia/Shanghai]
现在,在你的例子中,你有伪区域的全名而不是缩写。所以很可能没有歧义。所以你可能可以摆脱 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 );
input: Nov 30, 2013 19:00:00.001930000 Eastern Standard Time
zdt.toString(): 2013-11-30T19:00:00.001930-05:00[America/New_York]
然而, 也可以在这里传递 Set
个 ZoneId
对象。 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 );
input: Nov 30, 2013 19:00:00.001930000 Eastern Standard Time
zdt.toString(): 2013-11-30T19:00:00.001930-06:00[America/Cancun]
假设我有这样一个日期:
Nov 30, 2013 19:00:00.001930000 Eastern Standard Time
我正在尝试使用下面的 null
DateTimeFormatterBuilder
, but I can't figure out what to put for the Set
of generic type ZoneId
来解析输入。
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();
伪区
作为 class documentation explains briefly, the 3-4 letters commonly seen in mainstream media to indicate a time zone are not actually official time zones. These pseudo-zones are not standardized, and are not even unique! Many codes are re-used around the globe. For example, IST
is both India Standard Time and Irish Standard Time. And CST
is both China Standard Time and Central Standard Time(在北美)。
切勿使用这些伪区域。指定 proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
或 Pacific/Auckland
.
解决歧义
如果您的输入确实有这些伪区域,在缩写格式中,您必须处理歧义。默认情况下,格式化程序构建器将尝试通过考虑格式化程序的 Locale
来解决歧义。在 CST
的情况下,如果 Locale
是 Locale.CHINA
,那么 CST
可能表示中国标准时间而不是中部标准时间。
不幸的是,这是一种粗略的方法。 Locale
和时区的问题是正交的。您可以让讲中文的用户处理在芝加哥交付的数据,在这种情况下,格式化程序在尝试解析 CST
以覆盖考虑 Locale
的默认值时会考虑 Locale
may be China but the CST
found in the data means Central Standard Time. So in such cases, you can specify one or more time zones such as America/Chicago
and America/Winnipeg
.
Set< ZoneID > zones = new TreeSet<>() ;
zones.add( ZoneId.of( "America/Chicago" ) ;
zones.add( ZoneId.of( "America/Manitoba" ) ;
…
.appendZoneText( TextStyle.SHORT , zones )
…
这是一个完整的示例,将 CST
解析为我的 macOS MacBook 上的中央标准时间设置为默认时区 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 );
input: Nov 30, 2013 19:00:00.001930000 CST
zdt.toString(): 2013-11-30T19:00:00.001930-06:00[America/Chicago]
让我们传递 ZoneId
个对象的 Set
来覆盖该行为,暗示 CST
意味着 China Standard Time
。这里我们做传递Set
个ZoneId
个对象。我们使用相同的输入来获得截然不同的输出。
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 );
input: Nov 30, 2013 19:00:00.001930000 CST
zdt.toString(): 2013-11-30T19:00:00.001930+08:00[Asia/Shanghai]
现在,在你的例子中,你有伪区域的全名而不是缩写。所以很可能没有歧义。所以你可能可以摆脱 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 );
input: Nov 30, 2013 19:00:00.001930000 Eastern Standard Time
zdt.toString(): 2013-11-30T19:00:00.001930-05:00[America/New_York]
然而, 也可以在这里传递 Set
个 ZoneId
对象。 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 );
input: Nov 30, 2013 19:00:00.001930000 Eastern Standard Time
zdt.toString(): 2013-11-30T19:00:00.001930-06:00[America/Cancun]