Java,在不改变时间的情况下将字符串 'yyyy-MM-dd'T'HH:mm:ss' 解析为 ZonedDateTime

Java, parse string 'yyyy-MM-dd'T'HH:mm:ss' to ZonedDateTime without changing time

我有一个字符串“2018-07-24T01:30:27”。我想在不更改时间的情况下将其解析为带有 EST 时区的 ZonedDateTime。

我有密码...

String foo = "2018-07-24T01:30:27";
ZoneId zone = ZoneId.of("America/New_York");

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
return simpleDateFormat.parse(foo).toInstant().atZone(zone);

return值为

2018-07-24T02:30:27-04:00[America/New_York]

我也试过创建这个 class,但没用

@Configuration
public class TimeZoneConfig {

    @PostConstruct
    public void init() {

        TimeZone.setDefault(TimeZone.getTimeZone("EST"));

        System.out.println("Date in UTC: " + new Date().toString());
    }
}

请问我能得到一些建议吗?

因为你想要 ZonedDateTime 考虑使用 java.time 中的 DateTimeFormatter :

String foo = "2018-07-24T01:30:27";
ZoneId zone = ZoneId.of("America/New_York");
        
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
TemporalAccessor temporalAccessor = dateTimeFormatter.withZone(zone).parse(foo);
return ZonedDateTime.from(temporalAccessor);

返回的 ZonedDateTime 将是:

2018-07-24T01:30:27-04:00[America/New_York]
    String foo = "2018-07-24T01:30:27";
    ZoneId zone = ZoneId.of("America/New_York");

    ZonedDateTime result = LocalDateTime.parse(foo).atZone(zone);
    
    System.out.println(result);

知道怎么做就这么简单。输出为:

2018-07-24T01:30:27-04:00[America/New_York]

消息:

  1. 您的字符串采用 ISO 8601 格式。 java.time 的 classes 本机解析最常见的 ISO 8601 变体,没有任何显式格式化程序。所以我们不需要一个。
  2. 不要把旧的和现代的混在一起。当你可以使用java.time,现代Java日期和时间API(ZoneIdZonedDateTime),远离SimpleDateFormat和朋友.顺便说一句,无论如何都要远离他们。 SimpleDateFormat 是 class.
  3. 臭名昭著的麻烦制造者
  4. 不要设置也不要依赖 JVM 的默认时区。
    1. 您正在影响程序的所有其他部分以及同一 JVM 中的所有其他程序 运行,可能产生不利影响。
    2. 您的程序的任何其他部分和同一 JVM 中的任何其他程序 运行 都可以将默认时区更改为其他内容,因此可能不会保持您设置的状态。

你的程序出了什么问题?

SimpleDateFormat 假定字符串处于 JVM 的默认时区,因此从该时区转换而来。接下来,您转换为 America/New_York(东部夏令时或 EDT),从而相应地更改一天中的小时数。

为什么设置你的 JVM 的默认时区不起作用是因为 TimeZone 需要 EST,东部 标准 时间,字面意思(与它对 CST 的作用相反和太平洋标准时间),但 7 月纽约正处于东部 夏令时 时间,因此转换仍在进行。但是正如我所说,您无论如何都不想设置默认时区。

Link

Wikipedia article: ISO 8601

tl;博士

LocalDateTime                               // Represents a date with a time-of-day, but lacks a time zone or offset-from-UTC.
.parse( "2018-07-24T01:30:27" )             // Returns a `LocalDateTime`.
.atZone( ZoneId.of( "America/New_York" ) )  // Returns a `ZonedDateTime` object. 

java.time

中使用具体的classes

is correct but a bit obtuse. It uses the TemporalAccessor 接口显式。一般在Java,这会是一件好事。但是 java.time classes 是为应用程序作者设计的,可以调用具体的 classes 而不是接口。引用 Java 文档:

This interface is a framework-level interface that should not be widely used in application code. Instead, applications should create and pass around instances of concrete types, such as LocalDate. There are many reasons for this, part of which is that implementations of this interface may be in calendar systems other than ISO. See ChronoLocalDate for a fuller discussion of the issues.

LocalDateTime

让我们开始吧。首先,将输入解析为 LocalDateTime。这个class不代表一个时刻,不是时间轴上的一个点,因为它缺少时区或偏移量的上下文。

String input = "2018-07-24T01:30:27" ;
LocalDateTime ldt = LocalDateTime.parse( input ) ;

ZonedDateTime

提供时区的上下文。

ZoneId zone = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ldt.atZone( zone ) ;

Instant

如果您想看到调整为 UTC 的同一时刻,请提取一个 Instant 对象。

Instant instant = zdt.toInstant() ;

不要乱用默认时区

TimeZone.setDefault

除非万不得已,否则不要设置 JVM 的当前默认时区。这样做会影响该 JVM 中所有应用程序的所有线程中的所有代码。

相反,编写 java.time 代码以明确指定 desired/expected 时区。永远不要省略各种方法中的可选时区或偏移量。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes。 Hibernate 5 和 JPA 2.2 支持 java.time.

从哪里获得 java.time classes?