Calendar.getInstance 使用 TimeZone 与 new Date()

Calendar.getInstance with TimeZone vs new Date()

我在我的应用程序中编写了一个实用程序,它接受一个时区和 returns 我一个日期 (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 中没有任何时区信息。相反,它是从 1970 年开始的一个 long 值的包装器。所以,我的问题是,如果我必须获取当前的日期时间即时发生事务以存储在日期字段(类型 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.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());

这篇将帮助您理解Instant/DateLocalDateTimeZonedDateTime

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

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

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

第一个建议:

    Instant transactionTime = Instant.now();

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

如果您的交易条目 class 内部使用 java.util.Date 并且您现在不想升级其内部逻辑,您仍然可以添加一个重载的 setTime 方法来接受一个Instant.

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() 的作用相同,但更直接地告诉 reader 您获得了当前时刻。我看不出这怎么可能不是优势。

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

是正确的。我将添加一些讨论。

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

学习将 UTC 视为 唯一真实时间。所有其他偏移量和区域都只是 UTC 的变体。当作为程序员或系统管理员工作时,忘掉你自己狭隘的时区吧。再次点击您的桌面或墙上的 UTC 跟踪时间。在 UTC 中执行所有日志记录、跟踪、数据存储和数据交换。

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

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

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

又回来了。

Instant instant = odt.toInstant() ;

与 UTC 的偏移量仅仅是小时-分钟-秒的数字。时区要多得多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。

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

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

又要回去了。

Instant instant = zdt.toInstant() ;

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

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