我有以下代码:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.UK);
Instant inst = DateTimeUtils.toInstant(sdf.parse("2019-08-13T18:00:00Z"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm").withLocale(Locale.UK).withZone(ZoneId.of("Europe/London"));
System.out.println(formatter.format(inst));
//prints 18:00
这让我感到惊讶,因为我认为inst
将是格林尼治标准时间(GMT)/ UTC时间,而formatter
会将其格式化为伦敦时间(该日期的BST(UTC + 1:00)) ),生成19:00
。
我在这里想念什么?
我猜这是我的代码中的一般性问题,但是如果有所区别,那就使用ThreeTen-Backport项目中的org.threeten.bp.*
类,在{{ 3}}项目。
答案 0 :(得分:4)
Instant // Represent a moment in UTC.
.parse( // Generate a `Instant` object from the content of text input.
"2019-08-13T18:00:00Z" // String in standard ISO 8601 format.
) // Returns a `Instant` object.
.atZone( // Adjust from UTC to the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "Europe/London" ) // Specify a time zone using name in proper `Continent/Region` format. Never use 2-4 letter pseudo-zones such as `BST`.
) // Returns a `ZonedDateTime` object.
.toLocalTime() // Extract the time-of-day, without a date and without a time zone or offset. Returns a `LocalTime` object.
.format( // Generate text representing the content of this `LocalTime` object.
DateTimeFormatter
.ofLocalizedTime ( FormatStyle.SHORT ) // Automatically localize while generating a `String`.
.withLocale ( Locale.UK ) // Locale determines the human language and cultural norms to use in localizing.
) // Returns a `String` object.
19:00
您正在将可怕的旧类(SimpleDateFormat
,Date
)与现代的 java.time 类混合在一起。不要那样做仅使用 java.time 。
Instant
= UTC时刻跳过您的前两行代码。您的输入字符串"2019-08-13T18:00:00Z"
为标准ISO 8601格式。在解析/生成字符串时, java.time 类默认使用这些标准格式。因此,无需指定格式设置模式。
String input = "2019-08-13T18:00:00Z" ;
Instant instant = Instant.parse( input ) ;
instant.toString():2019-08-13T18:00:00Z
Instant
不灵活我怀疑您的问题是您尝试在Instant
中设置值的格式。 Instant
类是 java.time 中的基本构建模块类。它仅代表UTC的时刻。它不适用于诸如灵活生成字符串之类的事情。
更灵活的类是OffsetDateTime
和ZonedDateTime
类。
ZonedDateTime
在您的ZoneId
上应用Instant
,以调整为时区,从而呈现ZonedDateTime
对象。
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString():2019-08-13T19:00 + 01:00 [欧洲/伦敦]
您似乎想只关注一天中的时间。提取一个LocalTime
对象。
LocalTime lt = zdt.toLocalTime ();
lt.toString():19:00
对于该日期的夏令时(DST)的伦敦地区,UTC偏移时间提前一小时。因此,我们看到时间是UTC的下午7点,而UTC是6 PM。
BST
不是时区。我建议您避免使用这些伪区域。
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用2-4个字母的缩写,例如BST
或EST
或IST
,因为它们不是真正的时区,不是标准化的,甚至不是唯一的( !)。
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
您的示例代码表明您过于关注字符串。使用智能对象,而不是哑字符串。
使用适当的类型使对象平直。生成字符串应该是最后一步,是类似于本地化的努力。您的业务逻辑应该通过使用适当的对象来完成,而不是通过操作字符串来完成。
谈到本地化:
Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime ( FormatStyle.MEDIUM ).withLocale ( locale );
String output = lt.format ( f );
19:00:00
将语言环境切换到Locale.US
以获得不同类型的结果:
7:00:00 PM
以上所有代码均根据问题中所述的ThreeTen-Backport库在Java 13早期访问中运行。
import org.threeten.bp.* ;
import org.threeten.bp.format.* ;
读者注意: ThreeTen-Backport 库在ThreeTenABP库中进一步适用于早期的Android。参见How to use ThreeTenABP in Android。如果使用的是Android 26和更高版本,则 java.time 类是捆绑在一起的,因此您根本不需要反向端口。