【问题标题】:Calendar.getInstance with TimeZone vs new Date()Calendar.getInstance 与 TimeZone 与 new Date()
【发布时间】:2019-03-12 06:10:10
【问题描述】:

我在我的应用程序中编写了一个实用程序,它接受一个时区并返回一个日期(java.util.Date),如下所示 -

// Inside MyDateHelper.java
public static java.util.Date getTimeForTimeZone(String timeZoneId)
{
    final Date currentDate = Calendar.getInstance(TimeZone.getTimeZone(timeZoneId)).getTime();
    return currentDate;
}

我在互联网上进行了研究,发现 Date 中没有任何 timeZone 信息。相反,它是一个从 1970 年开始的长值的包装器。所以,我的问题是,我是否必须获取当前的日期时间瞬间,交易已经发生以存储在日期字段(类型为 java.util.Date ), 这个实用程序有什么用吗?

出于所有实际目的,这两行代码实现的效果相同吗?

transactionEntry.setTime(MyDateHelper.getTimeForTimeZone("UTC+7:00"));

transactionEntry.setTime(new Date());

我还检查了新的 Java 8 Time API,但出于我的目的,我认为这样做也有点矫枉过正,没有更多的收获new Date() -

Date date4 = new Date();
LocalDateTime localDate = date4.toInstant().atZone(ZoneId.of("UTC+07:00")).toLocalDateTime();
final ZonedDateTime dateWithZone = localDate.atZone(ZoneId.of("UTC+07:00"));
Date dateOutput = Date.from(dateWithZone.toInstant());
transactionEntry.setTime(dateOutput);

整个目的是使用时区捕获事务发生的时间,但我只能将时间存储为 java.util.Date。无论交易发生在哪个时区,Date 都能正确捕捉到时间,对吧?有人可以确认这种理解是正确的吗?

【问题讨论】:

    标签: java datetime java-8 timezone java-time


    【解决方案1】:

    第一个建议:

        Instant transactionTime = Instant.now();
    

    你是对的,在你的时区,无论是ZonedDateTime还是Calendar,都没有意义,因为你需要的是一个时间戳,一个时间点。 java.time.Instant 正是如此,一个没有时区的时间点(因此是Date 的最直接替代品)。您可能应该尽可能使用现代 Java 日期和时间 API java.time。 DateCalendarTimeZone 不仅早已过时,而且还存在一系列设计问题。使用现代类要好得多。

    如果您的事务入口类在内部使用java.util.Date,并且您现在不想升级其内部逻辑,您仍然可以添加一个接受Instant 的重载setTime 方法。

    public void setTime(Instant time) {
        // Delegate to the old method that accepts a java.util.Date
        setTime(Date.from(time));
    }
    

    下一步可能是标记接受Date 的方法已弃用。

    第二个建议,如果你坚持Date我还是更喜欢:

        Date transactionTime = Date.from(Instant.now());
    

    它与new Date() 的作用相同,但更直接地告诉读者您获得了当前时刻。我看不出这怎么可能不是优势。

    顺便说一句,如果您确实想使用您的时区,您应该使用正确的时区 ID,例如 Antarctica/Davis,而不是偏移量。它再次告诉您的读者更多有关您的意图的信息,并且在某个时间引入夏令时 (DST) 或由于其他原因偏移量发生变化的情况下是面向未来的。

    【讨论】:

    • 谢谢!您的回答再次证实了我的理解和在您面前做出的回答。是的,我同意,只要事件发生需要时间戳,Instant 就应该替换 Date,但恐怕我无法更改基本代码。我认为对于显示特定地点的正确时间和时区的所有目的,我应该使用格式化程序来设置 TimeZone 并且日期将正确打印,是吗?
    • 使用老式类是正确的。使用现代的转换为用户的时区和格式化是两个不同的操作,但是是的,您通常会同时执行这两种操作来显示特定地点的正确时间。
    【解决方案2】:

    Answer by Ole V.V. 是正确的。我将添加一些讨论。

    地球上的每个人都只能同时经历一个时间线(忽略爱因斯坦等人)。按照惯例,我们以 UTC 跟踪该时间。

    学会将 UTC 视为 真正的时间。所有其他偏移量和区域只是 UTC 的变体。当作为程序员或系统管理员工作时,忘记你自己的狭隘时区。在 UTC 中再次单击您的办公桌或墙壁跟踪时间。使用 UTC 进行所有日志记录、跟踪、数据存储和数据交换。

    在 UTC 中跟踪时刻的基本类是 java.time.Instant。根据定义,此类的对象始终采用 UTC。这个类取代了java.util.Date,它也代表了UTC中的一个时刻,但有很多设计缺陷,永远不应该使用。 Instant 的分辨率为纳秒级,比 Date 的毫秒级更精细。

    要获得更大的灵活性,请使用OffsetDateTime 类。

    OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
    

    然后又回来了。

    Instant instant = odt.toInstant() ;
    

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

    要使用时区,请应用ZoneId 以获取ZonedDateTime。这个类取代了可怕的CalendarGregorianCalendar 类。

    ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
    ZonedDateTime zdt = instant.atZoneId( z ) ;
    

    又回来了。

    Instant instant = zdt.toInstant() ;
    

    zdtodtinstant 都代表同一时刻,时间轴上的同一点。

    最后,停止使用DateCalendar。 Sun、Oracle 和 JCP 社区在采用 JSR 310 时都放弃了这些课程,您也应该如此。那些遗留类真的是一团糟。

    【讨论】:

    • 感谢您的回答和确认!我们使用与 Java 8 保持同步的专有软件,但在它的所有 ORM 类上使用 Date 而不是 Instant。我想知道他们是否仍然这样做是为了与数据库中的日期保持一致。
    【解决方案3】:

    但我只能将时间存储为 java.util.Date。

    那么时区无所谓,

    Calendar calendar = Calendar.getInstance();
    
    calendar.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
    Date america = calendar.getTime();
    
    calendar.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    Date shanghai = calendar.getTime();
    
    System.out.println(america.equals(shanghai)); // true
    

    你可以使用:

    transactionEntry.setTime(new Date());
    

    page 将帮助您了解Instant/DateLocalDateTimeZonedDateTime

    我认为您可能会重新考虑设计。为什么只允许java.util.Date

    从描述来看,您似乎确实关心交易发生的时区。而java.util.Date 只是不支持它。这意味着您可以将准确的值(自纪元以来的毫秒数)存储到java.util.Date,但该值不能用准确的日期时间表示,因为时区已丢失。

    您可能需要使用java.time.ZonedDateTime 来实现它。

    【讨论】:

    • 感谢您的确认!我运行了你提到的那段代码,是的,它打印出两个相同的日期。
    • @Anurag 是的,这就是我想说的:即使你通过了 ZoneId,它也没有任何区别
    • 不,永远不要在跟踪特定时刻时使用LocalDateTime。根据定义,该类不能代表片刻。
    猜你喜欢
    • 2011-02-04
    • 2010-09-26
    • 2021-04-09
    • 2018-07-29
    • 1970-01-01
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多