【问题标题】:Convert Instant to OffsetDateTime then Instant.parse() on the OffsetDateTime leads to DateTimeParseException for zero seconds cases将 Instant 转换为 OffsetDateTime 然后 OffsetDateTime 上的 Instant.parse() 在零秒情况下导致 DateTimeParseException
【发布时间】:2021-09-28 22:10:18
【问题描述】:

我正在从服务接收即时信息,以下是案例。

在第一种情况下,说instant = "2021-03-23T04:17:35Z" & 在第二种情况下, instant = "2021-07-15T05:27:00Z"

然后需要将instant转换为offsetDateTime,就像

OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC)

现在我想计算以上 offsetDateTime 和 instant.now 之间的小时差

ChronoUnit.HOURS.between(Instant.parse(offsetDateTime), Instant.now())

结果

案例1:效果很好

案例 2:错误:DateTimeParseException: Text '2021-07-15T05:27Z' could not be parsed at index 16

找出原因: 在情况 2 中,如果它会通过相同的 2021-07-15T05:27:00Z。它会起作用,但由于instant.atOffset(ZoneOffset.UTC) 内部会调用下面的方法,将删除零,基本上会整理出微小的部分。所以低于 fn 将返回 2021-07-15T05:27Z ,这将导致 DateTimeParseException。

public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) {
        Objects.requireNonNull(instant, "instant");
        Objects.requireNonNull(zone, "zone");
        ZoneRules rules = zone.getRules();
        ZoneOffset offset = rules.getOffset(instant);
        LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
        return new OffsetDateTime(ldt, offset);
    }

我假设一个解决方案是手动附加零,但这可能不是一个好习惯。

【问题讨论】:

  • 我投了反对票,因为您的问题不清楚,并且您没有向我们展示失败的代码。 minimal reproducible example 会有很大帮助。
  • Ole V.V.我尝试用我所拥有的任何东西进行解释,我注意到了有问题的根本原因,所以错误是在 Insant 中解析失败,由@Arvind 清除,我只需要花费 offsetDate 时间并直接可以进行转换,没有这样的解析/格式化程序需要转换工作。从今以后,我能够计算出 ChronoUnit.HOURS.between 了。

标签: java java-8 java-time


【解决方案1】:

你不需要任何DateTimeFormatter

您不需要任何DateTimeFormatter 来解析您的日期时间字符串。现代日期时间 API 基于 ISO 8601,只要日期时间字符串符合 ISO 8601 标准,就不需要明确使用 DateTimeFormatter 对象。

import java.time.Instant;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        Instant instant1 = Instant.parse("2021-03-23T04:17:35Z");
        Instant instant2 = Instant.parse("2021-07-15T05:27:00Z");

        System.out.println(instant1);
        System.out.println(instant2);

        System.out.println(ChronoUnit.HOURS.between(instant1, instant2));
    }
}

输出:

2021-03-23T04:17:35Z
2021-07-15T05:27:00Z
2737

ONLINE DEMO

Trail: Date Time 了解有关现代日期时间 API 的更多信息。

【讨论】:

  • 但在这种情况下,需要 DateTimeFormatter System.out.println("== before =="+offsetDateTime); System.out.println("== after == "+ offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));稍后我什至可以用上面的 instant.parse(offsetDate) 进行解析
  • @toxi_code - 如果您想格式化Instant 获得的ZonedDateTimeOffsetDateTime,您将需要一个@ 987654332@。当您想要解析不符合 ISO8601 格式的日期时间字符串时,您也需要 DateTimeFormatter。如果有任何进一步的疑问/问题,请随时发表评论。
  • 是的,正确的ChronoUnit.HOURS.between(Instant.parse(offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)), Instant.now()) 解决了这个问题。
  • @toxi_code - 不,不要那样做。如果出于某种原因,您需要将offsetDateTime 转换为Instant,只需将其转换为offsetDateTime.toInstant(),即您应该将其转换为ChronoUnit.HOURS.between(offsetDateTime.toInstant(), Instant.now())。为此,无需使用DateTimeFormatter,这不仅没有必要而且容易出错。
  • 对!!,那么不需要 DateTimeFormatter。
【解决方案2】:

Answer by Avinash 是正确的。

另外,我们看一下问题中的这段代码:

public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) 
{
        Objects.requireNonNull(instant, "instant");
        Objects.requireNonNull(zone, "zone");
   
        ZoneRules rules = zone.getRules();
        ZoneOffset offset = rules.getOffset(instant);
        LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);

        return new OffsetDateTime(ldt, offset);
}

首先,如果您想对某个时刻应用偏移量,则不需要 LocalDateTime 类。只需这样做:

OffsetDateTime odt = instant.atOffset( myZoneOffset ) ;

at…from…with… 等请参见tutorial on naming conventions

当您想了解某个特定地区的人们使用的挂钟时间时,将时区 (ZoneId) 应用于Instant 以获得ZonedDateTime。使用ZonedDateTime 而不是OffsetDateTimeZonedDateTimeOffsetDateTime 更可取,因为它包含更多信息。如果添加或减去移动到另一个时刻,此信息可能很关键。在生成表示此日期时间对象内容的文本时,此信息也很有用。

了解与 UTC 的偏移只是几个小时-分钟-秒。时区更多。时区是政治家决定的特定地区人民使用的偏移量的过去、现在和未来变化的历史。

ZonedDateTime zdt = instant.atZone( myZoneId ) ;

所以你的方法应该是这样的。

public static ZonedDateTime ofInstant(Instant instant, ZoneId zone) 
{
    ZonedDateTime zdt = 
        Objects.requireNonNull( instant )
        .atZone(
            Objects.requireNonNull( zone )
        )
    ;
    return zdt ;
}

如果您确实需要OffsetDateTime,尽管ZonedDateTime 是首选,请提取一个。

OffsetDateTime odt = zdt.toOffsetDateTime() ;

【讨论】:

  • 真正的罗勒,这也是作为充值分享的有用知识。谢谢 n 继续分享好事实。
  • @Basil 我相信您是在引用和批评 OP 粘贴到问题中的 java.time 库中的源代码,作为对问题的部分解释。我同意应用程序中的方法不应该这样写,但我认为库中的代码在这里很好。我也同意这个问题并不明确,应该包含更多 OP 自己的代码。
猜你喜欢
  • 2019-12-18
  • 2019-03-09
  • 2015-08-19
  • 2020-10-17
  • 2019-09-22
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
相关资源
最近更新 更多