添加1小时到13位数的时间戳

时间:2017-11-09 10:46:32

标签: java collections timestamp

我尝试了下面的代码。

Timestamp timestampDate = scheduled.getInterviewDateAndTime(); //from DB
Map<String, Object> map = new HashMap();    
map.put("eventTitle", "interview with");
map.put("startDateTime", timestampDate);
System.out.println("startDateTime : " + timestampDate);

long addTime = 1*60*60*1000;
timestampDate.setTime(timestampDate.getTime() + TimeUnit.HOURS.toMillis(addTime));
map.put("endDateTime", timestampDate);
System.out.println("endDateTime : " + timestampDate);

这是正确的方法还是有任何好的替代方法?

输出是:

startDateTime : 2017-11-07 09:08:00.0
endDateTime : 2428-07-15 09:08:00.0

如何获得正确的输出?

2 个答案:

答案 0 :(得分:1)

我建议您停止使用已过时的Timestamp课程。如果可以,或者至少你最小化你的使用。我将向您展示这两个选项的代码。 The modern Java date and time API known as java.time or JSR-310可以更好地使用。当谈到时间算术时更是如此,比如在日期时间增加一小时。

java.time

更改getInterviewDateAndTime()以返回InstantInstant是来自java.time的类,它自然取代了旧的Timestamp类。同时更改Map的接收者以接受其中包含Instant个对象的地图。现代版本的JDBC,JPA等,很高兴从数据库中检索Instant个对象并将Instant存储回来。

    Instant instantStart = scheduled.getInterviewDateAndTime(); //from DB
    Map<String, Object> map = new HashMap<>();    
    map.put("eventTitle", "interview with");
    map.put("startDateTime", instantStart);
    System.out.println("startDateTime : " + instantStart);

    Instant instantEnd = instantStart.plus(1, ChronoUnit.HOURS);
    map.put("endDateTime", instantEnd);
    System.out.println("endDateTime : " + instantEnd);

注意事项:代码更自然,更直接地表达了添加一小时的事实。不需要乘法,不需要读者检查你乘以正常的常数,或者你的乘法没有溢出;这一切都得到了照顾。 Instant类是不可变的,因此不存在意外更改已添加到地图中的Instant对象的风险。

在我的例子中,上面的代码打印出来了:

startDateTime : 2017-11-29T09:15:00Z
endDateTime : 2017-11-29T10:15:00Z

时间是UTC。

编辑:正如Basil Bourque在评论中指出的那样,添加一小时也可以这样做:

    Instant instantEnd = instantStart.plus(Duration.ofHours(1));

结果是一样的。

与旧版API一起使用

假设您无法更改getInterviewDateAndTime()的返回类型和/或地图的接收者绝对需要Timestamp个对象。实现此类限制的标准方法是您仍然在自己的代码中使用现代API。收到Timestamp后,您会立即将其转换为Instant。当您需要将Timestamp传递给遗留代码时,只能在最后一刻转换Instant

    Timestamp timestampStart = scheduled.getInterviewDateAndTime(); //from DB
    Instant instantStart = timestampStart.toInstant();
    Map<String, Object> map = new HashMap<>();    
    map.put("eventTitle", "interview with");
    map.put("startDateTime", timestampStart);
    System.out.println("startDateTime : " + timestampStart);

    Instant instantEnd = instantStart.plus(1, ChronoUnit.HOURS);
    Timestamp timestampEnd = Timestamp.from(instantEnd);
    map.put("endDateTime", timestampEnd);
    System.out.println("endDateTime : " + timestampEnd);

您仍然具有上述现代API的大部分优势,但转换时需要额外的几行。此代码已打印

startDateTime : 2017-11-29 10:15:00.0
endDateTime : 2017-11-29 11:15:00.0

时间与上面相同,但是在我的本地时区,因为Timestamp.toString()以这种方式呈现它们(这会让很多人感到困惑)。

问题:我可以在Java版本中使用现代API吗?

如果至少使用Java 6 ,则可以。

  • 在Java 8及更高版本中,内置了新的API。
  • 在Java 6和7中获取the ThreeTen Backport,新类的backport({3}}的ThreeTen)。 TimestampInstant之间的转换略有不同,通过名为DateTimeUtils的类,但并不复杂。其余的都一样。
  • 在Android上,使用Android版的ThreeTen Backport。它被称为ThreeTenABP,我认为在JSR 310中有一个很好的解释。

答案 1 :(得分:0)

这里有几个问题:

  • java.sql.Timestamp(我认为它是什么)是一个可变类,因此在其上设置时间会改变时间戳状态。即使你的println语句不明显,之后调试地图也会在心跳中显示出来。
  • 你计算小时的逻辑是错误的(你在那里乘以两次)
    1. 第一次制作addTime变量。
    2. 第二次使用TimeUnit.toMillis()

当然有几种解决方法:

我喜欢的方式(但是,它需要Java 8或ThreeTen库):

Timestamp start = obtain();
Timestamp end = Timestamp.from(start.toInstant().plus(1, ChronoUnit.HOURS));

它利用将时间戳转换为java.time.Instant对象(或来自ThreeTen的等效版本)的能力,然后使用工厂构造函数将Instant转换为Timestamp它(这是Java 8版本,ThreeTen将在其他类中具有类似的工厂,不在Timestamp上)。与JDK中的旧日期时间/日历类相比,它还在java.time中添加了更清晰的时间计算逻辑。

第二个变体,大致相同,并没有使用所有花哨的东西,但结果也不那么可读(对我来说):

Timestamp start = obtain();
Timestamp end = new Timestamp(start.getTime() + TimeUnit.HOURS.toMillis(1));

正如您所看到的,第二种变体几乎就是您所拥有的,但没有不必要的计算。

请不要手动计算这些值;我们都知道小时假设是60分钟长,一分钟是60秒等等,但读到这个地方很快就会模糊眼睛。你自己先通过先手动计算然后再使用TimeUnit来看到它。当你需要增加一天时,它会更糟糕,因为考虑到夏令时和历史时区,毫不会让你在某个特定时间点没有提取大量关于日长的信息而只添加一天变化。你也知道,并非每一分钟都是60秒,对于那些人也有补偿措施。