将OffSetDateTime字符串转换为ZonedDateTime Java

时间:2019-12-17 16:54:59

标签: java datetime

我有一个模式为"yyyy-MM-dd'T'HH:mm:ssZ"的字符串,我想使用Java将其转换为ZonedDateTime格式。

输入字符串示例:"2019-11-23T10:32:15+12:24"
输出:ZonedDateTime

编辑:我已经尝试过了,但是没有用。

   ZonedDateTime convertToZonedDateTime(final String source) {
    final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = null;
    try {
        date = dateFormat.parse(source);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

}

我有这个适用于字符串“ 2018-04-05 19:58:55”的解决方案,它会产生输出2018-04-05T19:58:55 + 05:30 [Asia / Kolkata],但是当我更改模式时函数“ yyyy-MM-dd'T'HH:mm:ssZ”,并将字符串输入到2019-11-23T10:32:15 + 12:24,由于ParseException:无法解析的数据而无法正常工作。

我需要API的ZonedDateTime格式,该格式希望以这种格式输入时间。

4 个答案:

答案 0 :(得分:2)

您可以将输入的日期时间字符串解析为OffsetDateTime,然后将其转换为ZonedDateTime

String inputDate = "2019-11-23T10:32:15+12:24";

OffsetDateTime offset = OffsetDateTime.parse(inputDate);

ZonedDateTime dateTime = offset.toZonedDateTime();

如果您只需要在ZonedDateTime与当地时间同时使用ZoneId,请使用atZoneSimilarLocal

ZonedDateTime dateTime = offset.atZoneSimilarLocal(ZoneId.systemDefault());

答案 1 :(得分:2)

tl; dr

OffsetDateTime                       // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse(                              // Parse text into a date-time object.
    "2019-11-23T10:32:15+12:24"      // The offset of +12:24 looks suspicious, likely an error.
)                                    // Returns an `OffsetDateTime` object.

从概念上讲,到此,我们已经有了一个OffsetDateTime对象。

但是您声称使用的API需要一个ZoneDateTime对象。我们没有适用的时区,因此我们应用UTC(零时分分钟秒)。

OffsetDateTime                       // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse(                              // Parse text into a date-time object.
    "2019-11-23T10:32:15+12:24"      // The offset of +12:24 looks suspicious, likely an error.
)                                    // Returns an `OffsetDateTime` object.
.atZoneSameInstant(                  // Convert from `OffsetDateTime` to `ZonedDateTime` by applying a time zone.
    ZoneOffset.UTC                   // This constant is a `ZoneOffset` object, whose class extends from `ZoneId`. So we can use it as a time zone, though semantically we are making a mess. 
)                                    // Returns a `ZonedDateTime` object.
.toString()                          // Generate text in standard ISO 8601 format.

请参阅此code run live at IdeOne.com

  

2019-11-22T22:08:15Z

注意:您的示例输入字符串上的偏移量对我来说似乎是错误的。

详细信息

您需要了解一些有关日期时间处理的概念。

偏移量

与UTC的偏移距格林威治皇家天文台所画的子午线仅数小时-数分钟-秒。

在Java中,我们用ZoneOffset类表示偏移量。偏移量上下文中的日期和日期用OffsetDateTime类表示。这样的对象表示时刻,即时间轴上的特定点。

时区

时区远不止于此。时区是特定区域的人们过去,现在和将来对偏移量的更改的历史记录。这些变化是由政客决定的。因此,这些更改可能是任意的,反复无常的,并且经常出乎意料地发生,常常很少甚至没有警告。例如,在北美,大多数地区都采用了夏令时(DST)废话,导致偏移每年更改两次。当前,政客们热衷于退出DST更改,而在“夏令时”永久保持全年无休,比标准时间早一小时。

有一个数据库将这些更改分类。 tZ数据是IANA维护的文件,列出了全球范围内的更改。您可能会在主机操作系统,企业级数据库管理系统(例如 Postgres )和Java虚拟机中找到这些数据的副本。确保随着您关心的区域的变化而使这些保持最新。

时区的名称格式为Continent/Region。例如,Africa/TunisEurope/ParisAsia/Kolkata

OffsetDateTime

因此,“ 2019-11-23T10:32:15 + 12:24”之类的输入字符串没有时区指示符,只有偏移量。因此,我们必须将其解析为OffsetDateTime

OffsetDateTime odt = OffsetDateTime.parse( "2019-11-23T10:32:15+12:24" ) ;

以此为ZonedDateTime是没有道理的。我们不能仅根据偏移量来可靠地确定时区。许多时区可能会共享一些品脱的时间偏移。

此外,该特定输入字符串2019-11-23T10:32:15+12:24是可疑的。十二小时二十四分钟的偏移量不会映射到任何当前时区。您确定正确吗?

您可以通过指定要在调整中使用的时区来将OffsetDateTime转换为ZonedDateTime。我建议使用UTC。尽管这在技术上可行,但从语义上来说却令人困惑。 UTC中的时刻最好用OffsetDateTime而不是ZonedDateTime表示。但是显然您正在与需要ZonedDateTime的代码进行互操作,因此 c’est la vie

ZonedDateTime zdt = odt.atZoneSameInstant( ZoneOffset.UTC ) ;

Instant

提示:通常,应将API作为Instant对象来传递时间,根据定义,该对象始终位于UTC中。

LocalDateTime

您提出另一个字符串输入“ 2018-04-05 19:58:55”。此输入缺少任何时区或UTC偏移量指示符。因此,我们不知道这是否意味着在日本东京将近8PM,在法国图卢兹将近8PM,或在美国俄亥俄州托莱多将近8PM –所有这些事件相隔数小时,在时区上的不同时间点发生。

此类值必须解析为LocalDateTime。用T代替中间的SPACE,以符合ISO 8601标准格式。

LocalDateTime ldt = LocalDateTime.parse( "2018-04-05 19:58:55".replace( " " , "T" ) ) ;

结果对象不代表时刻,也不是时间轴上的一点。这样的物体代表了大约26-27小时的频谱中的潜在时刻,这是全球时区的范围。

ZonedDateTime

如果您确定输入字符串适用于特定时区,请应用ZoneId以获得ZonedDateTime。然后,您确定了一个时刻,即时间轴上的特定点。

ZoneId z = ZonedId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

Table of date-time types in Java, both modern and legacy


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

Table of which java.time library to use with which version of Java or Android

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

具有OffsetDateTime.toZonedDateTime:

<div>
<p>whatever don't print this</p>
</div>
<div id="yourdiv">
<p>print this only</p>
</div>
<button id="print" onclick="printContent('yourdiv');" >Print</button>

http://apisonar.com/java-examples/java.time.OffsetDateTime.toZonedDateTime.html

With OffsetDateTime.atZoneSimilarLocal:

public void test_adjustInto_OffsetDateTime() {
        ZoneOffset base = ZoneOffset.ofHoursMinutesSeconds(1, 1, 1);
        for (int i=-18; i<=18; i++) {
            OffsetDateTime offsetDateTime_target = OffsetDateTime.of(LocalDate.of(1909, 2, 2), LocalTime.of(10, 10, 10), ZoneOffset.ofHours(i));
            OffsetDateTime offsetDateTime_result = (OffsetDateTime)base.adjustInto(offsetDateTime_target);
            assertEquals(base, offsetDateTime_result.getOffset());

            ZonedDateTime zonedDateTime_target = offsetDateTime_target.toZonedDateTime();
            ZonedDateTime zonedDateTime_result = (ZonedDateTime)(base.adjustInto(zonedDateTime_target));
            assertEquals(zonedDateTime_target.getOffset(), zonedDateTime_result.getOffset());
        }
    }

http://apisonar.com/java-examples/java.time.OffsetDateTime.atZoneSimilarLocal.html

答案 3 :(得分:0)

目前尚不清楚您为什么想要ZonedDateTime,如果需要,则在哪个时区。已经讲了一些以下内容,但是我想给您三个建议供您选择:

  1. 您不需要ZonedDateTimeOffsetDateTime更适合您的字符串。
  2. 如果您希望在默认时区使用ZonedDateTime,请使用OffsetDateTime.atZoneSameInstant()(如Basil Bourque的回答)。
  3. 如果只想用ZonedDateTime表示字符串,则一个参数ZonedDateTime.parse()会直接对其进行解析。

使用OffsetDateTime

您的字符串包含偏移量+12:34,而不是时区,例如Pacific / Galapagos。因此OffsetDateTime更能代表其内容。

    String inputStringExample = "2019-11-23T10:32:15+12:24";
    OffsetDateTime dateTime = OffsetDateTime.parse(inputStringExample);
    System.out.println(dateTime);

此代码段的输出为:

  

2019-11-23T10:32:15 + 12:24

我同意Basil Bourque的评论,+12:24的偏移量看起来并不像现实中的UTC偏移量,但对于Stack Overflow示例来说,这很好。在2019年,大多数抵销时间为一个小时,其余时间通常为一刻钟,因此不使用24分钟。历史胶印包括很多分钟和秒。

我正在利用您的字符串为ISO 8601格式的事实。 java.time类将最常见的ISO 8601变量解析为默认变量,即没有任何显式格式化程序。这样做很好,因为编写格式模式字符串总是容易出错。

使用OffsetDateTime.atZoneSameInstant()

您在问题代码中对ZoneId.systemDefault()的调用似乎表明您希望在默认时区使用ZonedDateTime。一方面,ZonedDateTime的这种使用似乎是合理且合理的。另一方面,对ZoneId.systemDefault()的依赖是不稳定的,因为您的JVM的默认时区可以随时由程序的另一部分或在同一JVM中运行的任何其他程序来更改。

    ZonedDateTime dateTime = OffsetDateTime.parse(inputStringExample)
            .atZoneSameInstant(ZoneId.systemDefault());
    System.out.println(dateTime);

我所在时区的输出:

  

2019-11-22T23:08:15 + 01:00 [欧洲/哥本哈根]

直接解析

如果您只需要ZonedDateTIme来使用一个需要一个API(在大多数情况下是较差的设计)的API,只需将字符串解析为一个即可:

    ZonedDateTime dateTime = ZonedDateTime.parse(inputStringExample);
  

2019-11-23T10:32:15 + 12:24

输出与我们从OffsetDateTime得到的输出没有区别,但是您现在有了所需的类型。

远离SimpleDateFormat和Date

在问题代码中,您尝试使用SimpleDateFormat来解析字符串。因为您可以使用现代Java日期和时间API java.time,所以请坚持使用它,而忘记所有有关旧日期和时间类的内容。现代化的API为您提供了所需的所有功能。如果我们需要格式化程序来进行解析,则可以使用现代的DateTimeFormatter这个类。

您的代码出了什么问题?

  

…由于ParseException:无法分析的数据而无法使用。

格式模式字符串中的

Z用于RFC 822时区偏移量。这是没有冒号的,并且将解析+1224,但不会解析+12:24

链接