【问题标题】:Convert OffSetDateTime String to ZonedDateTime Java将 OffSetDateTime 字符串转换为 ZonedDateTime Java
【发布时间】:2019-12-17 16:54:59
【问题描述】:

我有 "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: Unparsable data 它不起作用。

我需要 ZonedDateTime 格式的 API,该 API 需要该格式的输入时间。

【问题讨论】:

  • 到目前为止你做了什么来解决它?
  • 添加了所需信息
  • 有什么理由需要ZonedDateTime 而不是OffsetDateTime?你真的没有时区 - 你有一个偏移量。
  • +12:24 的偏移量看起来不对,没有映射到任何时区。你确定它是正确的吗?你能解释一下来源吗?
  • 您正在混合过时的和现代的日期/时间类。我建议你不要使用SimpleDateFormatDate。这些类设计不良且过时,尤其是前者,尤其是出了名的麻烦。而是使用来自java.time, the modern Java date and time APIDateTimeFormatter

标签: java datetime


【解决方案1】:

tl;博士

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) 废话,导致偏移量每年变化两次。目前,政客们有一种时尚,即退出夏令时的变化,同时在“夏令时”全年永久停留,比标准时间提前一小时。

有一个数据库对这些更改进行编目。 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 的偏移量。所以我们不知道这是否意味着日本东京几乎晚上 8 点,法国图卢兹几乎晚上 8 点,或者美国俄亥俄州托莱多几乎晚上 8 点——这些事件都发生在几个小时内,时区的不同点。

这样的值必须被解析为LocalDateTime。将中间的空格替换为T 以符合 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 ) ;


关于java.time

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

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

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 这是分区日期时间 2018-06-20T01:30:51.468Z 吗?
  • @idk 请参阅我在顶部添加的“tl;dr”部分。 2019-11-23T10:32:15+12:24 调整为 UTC 为 2019-11-22T22:08:15Z
  • 谢谢,我会通过发送 api 请求来检查它是否有效
【解决方案2】:

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

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

OffsetDateTime offset = OffsetDateTime.parse(inputDate);

ZonedDateTime dateTime = offset.toZonedDateTime();

如果您只需要 ZonedDateTimeZoneId 在同一本地时间,请使用 atZoneSimilarLocal

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

【讨论】:

  • 输出结果是 2019-11-23T10:32:15+12:24 。这里不是预期的时区吗?来源:baeldung.com/java-zoneddatetime-offsetdatetime
  • 不确定是否应考虑使用的jodatime 标签。可能值得指出的是这是java.time(除非2个api完全兼容)
  • @idk 如果您的意图是将其转换为具有标准时区的分区日期时间,那么您可能需要read through this
  • 对于那个特定的偏移量,我没有得到任何TimeZones,您能否更具体一些预期的输出@idk
  • 仍然没有显示预期的输出?您想将输入字符串转换为特定区域还是只是将区域 ID 附加到那个时间? @idk
【解决方案3】:

目前尚不清楚您为什么认为自己想要 ZonedDateTime,如果您想要,在哪个时区。下面已经说了一些,但我想给你三个建议供你选择:

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

使用偏移日期时间

您的字符串包含偏移量+12:34,而不是时区,例如太平洋/加拉帕戈斯。所以OffsetDateTime表示它的内容更正确。

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

这个 sn-p 的输出是:

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 需要一个(在大多数情况下是一个糟糕的设计),只需将您的字符串解析为一个:

    ZonedDateTime dateTime = ZonedDateTime.parse(inputStringExample);

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

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

远离 SimpleDateFormat 和 Date

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

你的代码出了什么问题?

…由于 ParseException: Unparsable data,它不起作用。

Z 在您的格式模式字符串中用于 RFC 822 时区偏移。这没有冒号,会解析+1224,但不会解析+12:24

链接

【讨论】:

    猜你喜欢
    • 2017-11-02
    • 2020-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 2016-04-09
    • 1970-01-01
    相关资源
    最近更新 更多