关于 Java 的 Date 类和 getTime() 函数的困惑



我是Java的Date类的新手。当我尝试使用它的getTime()函数来计算时差时,问题就出现了。例如,下面是代码。


Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
task = opt.get();
task.setEndDate(dateFormat.format(date));
Date startDate = null;
try {
startDate = dateFormat.parse(task.getStartDate());
} catch (ParseException e) {
System.out.println("date parsing error...");
startDate = date;
}
System.out.printf("Start date is: %s", task.getStartDate());
System.out.printf("Start date is: %d", startDate.getTime());
System.out.printf("End date is: %s", task.getEndDate());
System.out.printf("End date is: %d", date.getTime());
long diff = date.getTime() - startDate.getTime() - 43200000;
System.out.printf("Time difference is: %d", diff);
int secNum = (int)(diff / 1000);
String timeCost = String.valueOf(secNum);
System.out.println("Time cost(sec) is:");
System.out.println(timeCost);
task.setTimeCost(timeCost);

输出为:

Start date is: 2020-04-15 01:46:17
Start date is: 1586929577000
End date is: 2020-04-15 01:46:35
End date is: 1586972795461
Time difference is: 18461
Time cost(sec) is:18

正如您可能注意到的,通过"date.getTime()-startDate.getTime)",计算出的差异和实际差异之间有12小时(43200000毫秒)的偏移量。

我不知道发生了什么事。有人有主意纠正我吗?

您似乎将日期/时间作为字符串存储在task对象中,并使用格式"yyyy-MM-dd hh:mm:ss"在date和string之间转换。我相信小写h表示您使用的是12小时时钟,但您的格式字符串中不包含AM/PM指示器。

我猜您是在下午1点46分运行代码来生成示例输出的。

您已将"2020-04-15 01:46:17"存储为开始日期。当您将其转换回日期时,格式化程序不知道它是上午时间还是下午时间。我想它默认为AM.

然而,Date对象知道它是用PM时间初始化的。因此,当你减去两者时,你会得到超过12小时的差异,因为它是从下午1:46:35减去凌晨1:46:17

一个简单的建议是在日期格式中添加AM/PM指示器,或者使用24小时时钟(格式字符串中的大写H)。

更好的建议是将日期存储为日期,而不是字符串当您想要显示它们时,将它们转换为字符串。

您使用的是12小时格式的hh,因此20:00变为08:00。您应该使用24小时格式的HH。以下说明了差异。

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Date date = new Date(1586973600000L);
System.out.println(date);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String fd1 = df.format(date);
System.out.println(fd1);
System.out.println(df.parse(fd1));
df.applyPattern("yyyy-MM-dd HH:mm:ss");
String fd2 = df.format(date);
System.out.println(fd2);
System.out.println(df.parse(fd2));

此外,java.util.Date是旧的,有缺陷的,现在通常会避免一段时间。您可能需要切换到java.time

java.time

我是Java的Date类的新手。

停止!倒车,快退。

java.util.Datejava.sql.Date类都是可怕的,存在严重缺陷,并且非常令人沮丧永远不要使用这些类

这些类是在最早的Java版本中提供的。几年前由JSR310中定义的现代java.time类提供。


Date date = new Date();

要捕捉UTC中的当前时刻,请使用Instant.now。使用比它所替换的java.util.Date类中使用的毫秒更精细的分辨率。

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

task.setEndDate(dateFormat.format(date));

Task类应该包含一个java.time对象,而不仅仅是一个字符串。

class Task {
Instant start , stop ;
…
}

在整个Java代码库中使用智能对象而不是哑字符串。这样做可以确保有效的值,提供类型安全性,并使代码更加自我文档化。

如果您的Task类似于未来的预约,无论时区使用的偏移量如何变化,您都希望一天中的某个时间,那么请使用LocalDateTime。这种类型只表示一天中的日期和时间,但没有任何时区或偏移量的概念。

LocalDate ld = LocalDate.of( 2020 , Month.APRIL , 15 ) ;
Localtime lt = LocalTime.of( 15 , 30 ) ;
LocalDateTime ldt = LocalDateTime.of( ld , lt ) ;

在生成需要时间线上特定点的日历时,请应用相关时区。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;

这里的关键问题是,世界各地的政治家都表现出了改变其管辖范围内时区使用的偏移量的偏好。政客们这样做的频率惊人。他们这样做几乎没有预警。

当以文本方式与其他系统交换日期-时间值时,请使用ISO 8601格式。在java.time中,解析/生成文本时默认使用这些格式。为了向用户演示,使用DateTimeFormatter自动生成本地化字符串。


新的SimpleDateFormat("yyyy-MM-dd hh:MM:ss")

如果您试图记录时间线上的特定时刻,则此格式不正确。您必须包含时区和/或UTC偏移量的指示才能跟踪时刻。

对于力矩,请使用上述ISO 8601格式。默认情况下使用,因此无需指定格式模式。

String input = "2020-01-23T01:23:45.123456789Z" ;
Instant instant = Instant.parse( input ) ;

将UTC调整为特定地区(时区)的人使用的挂钟时间。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

生成本地化文本。

Locale locale = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale ) ;
String output = zdt.format( f ) ;

请在IdeOne.com上实时查看此代码。

zdt.toString():2020-01-22T20:23:45.123456789-05:00[美国/蒙特利尔]

输出:2020年1月22日至20小时23分钟45秒东部正常时间


long diff = date.getTime() - startDate.getTime() - 43200000;

无需自己计算。我们有一个课程:Duration

Duration d = Duration.between( start , stop ) ;

如果您希望在整个时间跨度内计数整秒,请调用Duration::toSeconds

long seconds = d.toSeconds() ;  // Entire duration in terms of whole seconds.

关于java.time

java.time框架构建在Java8及更高版本中。这些类取代了诸如java.util.DateCalendar、&CCD_ 26。

要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR310。

现在处于维护模式的JodaTime项目建议迁移到java.Time类。

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,也不需要java.sql.*类。休眠5&JPA 2.2支持java.time.

从哪里获得java.time类?

  • Java SE 8Java SE 9Java SE 10爪哇SE 11及更高版本-标准Java API的一部分,带有捆绑实现。
    • Java9添加了一些小功能和修复程序
  • Java SE 6Java SE 7
    • 大多数Java.time功能都是向后移植到Java 6&7英寸ThreeTen背包
  • Android
    • java.time类的Android捆绑包的后续版本
    • 对于早期的Android(<26),ThreeTenABP项目适用于ThreeTen Backport(如上所述)。请参阅如何使用ThreeTenABP…

ThreeTen Extra项目通过附加类扩展java.time。这个项目是将来可能添加到java.time的试验场。您可以在这里找到一些有用的类,如IntervalYearWeekYearQuarter等等。

最新更新