我尝试了下面的代码。
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
如何获得正确的输出?
答案 0 :(得分:1)
我建议您停止使用已过时的Timestamp
课程。如果可以,或者至少你最小化你的使用。我将向您展示这两个选项的代码。 The modern Java date and time API known as java.time
or JSR-310可以更好地使用。当谈到时间算术时更是如此,比如在日期时间增加一小时。
java.time
更改getInterviewDateAndTime()
以返回Instant
。 Instant
是来自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));
结果是一样的。
假设您无法更改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 6 ,则可以。
Timestamp
和Instant
之间的转换略有不同,通过名为DateTimeUtils
的类,但并不复杂。其余的都一样。答案 1 :(得分:0)
这里有几个问题:
java.sql.Timestamp
(我认为它是什么)是一个可变类,因此在其上设置时间会改变时间戳状态。即使你的println语句不明显,之后调试地图也会在心跳中显示出来。addTime
变量。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秒,对于那些人也有补偿措施。