answer by Kokorin 是正确的。这里有更多的讨论。
问题
当您调用now 方法并且未传递任何参数时,您未能指定时区。在那个遗漏中,java.time 默默地应用了你的 JVM 当前的默认时区来确定当前的本地时间和当前的本地日期。
您声称您的 JVM 当前的默认时区是 Asia/Kolkata(印度时间)。如果您在办公室运行该代码时是 15:30 时间,那么您的代码会说“让我们以我的 15:30 为输入来表示迪拜的wall-clock time”。因此,虽然迪拜的当前时刻实际上是 14:00(我认为比印度更接近 UTC 一个半小时,不确定),但您在迪拜的未来创建了一个半小时的日期时间:15:30。
当您在ZonedDateTime.of( localDate, localTime, dubai ) 行中通过dubai 时,您假设您要求在时区之间进行调整。但实际上,您将时区分配到了根本没有时区的普通(“本地”)日期和时间。所有三个 Local… 类在内部不存储时区;他们的目的是忽略时区。您的代码与您的意图不符。
请注意,在对您的代码的此修订中,我如何将您的 ZoneId 对象传递给 now 方法。这将解决您的问题。
ZoneId dubai = ZoneId.of ( "Asia/Dubai" );
LocalDate localDate = LocalDate.now ( dubai );
LocalTime localTime = LocalTime.now ( dubai ); // Capturing `14:00` in Dubai rather than than `15:30` in India as in your version of code.
ZonedDateTime zonedDateTime = ZonedDateTime.of ( localDate , localTime , dubai );
System.out.println ( "Dubai Tiime:" + zonedDateTime );
但这仍然是糟糕的代码。如果在午夜时分调用这对 .now 方法,您将得到非常错误的信息(大约 24 小时后关闭)。
解决方案
相反,您应该以原子方式捕捉当前时刻。要么使用 Kokorin 的代码,要么使用下面显示的我的代码。
Instant 是 UTC 时间线上的时刻,分辨率为 nanoseconds。
Instant instant = Instant.now();
ZoneId zoneId_Dubai = ZoneId.of( "Asia/Dubai" );
ZonedDateTime zdt_Dubai = ZonedDateTime.ofInstant( instant , zoneId_Dubai );
作为快捷方式,调用静态方法ZonedDateTime.now。
ZonedDateTime zdt_Dubai = ZonedDateTime.now( zoneId_Dubai );
要查看相同的时刻但使用您自己的wall-clock time,请调整为India time。
ZonedDateTime zdt_Kolkata = zdt_Dubai.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) );
重要提示: 始终传递可选的时区参数。虽然我非常尊重 java.time 所做的工作,但我认为在各种方法上将时区参数设为可选是一个设计缺陷。 JVM 当前默认时区的静默隐式应用对于这么多程序员来说太容易掉入陷阱了。顺便说一句,Locale 也一样,总是指定。
另一个提示: 在UTC 中思考、工作和存储。 作为一名程序员,你必须学会在 UTC 中思考,不要把头脑从“我的时间在加尔各答”和“他们在迪拜的时光”。你会让自己发疯,让你的大脑受伤。编程时,要知道唯一的真实时间是 UTC。所有其他迪拜/加尔各答/蒙特利尔/奥克兰时代都是烟雾缭绕的,仅仅是幻想。在您的大部分代码中使用Instant 类,在进行日期时间工作时将其设为您的“首选”类(仅应用时区以向用户显示)。在您的数据库中使用 UTC。以 UTC 登录。将您的服务器保持在 UTC(或冰岛)时区。在将日期时间值序列化到存储或数据交换时使用 UTC(并使用ISO 8601 格式顺便说一句)。在你的桌子或屏幕上放一个时钟,显示 UTC。稍后,当你下班回家时,你就可以重新回到你自己当地的“印度时间”思维中。