为什么我的 UTC 即时没有转换为 BST?

Why is my UTC instant not converting to BST?

提问人:Chris Knight 提问时间:8/14/2019 最后编辑:Basil BourqueChris Knight 更新时间:8/14/2019 访问量:1172

问:

我有以下代码:

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

这让我感到惊讶,因为我认为这将是 GMT/UTC 时间,并将其格式化为伦敦时间(此日期为 BST (UTC+1:00),产生 .instformatter19:00

我在这里错过了什么?

我猜这是我的代码的一个普遍问题,但如果它有所作为,那就是使用 ThreeTen-Backport 项目中的类,在 ThreeTenABP 项目中进一步适应早期的 Android。org.threeten.bp.*

java-time tenbp 三tenabp

评论

0赞 Basil Bourque 8/14/2019
我添加了对 ThreeTenABP 的提及,我假设您正在将其用于早期的 Android。查看如何在Android项目中使用ThreeTenABP

答:

5赞 Basil Bourque 8/14/2019 #1

tl;博士

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

避免使用传统的日期时间类

您正在将可怕的遗留类 (, ) 与现代 java.time 类混合在一起。别这样。仅使用 java.timeSimpleDateFormatDate

Instant= UTC 时刻

跳过前两行代码。输入字符串采用标准 ISO 8601 格式。默认情况下,java.time 类在解析/生成字符串时使用这些标准格式。因此,无需指定格式模式。"2019-08-13T18:00:00Z"

String input = "2019-08-13T18:00:00Z" ;
Instant instant = Instant.parse( input ) ;

instant.toString():2019-08-13T18:00:00Z

Instant不灵活

我怀疑您的问题出在您尝试格式化 .该类是 java.time 中的基本构建块类。它只代表UTC中的一个时刻。它不适用于字符串的灵活生成等操作。InstantInstant

更灵活的类是 & 类。OffsetDateTimeZonedDateTime

ZonedDateTime

将 a 应用于 your 以调整为时区,渲染对象。ZoneIdInstantZonedDateTime

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 的偏移量提前一小时。因此,我们看到一天中的时间是晚上 7 点,而 UTC 的时间是下午 6 点。

适当的时区

顺便说一句,不是时区。我建议你避免使用这些伪区域。BST

以 的格式指定适当的时区名称,例如 、 或 。切勿使用 2-4 个字母的缩写,例如 或 或,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。Continent/RegionAmerica/MontrealAfrica/CasablancaPacific/AucklandBSTESTIST

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


上述所有代码都是在 Java 13 抢先体验版中使用 ThreeTen-Backport 库运行的,具体取决于您在问题中陈述的需求。

import org.threeten.bp.* ;
import org.threeten.bp.format.* ;

读者注意:ThreeTen-Backport 库在 ThreeTenABP 库中进一步适用于早期的 Android.请参阅如何在Android中使用ThreeTenABP.如果使用的是 Android 26 及更高版本,则会捆绑 java.time 类,因此您根本不需要向后移植。