首先看一个测试用例:
import org.junit.Assert; import org.junit.Test; import java.sql.Time; import java.sql.Timestamp; import java.util.Date; /** * <p>User: Zhang Kaitao * <p>Date: 13-5-26 下午5:43 * <p>Version: 1.0 */ public class DateTest { //when only millisecond part is different @Test public void testDateAfter() { Date d1 = new Date(1369461400000L); Date d2 = new Date(1369461400001L); Assert.assertTrue(d2.after(d1)); } @Test public void testTimestampAfterOK() { Timestamp d1 = new Timestamp(1369461400000L); Timestamp d2 = new Timestamp(1369461400001L); Assert.assertTrue(d2.after(d1)); } @Test public void testTimestampCastToDateAfterFail() { Date d1 = new Timestamp(1369461400000L); Date d2 = new Timestamp(1369461400001L); Assert.assertFalse(d2.after(d1)); } @Test public void testDateCompare() { Date d1 = new Date(1369461400000L); Date d2 = new Date(1369461400001L); Assert.assertTrue(d2.compareTo(d1) == 1); } @Test public void testTimestampCompareOK() { Timestamp d1 = new Timestamp(1369461400000L); Timestamp d2 = new Timestamp(1369461400001L); Assert.assertTrue(d2.compareTo(d1) == 1); } @Test public void testTimestampCastToDateCompareOK() { Date d1 = new Timestamp(1369461400000L); Date d2 = new Timestamp(1369461400001L); Assert.assertTrue(d2.compareTo(d1) == 1); } }
大家可能看到testTimestampCastToDateAfterFail测试用例,d2.after(d1) 是false。
从网络上找了下,类似的bug如下:
http://bugs.sun.com/view_bug.do?bug_id=5008227
EVALUATION
This is a side effect caused by the 4340146 fix. Because Date.after() no longer calls getTime(), after() and equals() in Timestamp work compare different time values.
Timestamp.after and before should call compareTo which works correctly.
###@###.### 2004-03-15
其也是建议使用compareTo,而不是after/before。
还一篇是在stackoverflow上的:
http://stackoverflow.com/questions/15629222/java-sql-timestamp-comparison-bug
有一个compareTo的,也有过类似的问题,不过1.5已经修复。
http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=676
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103041
其中的主要问题是:
Timestamp没有重载(public boolean after(Date d) );
Date中的fastTime 存储了毫秒值;但Timestamp的fastTime只存储到秒,毫秒值部分存储到nanos部分。具体细节可参考jdk代码。
有细心的朋友可能注意到了:我的d1 和 d2 实际上是Timestamp类型啊
@Test public void testTimestampCastToDateAfterFail() { Date d1 = new Timestamp(1369461400000L); Date d2 = new Timestamp(1369461400001L); Assert.assertFalse(d2.after(d1)); }
那比较的时候,怎么会发生这种事情?而且有朋友还注意到了compareTo就没有这个问题。
首先看下jdk的文档:
注:此类型由 java.util.Date 和单独的毫微秒值组成。只有整数秒才会存储在 java.util.Date 组件中。小数秒(毫微秒)是独立存在的。传递不是 java.sql.Timestamp 实例的对象时,Timestamp.equals(Object) 方法永远不会返回 true,因为日期的毫微秒组件是未知的。因此,相对于 java.util.Date.equals(Object) 方法而言,Timestamp.equals(Object) 方法是不对称的。此外,hashcode 方法使用底层 java.util.Date 实现并因此在其计算中不包括毫微秒。
鉴于 Timestamp 类和上述 java.util.Date 类之间的不同,建议代码一般不要将 Timestamp 值视为 java.util.Date 的实例。Timestamp 和 java.util.Date 之间的继承关系实际上指的是实现继承,而不是类型继承。
此处 可能已经注意到了:
1、Timestamp 和 java.util.Date 之间的继承关系实际上指的是实现继承,而不是类型继承。
2、Timestamp.equals(Object) 方法是不对称的。此外,hashcode 方法使用底层 java.util.Date 实现并因此在其计算中不包括毫微秒。
此处我们大体能概括出来:
1、Date和Timestamp并不是继承关系。。。。。
2、after方法也是双向不对称的。。。。
关于不对称,再来看两个测试用例:
@Test public void testTimestampAfterOK2() { Date d1 = new Timestamp(1369461400000L); Timestamp d2 = new Timestamp(1369461400001L); Assert.assertFalse(d2.after(d1)); } @Test public void testTimestampAfterOK3() { Timestamp d1 = new Timestamp(1369461400000L); Date d2 = new Timestamp(1369461400001L); Assert.assertFalse(d2.after(d1)); }
主要原因是Timestamp没有重载(public boolean after(Date d) );而且仔细思考了下,如果从JDK文档上总结的话,不应该算作bug;但是从compareTo上看那就应该是bug。
真是混乱啊。。。 JSR 310 新的日期和时间API 在JDK8会添加进去
更好的选择是使用如joda-time/或者使用JSR-310。
比较时应该注意自己的情况,如果不知道当前类型(Date/Timestamp)那么请使用compareTo;是什么使用日期,应该要做好单元测试。有了单元测试,才有了保险。。。。
此处如果你的java.sql.Time,JDK也没有提供只比较Time部分的API。。。。。。。
commons-lang也没有提供类似的API,不过commons-lang也在犹豫是否添加:
DateUtils.isAfterDay
https://issues.apache.org/jira/browse/LANG-400
=================分割线==================================================
关于mysql的Timestamp:
假设表结构是:
create table `personal_message`( `id` bigint not null auto_increment, `sender_id` bigint, `receiver_id` bigint, `send_date` timestamp, }
如果有人执行:
update receiver_id=1 where id=?
你可能会发现:你的send_date改成了当前时间!具体原因仔细看mysql官方文档,官方文档说的很明白:
http://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html
因为像send_date发送时间,一旦确定是不需要改的,解决方案是只加个默认值:
`send_date` timestamp default 0,
而且mysql还一个问题是timestamp不是存储到毫秒值,所以如果想存到毫秒值级别 请使用如bigint直接存储毫秒值。
=================分割线==================================================
jpa中映射日期类型,可以使用:
@Temporal(TemporalType.TIMESTAMP) private Date sendDate;
TemporalType表示日期类型,分别对应:
public enum TemporalType { /** * Map as <code>java.sql.Date</code> */ DATE, /** * Map as <code>java.sql.Time</code> */ TIME, /** * Map as <code>java.sql.Timestamp</code> */ TIMESTAMP }
如果想在hibernate中映射其他日期类型,如Calendar:
可以使用hibernate的@org.hibernate.annotations.Type,如@Type(type = "timestamp"),默认支持的是:
Type mappings from java.util.Date and its subclasses to SQL types DATE, TIME and TIMESTAMP (or equivalent).
calendar, calendar_date
Type mappings from java.util.Calendar to SQL types TIMESTAMP and DATE (or equivalent).
http://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html_single/#mapping-types
当然,你也可以选择如joda-time,已经有hibernate集成了:
https://github.com/JodaOrg/joda-time-hibernate
如果你存储到数据库的是毫秒值,取回来想变成日期,可以自定义UserType,这个可以参考:
对于hibernate 写原生SQL时,还需要注意这个问题:《hibernate createSQLQuery的问题》,解决方案是:
如果hibernate4 addScalar("m_apiendtime ",TimestampType.INSTANCE)
其他 addScalar("m_apiendtime ",Hibernate.TIMESTAMP)
如果有些拿不准的,可以考虑上单元测试,好处多多。
相关推荐
jdk1.6 和jdk1.8中文api jdk1.6 和jdk1.8中文api jdk1.6 和jdk1.8中文api jdk1.6 和jdk1.8中文api
jdk api 1.8_中文文档 jdk api 1.8_中文文档 jdk api 1.8_中文文档
JDK 1.8中文API文档
建议官方只收1积分,汉化绝大多数方法与函数只有少数1.7少用方法没有汉化,并含使用案例,jdk api 1.7是一款JAVA1.7中文版的API帮助文档,众所周知JDK是Java语言的软件开发工具包,主要用于移动设备、嵌入式设备上的...
自用最全面,稳妥的文档,jdk 1.9api文档,内部包含中英文,方便查阅。 Java9可以说是一个庞大的系统工程。Java 的方方面面,包括 JDK 编译工具,运行时,Java 公共 API 和私有代码等等,完全做了一个整体改变。 这...
JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 中文 JDK API 1.8 ...
jdk1.9英文版API,小部分内容可能需要联网才能看到,大部分基础都是可以直接查看的。
包含翻译后的API文档:bcprov-jdk15on-1.68-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.bouncycastle:bcprov-jdk15on:1.68; 标签:jdk15on、bouncycastle、bcprov、jar包、java、中文文档; 使用方法:...
本手册为JDK-API-1.8版本,java中文版api手册。JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具...
JDK_API中文版全套离线 JDK_API中文版全套离线 JDK_API中文版全套离线
JDK_API_1.8(中文版)JDK_API_1_8_zh_CN.zip
jdk8中文API文档
JDK1.6API。java1.6开发文档,最新官网文档。满足java开发需求
本版本修复 JDK的Bug1–数组切割 JDK的Bug2–三元运算符 JDK中不算Bug的Bug–ArrayList可通过构造函数传入非指定泛型的List并在get时出错
jdk 1.9 api 包含中文,英文版,有需要的可以下载。。。
JDK1.8和JDK1.6API帮助文档
JDK API 1.6 中文版........
jdk api 1.8_google.CHM亲测双击即可打开。之前在网上下载了很多,要么双击打开报错,要么打开后里面是空白,终于找到一款好用的chm jdk api文档,跟大家分享下。
JDK1.8 API 中文 谷歌翻译版 java帮助文档 JDK API java 帮助文档 谷歌翻译 JDK1.8 API 谷歌百度翻译版 java帮助文档 Java最新帮助文档 本帮助文档是使用谷歌翻译,非人工翻译。准确性不能保证,请与英文版配合使用
jdk1.8中文api压缩包jdk1.8中文api压缩包jdk1.8中文api压缩包jdk1.8中文api压缩包