【问题标题】:Why isn't Date object representing date in my JVM's timezone?为什么 Date 对象不代表我的 JVM 时区中的日期?
【发布时间】:2019-08-08 15:05:36
【问题描述】:

我有 UTC 日期,我需要创建与 UTC 值完全相同的 Date 对象(遗留原因)。

我已经做到了:

String date = "2012-05-05 12:13:14";
TemporalAccessor formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
        .withZone(ZoneId.of("UTC"))
        .parse(date);
Instant instant = Instant.from(
        formatter
); //
ZonedDateTime zdf = ZonedDateTime.ofInstant(instant,ZoneId.of("UTC"));
Calendar calendar = Calendar.getInstance();
calendar.set(zdf.getYear(),zdf.getMonthValue(),zdf.getDayOfMonth(),zdf.getHour(),zdf.getMinute(),zdf.getSecond());
Date dt = calendar.getTime();

Date d2 = Date.from(instant);

但是,有什么困扰我 -

当我创建日期对象时,它应该在我的 JVM 的默认时区显示日期。但是这里dt 的值与我输入的UTC 日期完全相同,但dt2 在我的默认时区中表示的日期相同,为什么会发生这种情况?为什么一个人没有皈依而另一个人皈依了?

感谢您的解释!

【问题讨论】:

  • A java.util.Date 根本没有时区。自 Unix 纪元以来,它 只是 毫秒数。忽略调用Date.toString() 的结果——将值转换为本地时区的事实很烦人:(
  • @JonSkeet Date.toString() 即使在调试期间检查变量时也会调用?我以这种方式检查了对象的值
  • @JonSkeet 我尝试打印它并且都打印不同的值,所以一个被转换而另一个不是
  • 好吧,dt 被创建为“系统时区的 2012-05-05 12:13:14”,因为您调用了使用系统时区的 Calendar.getInstance()。而 d2 在 UTC 中是 2012-05-05 12:13:14。所以,是的,我对他们在做不同的事情并不感到惊讶,但目前还不清楚你想要做什么。
  • @jejjejd 您现在已经在 Stack Overflow 上提出了至少 21 个问题,并收到了大部分问题的答案。请允许我建议您是时候学会接受答案了。请仔细阅读这几段:What should I do when someone answers my question?这里的许多用户会非常感激。

标签: java time timezone


【解决方案1】:

tl;博士

  • 正如其他人所说,java.util.Date 代表 UTC 中的时刻。它的toString 方法通过动态应用 JVM 的当前默认时区同时生成格式不佳的字符串来欺骗您。 许多从不使用此类的原因之一。
  • 不要浪费你的时间去理解可怕的课程Date & Calendar。他们现在是legacy,要避免。
  • 仅将java.time classes 用于您的所有date-time handling

详情

Answer by Ole V.V. 是正确的,应该被接受。我会补充一些想法。

您正在将可怕的传统日期时间类(DateCalendar)与其现代替代品(InstantZonedDateTime)混合在一起。 不要混合使用这些。 旧类完全被 java.time 类所取代,并采用了JSR 310

无需再次使用DateCalendar不要浪费时间去理解它们。它们被替换是有原因的——实际上有很多原因。用你的大脑和时间进行更有成效的工作。

如果您必须使用遗留类与尚未更新到 java.time 的旧代码进行互操作,请通过调用添加的新 to…/from… 转换方法来进行来回转换到老班。使用 java.time 类执行您的业务逻辑、数据交换和数据存储。

java.util.Date 类被 Instant 替换。

java.util.Date d = Date.from( instant ) ;     // From  modern to legacy.
Instant instant = d.toInstant() ;             // From legacy to modern.

Calendar 类,或者更确切地说是其常用的子类GregorianCalendar,被ZonedDateTime 取代。假设您的 Calendar 对象实际上是下面的 GregorianCalendar,您可以投射,然后转换。

Calendar c = GregorianCalendar.from( zonedDateTime ) ;                        // From  modern to legacy.
ZonedDateTime zonedDateTime = ( (GregorianCalendar) c ).toZonedDateTime() ;   // From legacy to modern.

这是一个将传统类映射到现代类的图表。

【讨论】:

    【解决方案2】:

    保留时间与保留时刻

    我会一一解释中心线。

        Calendar calendar = Calendar.getInstance();
    

    这将创建一个Calendar,其时区与您的 JVM 的默认时区相同。与Date 不同,Calendar 有一个时区。

        calendar.set(zdf.getYear(),zdf.getMonthValue(),zdf.getDayOfMonth(),
                zdf.getHour(),zdf.getMinute(),zdf.getSecond());
    

    这会将您的Calendar 设置为与您的ZonedDateTime 相同的挂钟时间,即12:13:14。由于ZonedDateTimeCalendar 具有不同的时区(分别为UTC 和您的本地时区),这会导致不同的时刻。

    @VGR 也是正确的:虽然 ZonedDateTIme 的日期是在 5 月(第 5 个月),但您将 Calendar 月份设置为 6 月,因为 Calendar 月份令人困惑地从 0 开始,从 0 表示 1 月12 月至 11 日。

        Date d2 = Date.from(instant);
    

    这是从InstantDate 的正确转换,并为您提供与Instant 相同的时间点。因此不是Calendardt 相同的时刻。

    您的问题可能已经理解,但对于任何阅读的人,我想直接声明:DateCalendar 设计不佳且早已过时。除非需要与您无法更改或不想升级的遗留 API 交互,否则不应使用它们。对于所有其他用途,请坚持使用现代 Java 日期和时间 API java.time。

    链接

    【讨论】:

      猜你喜欢
      • 2016-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-27
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多