概述
在这个简短教程中,我们将探讨将java.util.Date
转换为java.sql.Date
的各种策略。首先,我们会介绍标准转换方法,然后探讨被认为是最佳实践的一些替代方案。
1. 引言
*java.util.Date和
java.sql.Date`这两个日期类分别用于特定场景,它们属于Java标准包的不同部分:
-
java.util
包是JDK的一部分,包含各种实用类以及日期和时间功能。 -
java.sql
包是JDBC API的一部分,自Java 7开始,默认包含在JDK中。
java.util.Date
表示时间的特定瞬间,具有毫秒精度:
java.util.Date date = new java.util.Date();
System.out.println(date);
// Wed Mar 27 08:22:02 IST 2015
而java.sql.Date
是围绕毫秒值的轻量级封装,它允许JDBC驱动程序识别这是SQL DATE类型的值。这个类的值只是一个具体日期的年、月和日,以从Unix纪元开始的毫秒计算。任何比天更细的时间信息都将被截断:
long millis=System.currentTimeMillis();
java.sql.Date date = new java.sql.Date(millis);
System.out.println(date);
// 2015-03-30
2. java.util.Date
与java.sql.Date
比较
3. 转换的必要性
虽然java.util.Date
的使用更为普遍,但在Java应用程序与数据库通信时,会使用java.sql.Date
。因此,在这些情况下需要进行转换。
显式的类型转换(如引用转换)也不可行,因为我们处理的是完全不同的类层次结构:没有向下或向上转换的选项。尝试将这些日期之一转换为另一种,我们将收到一个ClassCastException
:
java.sql.Date date = (java.sql.Date) new java.util.Date() // not allowed
4. 如何转换为java.sql.Date
我们将探讨几种从java.util.Date
到java.sql.Date
的转换方法。
4.1. 标准转换
如前所见,java.util.Date
包含时间信息,而java.sql.Date
不包含。我们通过使用java.sql.Date
的构造方法进行丢失精度的转换,该方法接受从Unix纪元开始的毫秒表示的时间输入:
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
实际上,由于时区差异,表示值的时间部分的丢失可能会导致报告的日期不同:
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
java.util.Date date = isoFormat.parse("2010-05-23T22:01:02");
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
System.out.println(sqlDate);
// This will print 2010-05-23
TimeZone.setDefault(TimeZone.getTimeZone("Rome"));
sqlDate = new java.sql.Date(date.getTime());
System.out.println(sqlDate);
// This will print 2010-05-24
出于这个原因,我们可能希望考虑下一节中将要讨论的转换替代方案。
4.2. 使用java.sql.Timestamp
而非java.sql.Date
第一个可考虑的替代方案是使用java.sql.Timestamp
类而不是java.sql.Date
。这个类也包含时间信息:
java.sql.Timestamp timestamp = new java.sql.Timestamp(date.getTime());
System.out.println(date); //Mon May 24 07:01:02 CEST 2010
System.out.println(timestamp); //2010-05-24 07:01:02.0
当然,如果查询数据库中的DATE类型列,这个解决方案可能不合适。
4.3. 使用java.time
包中的类
第二个也是最好的替代方案是将两个类转换为java.time
包提供的新类。这种转换的前提是使用JDBC 4.2(或更高版本)。JDBC 4.2于2014年3月随着Java SE 8的发布发布。
从Java 8开始,早期Java版本提供的日期时间类的使用已不再推荐,取而代之的是新的java.time
包提供的类。这些增强的类适用于所有日期/时间需求,包括通过JDBC驱动程序与数据库通信(/java-jdbc)。
如果我们采用这种方法,java.util.Date
应转换为java.time.Instant
:
Date date = new java.util.Date();
Instant instant = date.toInstant().atZone(ZoneId.of("Rome");
而java.sql.Date
应转换为java.time.LocalDate
:
java.sql.Date sqlDate = new java.sql.Date(timeInMillis);
java.time.LocalDate localDate = sqlDate.toLocalDate();
java.time.Instant
类可用于映射SQL DATETIME列,而java.time.LocalDate
可用于映射SQL DATE列。
例如,现在让我们生成一个带有时区信息的java.util.Date
:
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
Date date = isoFormat.parse("2010-05-23T22:01:02");
接下来,我们可以从java.util.Date
生成一个LocalDate
:
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
java.time.LocalDate localDate = date.toInstant().atZone(ZoneId.of("America/Los_Angeles")).toLocalDate();
Asserts.assertEqual("2010-05-23", localDate.toString());
然后,如果我们切换默认时区,LocalDate
的值将保持不变:
TimeZone.setDefault(TimeZone.getTimeZone("Rome"));
localDate = date.toInstant().atZone(ZoneId.of("America/Los_Angeles")).toLocalDate();
Asserts.assertEqual("2010-05-23", localDate.toString())
这按预期工作,因为我们在转换过程中明确指定了时区。
5. 关于从java.sql.Date
转换为java.util.Date
我们已经了解到如何将java.util.Date
转换为java.sql.Date
。有时,我们只有java.sql.Date
或java.sql.Timestamp
实例,但想要将其转换为java.util.Date
。
实际上,我们不需要将java.sql.Date
转换为java.util.Date
。这是因为java.sql.Date
和java.sql.Timestamp
都是java.util.Date
的子类。因此,java.sql.Date
或java.sql.Timestamp
本身就是java.util.Date
。
接下来,让我们在一个测试中验证这一点:
java.util.Date date = UtilToSqlDateUtils.createAmericanDate("2010-05-23T00:00:00");
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
Assertions.assertEquals(date, sqlDate);
java.util.Date dateWithTime = UtilToSqlDateUtils.createAmericanDate("2010-05-23T23:59:59");
java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(dateWithTime.getTime());
Assertions.assertEquals(dateWithTime, sqlTimestamp);
然而,有些情况下,我们想根据给定的java.sqlDate
或java.sql.Timestamp
实例创建一个新的java.util.Date
对象。为了实现这一点,我们可以利用java.sql.Date
或java.sql.Timestamp
类的getTime()
方法来创建一个新的java.util.Date
对象:
java.util.Date date = UtilToSqlDateUtils.createAmericanDate("2010-05-23T00:00:00");
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
java.util.Date newDate = new Date(sqlDate.getTime());
Assertions.assertEquals(date, newDate);
java.util.Date dateWithTime = UtilToSqlDateUtils.createAmericanDate("2010-05-23T23:59:59");
java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(dateWithTime.getTime());
java.util.Date newDateWithTime = new Date(sqlTimestamp.getTime());
Assertions.assertEquals(dateWithTime, newDateWithTime);
6. 总结
在这篇教程中,我们了解了如何从标准的java.util.Date
转换到java.sql
包提供的日期。除了标准转换,我们还探讨了两种替代方案。第一个使用Timestamp
,第二个依赖于更新的java.time
类。
如往常一样,文章的完整源代码可以在GitHub上找到。