tl;博士
Instant instant = myResultSet.getObject( … , Instant.class ) ;
…或者,如果您的 JDBC 驱动程序不支持可选的Instant,则需要支持OffsetDateTime:
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
避免使用java.util.Date 和java.sql.Timestamp。它们已被java.time 类取代。具体来说,Instant 类表示UTC 中时间轴上的一个时刻,分辨率为nanoseconds(最多九 (9) 位小数)。
不同的值→未验证的问题
解决问题的主要部分:“当 java.util.Date 和 java.sql.Timestamp 对象从另一个派生时,为什么会有不同的日期?”
您的代码一定有问题。您没有发布您的代码,因此我们无法查明问题所在。
首先,您为 java.util.Date 的值显示的字符串值并非来自其默认的 toString 方法,因此您显然正在执行其他操作。
其次,当我运行类似的代码时,我确实得到了完全相同的日期时间值。
首先创建一个 java.sql.Timestamp 对象。
// Timestamp
long millis1 = new java.util.Date().getTime();
java.sql.Timestamp ts = new java.sql.Timestamp(millis1);
现在提取 count-of-milliseconds-since-epoch 以实例化 java.util.Date 对象。
// Date
long millis2 = ts.getTime();
java.util.Date date = new java.util.Date( millis2 );
将值转储到控制台。
System.out.println("millis1 = " + millis1 );
System.out.println("ts = " + ts );
System.out.println("millis2 = " + millis2 );
System.out.println("date = " + date );
运行时。
millis1 = 1434666385642
ts = 2015-06-18 15:26:25.642
millis2 = 1434666385642
date = Thu Jun 18 15:26:25 PDT 2015
所以问题中显示的代码确实是从 java.sql.Timestamp 转换为 java.util.Date 的有效方法,尽管您将丢失任何 nanoseconds 数据。
java.util.Date someDate = new Date( someJUTimestamp.getTime() );
字符串输出的不同格式
请注意,toString 方法的输出是不同的格式,如文档所述。 java.sql.Timestamp 遵循 SQL 格式,类似于ISO 8601 格式,但中间没有T。
忽略继承
正如在其他答案和问题上的 cmets 中所讨论的,您应该忽略 java.sql.Timestamp 继承自 java.util.Date 的事实。 The j.s.Timestamp doc 明确指出您应该不将一个视为另一个的子类型:(强调我的)
由于上面提到的 Timestamp 类和 java.util.Date 类之间的差异,建议代码不要将 Timestamp 值一般视为 java.util.Date 的实例。 Timestamp 和 java.util.Date 的继承关系实际上是实现继承,而不是类型继承。
如果您忽略 Java 团队的建议并采取这样的观点,那么一个关键问题是 您将丢失数据:任何可能来自数据库丢失,因为 Date 只有 millisecond 分辨率。
基本上,早期 Java 中的所有旧日期时间类都是一团糟:java.util.Date、j.u.Calendar、java.text.SimpleDateFormat、java.sql.Timestamp/.Date/.Time。它们是业界最早在日期时间框架上做出的勇敢努力之一,但最终失败了。具体来说,java.sql.Timestamp 是一个带有纳秒的 java.util.Date;这是一个 hack,不是好的设计。
java.time
避免与早期版本的 Java 捆绑的旧日期时间类。
尽可能使用 Java 8 及更高版本中内置的 java.time package (Tutorial)。
java.time 的基础知识...Instant 是 UTC 时间线上的一个时刻。应用时区 (ZoneId) 以获取 ZonedDateTime。
使用 java.time 从 Java 8 开始的示例代码。使用支持 JDBC 4.2 及更高版本的 JDBC 驱动程序,您可以直接与数据库交换 java.time 类;不需要遗留类。
Instant instant = myResultSet.getObject( … , Instant.class) ; // Instant is the raw underlying data, an instantaneous point on the time-line stored as a count of nanoseconds since epoch.
您可能需要调整到 UTC 以外的时区。
ZoneId z = ZoneId.of( "America/Montreal" ); // Always make time zone explicit rather than relying implicitly on the JVM’s current default time zone being applied.
ZonedDateTime zdt = instant.atZone( z ) ;
执行您的业务逻辑。在这里,我们只是简单地添加一天。
ZonedDateTime zdtNextDay = zdt.plusDays( 1 ); // Add a day to get "day after".
在最后阶段,如果绝对需要,转换为 java.util.Date 以实现互操作性。
java.util.Date dateNextDay = Date.from( zdtNextDay.toInstant( ) ); // WARNING: Losing data (the nanoseconds resolution).
转储到控制台。
System.out.println( "instant = " + instant );
System.out.println( "zdt = " + zdt );
System.out.println( "zdtNextDay = " + zdtNextDay );
System.out.println( "dateNextDay = " + dateNextDay );
运行时。
instant = 2015-06-18T16:44:13.123456789Z
zdt = 2015-06-18T19:44:13.123456789-04:00[America/Montreal]
zdtNextDay = 2015-06-19T19:44:13.123456789-04:00[America/Montreal]
dateNextDay = Fri Jun 19 16:44:13 PDT 2015
转化次数
如果您必须使用遗留类型与尚未针对 java.time 更新的旧代码进行接口,您可以转换。使用添加到旧 java.util.Date 和 java.sql.* 类的新方法进行转换。
Instant instant = myJavaSqlTimestamp.toInstant() ;
……和……
java.sql.Timestamp ts = java.sql.Timestamp.from( instant ) ;
有关转换的更多信息,请参阅教程章节 Legacy Date-Time Code。
小数秒
注意小数秒的分辨率。从纳秒到毫秒的转换意味着可能会丢失一些数据。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。