Java,将字符串’yyyy-MM-dd’T’HH:mm:ss’解析为ZonedDateTime,不改变时间

huangapple go评论111阅读模式
英文:

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);

返回值是

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

我也尝试创建这个类,但没有帮助

@Configuration
public class TimeZoneConfig {

    @PostConstruct
    public void init() {

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

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

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

英文:

I have a string "2018-07-24T01:30:27". I want to parse this into ZonedDateTime with EST timezone without changing the time.

I have the code...

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);

The return value is

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

I've tried creating this class as well, but it didn't help

@Configuration
public class TimeZoneConfig {

    @PostConstruct
    public void init() {

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

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

Could I get some advice on this please?

答案1

得分: 10

# tl;dr

```java
LocalDateTime                               // 表示带有时间的日期,但不包含时区或UTC偏移量。
.parse("2018-07-24T01:30:27")             // 返回一个`LocalDateTime`。
.atZone(ZoneId.of("America/New_York"))  // 返回一个`ZonedDateTime`对象。

[![Java中的日期时间类型表格,包括现代和传统类型][1]][1]

java.time中使用具体类

Answer by michalk的答案是正确的,但有点晦涩。它明确使用了[TemporalAccessor][2]接口。通常在Java中,这是一种好事情。但java.time类是为应用程序作者设计的,应该调用具体类而不是接口。引用Javadoc的话:

此接口是一个框架级接口,不应广泛用于应用程序代码。相反,应用程序应创建并传递具体类型的实例,如LocalDate。有许多原因,其中一部分是该接口的实现可能位于除ISO之外的日历系统中。请参阅ChronoLocalDate以获取更详细的讨论。

LocalDateTime

所以让我们这样做。首先,将输入解析为LocalDateTime。这个类不表示一个瞬间,不是时间线上的一个点,因为它缺乏时区或偏移的上下文。

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代码以明确指定所需的/预期的时区。从各种方法中不要省略可选的时区或偏移。


关于java.time

java.time框架内置于Java 8及更高版本。这些类取代了问题多多的旧传统日期时间类,如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范为JSR 310

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,也不需要java.sql.*类。Hibernate 5和JPA 2.2支持java.time

在哪里获取java.time类?


[1]: https://i.stack.imgur.com/yciGt.png
[2]: https://docs.oracle.com/en/java/javase/

<details>
<summary>英文:</summary>

# tl;dr

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.


[![Table of date-time types in Java, both modern and legacy][1]][1]


# Use concrete classes in *java.time*

The [Answer by michalk](https://stackoverflow.com/a/63019262/642706) is correct but a bit obtuse. It uses the [`TemporalAccessor`][2] interface explicitly. Generally in Java, this would be a good thing. But the *java.time* classes were designed for app authors to call the concrete classes rather than the interfaces. To quote the Javadoc:

&gt; 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`

So let’s do that. First, parse the input as a `LocalDateTime`. This class does not represent a moment, is not a point on the timeline, because it lacks the context of a time zone or offset.

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

## `ZonedDateTime`

Provide the context of a time zone.

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

## `Instant`

If you want to see that same moment adjusted into UTC, extract a `Instant` object.

    Instant instant = zdt.toInstant() ;



# Don’t mess with default time zone

&gt; TimeZone.setDefault

Do not set the JVM’s current default time zone except as a last resort. Doing so affects all code in all threads of all apps within that JVM. 

Instead, write your *java.time* code to explicitly specify the desired/expected time zone. Never omit the optional time zone or offset from the various methods.


----------

# About *java.time*

The [*java.time*](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/package-summary.html) framework is built into Java 8 and later. These classes supplant the troublesome old [legacy](https://en.wikipedia.org/wiki/Legacy_system) date-time classes such as [`java.util.Date`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Date.html), [`Calendar`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Calendar.html), &amp; [`SimpleDateFormat`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/SimpleDateFormat.html).

To learn more, see the [*Oracle Tutorial*](http://docs.oracle.com/javase/tutorial/datetime/TOC.html). And search Stack Overflow for many examples and explanations. Specification is [JSR 310](https://jcp.org/en/jsr/detail?id=310).

The [*Joda-Time*](http://www.joda.org/joda-time/) project, now in [maintenance mode](https://en.wikipedia.org/wiki/Maintenance_mode), advises migration to the [java.time](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/package-summary.html) classes.

You may exchange *java.time* objects directly with your database. Use a [JDBC driver](https://en.wikipedia.org/wiki/JDBC_driver) compliant with [JDBC 4.2](http://openjdk.java.net/jeps/170) or later. No need for strings, no need for `java.sql.*` classes. Hibernate 5 &amp; JPA 2.2 support *java.time*. 

Where to obtain the java.time classes? 

 - [**Java SE 8**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_8), [**Java SE 9**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_9), [**Java SE 10**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_10), [**Java SE 11**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_11), and later  - Part of the standard Java API with a bundled implementation.
   - [**Java 9**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_9) brought some minor features and fixes.
 - [**Java SE 6**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_6) and [**Java SE 7**](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_7)
   - Most of the *java.time* functionality is back-ported to Java 6 &amp; 7 in [***ThreeTen-Backport***](http://www.threeten.org/threetenbp/).
 - [**Android**](https://en.wikipedia.org/wiki/Android_(operating_system))
   - Later versions of Android (26+) bundle implementations of the *java.time* classes.
   - For earlier Android (&lt;26), a process known as [*API desugaring*](https://developer.android.com/studio/write/java8-support#library-desugaring) brings a [subset of the *java.time*](https://developer.android.com/studio/write/java8-support-table) functionality not originally built into Android.
     - If the desugaring does not offer what you need, the [***ThreeTenABP***](https://github.com/JakeWharton/ThreeTenABP) project adapts [***ThreeTen-Backport***](http://www.threeten.org/threetenbp/) (mentioned above) to Android. See [*How to use ThreeTenABP…*](http://stackoverflow.com/q/38922754/642706).





  [1]: https://i.stack.imgur.com/yciGt.png
  [2]: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/temporal/TemporalAccessor.html

</details>



# 答案2
**得分**: 4

如果您想要使用`ZonedDateTime`,请考虑使用`java.time`中的`DateTimeFormatter`:

```java
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]
英文:

Since you want ZonedDateTime consider using DateTimeFormatter from java.time :

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

The returned ZonedDateTime will be :

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

答案3

得分: 3

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]


<details>
<summary>英文:</summary>

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

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

When you know how, it is this simple. Output is:

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

Messages:

 1. Your string is in ISO 8601 format. The classes of java.time parse the most common variants of ISO 8601 natively, without any explicit formatter. So we don’t need one.
 2. Don’t mix old and modern. When you can use java.time, the modern Java date and time API (`ZoneId` and `ZonedDateTime`), stay away from the `SimpleDateFormat` and friends. BTW stay way from them in any case. `SimpleDateFormat` is a notorious troublemaker of a class.
 3. Don’t set nor rely on the default time zone of the JVM.
     1. You are affecting all other parts of your program and all other programs running in the same JVM, probably adversely.
     2.  The default time zone can be changed to something else from any other part of your program and any other program running in the same JVM, so may not stay what you set it to.

## What went wrong in your program?

`SimpleDateFormat` assumed that the string was in the default time zone of the JVM, so converted from that time zone. Next you converted to America/New_York (Eastern Daylight Time or EDT) thus changing the hour of day correspondingly.

Why setting the default time zone of your JVM didn’t work is that `TimeZone` takes EST, Eastern *Standard* Time, literally (opposite what it does with CST and PST), but in July New York is on Eastern *Daylight* Time, so there is still a conversion happening. But as I said, you don’t want to set the default time zone anyway.

## Link

[Wikipedia article: ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)

</details>



huangapple
  • 本文由 发表于 2020年7月22日 00:39:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/63019084.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定