带有TimeZone的Calendar.getInstance与新Date()

时间:2018-10-07 10:24:16

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

我在应用程序中编写了一个实用程序,该实用程序采用时区并向我返回一个日期(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)中的事务的当前Date-Time瞬间。 ),此实用程序有用吗?

出于所有实际目的,两行代码是否达到同一目的?

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

OR

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。无论交易发生在哪个时区,“日期”都将能够正确捕获时间点,对吗?有人可以确认这种理解是正确的吗?

3 个答案:

答案 0 :(得分:4)

第一个建议:

    Instant transactionTime = Instant.now();

您是正确的,在ZonedDateTimeCalendar中时区中的时间都没有意义,当您需要的是时间戳记,时间片刻时。 java.time.Instant就是那个没有时区的时间点(因此是Date的最直接替代)。可能的话,您可能应该使用Java.time(现代的Java日期和时间API)。 DateCalendarTimeZone不仅已经过时,而且还存在一系列设计问题。现代班级好用得多。

如果事务入口类在内部使用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()相同,但是更直接地告诉读者您获得了当前时刻。我看不出这有什么好处。

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

答案 1 :(得分:4)

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

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

学习将UTC视为一个真实的时间。所有其他偏移量和区域仅是UTC的变化。当以程序员或系统管理员的身份工作时,请忘记自己的时区。在UTC上第二次单击您的办公桌或墙壁跟踪时间。在UTC中进行所有日志记录,跟踪,数据存储和数据交换。

用于跟踪UTC时刻的基本类是hashKey。根据定义,此类的对象始终位于UTC中。此类代替rangeKey,该类在UTC中也代表了片刻,但存在许多设计缺陷,因此不应使用。 java.time.Instant的分辨率为纳秒级,比java.util.Date的毫秒级更好。

为获得更大的灵活性,请使用Instant类。

Date

然后再次返回。

OffsetDateTime

与UTC的偏移量仅为数小时-数分钟-秒。时区更多。时区是特定区域的人们过去,现在和将来对偏移量的更改的历史记录。

要使用时区,请应用OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 以获得Instant instant = odt.toInstant() ; 。此类取代了可怕的ZoneIdZonedDateTime类。

Calendar

再次返回。

GregorianCalendar

ZoneId z = ZoneId.of( "Africa/Tunis" ) ; ZonedDateTime zdt = instant.atZoneId( z ) ; Instant instant = zdt.toInstant() ; zdt代表同一时刻,即时间轴上的同一点。

最后,停止使用odtinstant 。 Sun,Oracle和JCP社区在采用JSR 310时都放弃了这些类,您也应该这样做。这些遗留类确实是一个可怕的烂摊子。

答案 2 :(得分: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来实现。