为什么我的 UTC 即时不转换为 BST?
Why is my UTC instant not converting to BST?
我有以下代码:
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 project, further adapted for early Android in the ThreeTenABP 项目中的 org.threeten.bp.*
类。
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
避免遗留日期时间 classes
你把可怕的遗产 classes (SimpleDateFormat
, Date
) 和现代的 java.time classes。不要那样做。仅使用 java.time.
Instant
= UTC 时刻
跳过前两行代码。您输入的字符串 "2019-08-13T18:00:00Z"
是标准的 ISO 8601 格式。当 parsing/generating 字符串时,java.time classes 默认使用这些标准格式。因此无需指定格式模式。
String input = "2019-08-13T18:00:00Z" ;
Instant instant = Instant.parse( input ) ;
instant.toString(): 2019-08-13T18:00:00Z
Instant
不灵活
我怀疑您的问题在于您试图格式化 Instant
中的值。 Instant
class 是 java.time 中的基本构建块 class。它仅代表 UTC 中的一个时刻。它不适用于诸如灵活生成字符串之类的事情。
更灵活的 classes 是 OffsetDateTime
& ZonedDateTime
classes.
ZonedDateTime
将 ZoneId
应用到您的 Instant
以调整到时区,呈现 ZonedDateTime
对象。
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2019-08-13T19:00+01:00[Europe/London]
您似乎只想专注于一天中的时间。提取一个 LocalTime
对象。
LocalTime lt = zdt.toLocalTime ();
lt.toString(): 19:00
对于该日期采用夏令时 (DST) 的伦敦地区,与 UTC 的偏移量提前一小时。所以我们看到时间是晚上 7 点,而 UTC 是下午 6 点。
正确的时区
顺便说一句 BST
不是时区。我建议您避免使用这些伪区域。
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 BST
或 EST
或 IST
等 2-4 个字母的缩写,因为它们 不是 真实时区,未标准化,也不是甚至是独一无二的(!)。
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
上面的所有代码都是 运行 在 Java 13 中根据您在问题中所述的需求使用 ThreeTen-Backport 库进行早期访问。
import org.threeten.bp.* ;
import org.threeten.bp.format.* ;
reader 注释:ThreeTen-Backport 库进一步适用于 ThreeTenABP library. See 中的早期 Android。如果使用 Android 26 及更高版本,java.time classes 是捆绑的,因此您根本不需要后端口。
我有以下代码:
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 project, further adapted for early Android in the ThreeTenABP 项目中的 org.threeten.bp.*
类。
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
避免遗留日期时间 classes
你把可怕的遗产 classes (SimpleDateFormat
, Date
) 和现代的 java.time classes。不要那样做。仅使用 java.time.
Instant
= UTC 时刻
跳过前两行代码。您输入的字符串 "2019-08-13T18:00:00Z"
是标准的 ISO 8601 格式。当 parsing/generating 字符串时,java.time classes 默认使用这些标准格式。因此无需指定格式模式。
String input = "2019-08-13T18:00:00Z" ;
Instant instant = Instant.parse( input ) ;
instant.toString(): 2019-08-13T18:00:00Z
Instant
不灵活
我怀疑您的问题在于您试图格式化 Instant
中的值。 Instant
class 是 java.time 中的基本构建块 class。它仅代表 UTC 中的一个时刻。它不适用于诸如灵活生成字符串之类的事情。
更灵活的 classes 是 OffsetDateTime
& ZonedDateTime
classes.
ZonedDateTime
将 ZoneId
应用到您的 Instant
以调整到时区,呈现 ZonedDateTime
对象。
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2019-08-13T19:00+01:00[Europe/London]
您似乎只想专注于一天中的时间。提取一个 LocalTime
对象。
LocalTime lt = zdt.toLocalTime ();
lt.toString(): 19:00
对于该日期采用夏令时 (DST) 的伦敦地区,与 UTC 的偏移量提前一小时。所以我们看到时间是晚上 7 点,而 UTC 是下午 6 点。
正确的时区
顺便说一句 BST
不是时区。我建议您避免使用这些伪区域。
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 BST
或 EST
或 IST
等 2-4 个字母的缩写,因为它们 不是 真实时区,未标准化,也不是甚至是独一无二的(!)。
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
上面的所有代码都是 运行 在 Java 13 中根据您在问题中所述的需求使用 ThreeTen-Backport 库进行早期访问。
import org.threeten.bp.* ;
import org.threeten.bp.format.* ;
reader 注释:ThreeTen-Backport 库进一步适用于 ThreeTenABP library. See