提问人:Emmanuel Mtali 提问时间:1/24/2023 最后编辑:AnonymousEmmanuel Mtali 更新时间:1/28/2023 访问量:403
将 LocalDateTime 转换为 Instant 会给出不同的值
Converting LocalDateTime to Instant give different values
问:
我正在尝试使用 LocalDateTime 在我的应用程序中操作日期,但我注意到从中获取纪元秒返回的值与我预期的值不同
val now1 = Instant.now().epochSecond - 60
val now2 = Instant.now().minusSeconds(60).epochSecond
val now3 = LocalDateTime.now().minusSeconds(60).toEpochSecond(ZoneOffset.UTC)
val now4 = System.currentTimeMillis() / 1000 - 60
输出
Now1 = 1674501451
Now2 = 1674501451
Now3 = 1674512251
Now4 = 1674501451
请注意 Now3 如何具有不同的值。发生了什么事情?
答:
您需要使用 LocalTime#now(ZoneId zone)
with 才能获取 UTC 的本地时间;否则,系统将获取系统时区的本地时间。ZoneOffset.UTC
演示:
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
class Main {
public static void main(String[] args) {
Instant now = Instant.now();
var now1 = TimeUnit.SECONDS.convert(now.toEpochMilli(), TimeUnit.MILLISECONDS) - 60;
var now2 = TimeUnit.SECONDS.convert(now.minusSeconds(60).toEpochMilli(), TimeUnit.MILLISECONDS);
var now3 = LocalDateTime.now(ZoneOffset.UTC).minusSeconds(60).toEpochSecond(ZoneOffset.UTC);
var now4 = System.currentTimeMillis() / 1000 - 60;
System.out.println(now1);
System.out.println(now2);
System.out.println(now3);
System.out.println(now4);
}
}
示例运行的输出:
1674506413
1674506413
1674506413
1674506413
从 Trail: Date Time 了解有关新式日期时间 API 的更多信息。
TL的;博士
- JVM 的当前默认时区比 UTC 早 3 小时。因此,你的结果。
( 1_674_512_251L - 1_674_501_451L ) = 3 小时。 - 👉 您不恰当地使用了
LocalDateTime
类。
瞬间与非瞬间
我正在尝试使用 LocalDateTime 来操作日期
不要。
如果您表示的是某个时刻,即时间轴上的特定点,👉请不要使用 .该类保存具有时间的日期,但缺少时区或与 UTC 的偏移量的上下文。要表示一个时刻,请使用:LocalDateTime
即时
— 以 UTC 显示的时刻(与 UTC 的偏移量为零小时-分钟-秒)。OffsetDateTime
- 具有特定偏移量的时刻。ZonedDateTime
- 通过特定时区看到的时刻。
我无法想象任何打电话是正确的做法。LocalDateTime.now
示例代码:
Instant instant = Instant.now() ;
ZoneOffset offset = ZoneId.of( "Africa/Dar_es_Salaam" ).getRules().getOffset( instant ) ;
OffsetDateTime odt = OffsetDateTime.now( offset ) ;
ZoneId zone = ZoneId.of( "Africa/Dar_es_Salaam" ) ;
ZonedDateTime zonedDateTime = ZonedDateTime.now( zone )
转储到控制台。
System.out.println( instant + " | " + instant.getEpochSecond() ) ;
System.out.println( odt + " | " + odt.toEpochSecond() ) ;
System.out.println( zonedDateTime + " | " + zonedDateTime.toEpochSecond() ) ;
请参阅此代码在 Ideone.com 运行。请注意,这三者都在同一秒内,给或花一秒钟,因为时钟可能会在第二次或第三次调用时滚动到下一秒。now
2023-01-24T14:27:06.416597Z |1674570426
2023-01-24T17:27:06.478680+03:00 |1674570426
2023-01-24T17:27:06.487289+03:00[非洲/Dar_es_Salaam] |1674570426
LocalDateTime#toEpochSecond
从概念上讲,调用该方法很棘手。作为一个概念,没有纪元参考点。这只是一个有时间的约会。例如,“2023 年 1 月 23 日中午”本身没有特定的含义,无法确定在时间轴上的某个点。因此,将“2023 年 1 月 23 日中午”与参考点进行比较是没有意义的,例如 UTC 中显示的 1970 年的第一个时刻,1970-01-01T00:00Z。toEpochSecond
LocalDateTime
LocalDateTime
实际上,该类的实现方式就像在 UTC 中一样。因此,在内部,该类计算自 1970-01-01T00:00Z 以来的整秒数加上小数秒的纳秒数。但是,在为业务逻辑编写代码时,不应考虑此实现细节。对于业务逻辑,调用是没有意义的。LocalDateTime
LocalDateTime#toEpochSecond
从技术上讲
因此,您的问题的技术答案是,您的 3 小时增量 (1_674_501_451L - 1_674_512_251L) 来自您的 JVM 当前默认时区在当时使用的偏移量为 +03:00,比 UTC 早 3 小时。因此,您对 LocalDateTime.now
的调用捕获了比 UTC 早 3 小时的日期和时间。但是,您要求 toEpochSecond
,它将相同的日期和时间视为 UTC。
从逻辑上讲
当然,这个日期和时间并不意味着代表UTC中的某个时刻。所以从逻辑上讲,你的代码是荒谬的。您不应该将 epoch for a(非时刻)与其他类(例如(这是时刻))进行比较。LocalDateTime
Instant
换句话说,您正在比较苹果和橙子。
序列化日期时间数据
那么,如果不适合业务逻辑,为什么该类提供这样的方法?LocalDateTime#toEpochSecond
该方法对于序列化用于存储或数据交换的值非常有用。其他一些系统可能会以这种方式显示日期随时间变化的值。LocalDateTime
国际标准化组织 8601
但是,使用引用计数是传达日期时间值的一种糟糕方法。这些值是不明确的,因为它们的粒度是隐式的,并且它们的特定纪元参考点也是隐式的。而且,这些值使人类读者难以进行验证和调试。
我强烈建议在 Java 外部存储或交换日期时间值时使用标准 ISO 8601 文本而不是计数。关于您的示例,标准文本格式为:
2023-01-23T22:17:31
一般建议:避免使用 LocalDateTime
类,除非您非常清楚它的正确用法。
对于商业应用,我们通常会跟踪时刻。例如,“本合同何时生效”、“该员工何时被雇用”、“购买此房屋何时结束”。所以不需要。LocalDateTime
两种业务场景是 (a) 在时区规则发生变化时,我们希望时刻浮动的约会,例如诊所、沙龙和工作室预订,以及 (b) 我们指的是几个时刻,每个时刻都有相同的挂钟时间,但发生在时间轴上的不同时间点,例如“中午在我们德里的每个公司工厂宣布此公司消息, 杜塞尔多夫和底特律。搜索 Stack Overflow 以了解更多信息,因为我和其他人已经多次发布过。LocalDateTime
在 Ideone.com 上,请参阅我在编写此 Answer 时使用的一些代码。
评论