提问者:小点点

将 java.util.Date 截断为本地日期 *不带* 到实例(),因为 java.sql.Date 给出了不受支持的操作异常


如果我在一个恰好是< code>java.sql.Date的变量上使用< code>java.util.Date的< code>toInstant(),我会得到一个< code > UnsupportedOperationException 。

try {
    java.util.Date input = new java.sql.Date(System.currentTimeMillis());
    LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
    // grrr!
}

我关注的< code>java.util.Date来自mysql DB中的一个日期字段,通过一个遗留API,实际上是一个< code>java.sql.Date。

现在以下相关问题都非常有趣:

不支持的操作例外 - 为什么不能在 java.sql.Date 上调用”

将java.util.Date转换为java.time.LocalDate

将LocalDate设置为java.util。日期,反之亦然,最简单的转换?

但是它们没有提供任何优雅的方法来截断java.util.Date以摆脱时间组件并获得Java8LocalDate

我承认有一个问题,一个时区的同一时刻可能与另一时区的相同时刻的日期不同。

我怀疑这个解决方案会涉及到java.util.Calendar,但是与其精心设计我自己的解决方案,我宁愿先确定别人已经做了什么。

我更喜欢找比这个短的:

在Java中重置时间戳的时间部分:

Date date = new Date();                      // timestamp now
Calendar cal = Calendar.getInstance();       // get calendar instance
cal.setTime(date);                           // set cal to date
cal.set(Calendar.HOUR_OF_DAY, 0);            // set hour to midnight
cal.set(Calendar.MINUTE, 0);                 // set minute in hour
cal.set(Calendar.SECOND, 0);                 // set second in minute
cal.set(Calendar.MILLISECOND, 0);            // set millis in second
Date zeroedDate = cal.getTime();             // actually computes the new Date

共3个答案

匿名用户

通常最简单的解决方案最难找到:

public LocalDate convertDateObject(java.util.Date suspectDate) {

    try {
        // Don't do this if there is the smallest chance 
        // it could be a java.sql.Date!
        return suspectDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

    } catch (UnsupportedOperationException e) {
        // BOOM!!
    }

    // do this first:
    java.util.Date safeDate = new Date(suspectDate.getTime());

    return safeDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

}

匿名用户

如果已知< code>input变量是一个< code>java.sql.Date,那么您可以简单地转换它并调用< code>toLocalDate()方法:

LocalDate date = ((java.sql.Date) input).toLocalDate();

不幸的是,您不能在java.sql上调用toInstant()。日期,因为根据javadoc,它总是抛出UnsupportedOperationException

如果您不知道类型(它可以是java.util.Datejava.sql.Date),您可以使用getTime()方法返回的值来构建Instant,然后将其转换为时区(下面我使用JVM的默认值),最后从中获取本地日期:

LocalDate date = Instant
    // get the millis value to build the Instant
    .ofEpochMilli(input.getTime())
    // convert to JVM default timezone
    .atZone(ZoneId.systemDefault())
    // convert to LocalDate
    .toLocalDate();

toLocalDate()方法获取日期部分(日/月/年),忽略其余部分,因此无需截断它:无论时间是午夜、上午10点还是一天中的任何其他时间,toLocalDate()都会忽略它并只获取日期部分。

但是,如果您真的想将时间设置为午夜,您可以使用< code>with方法并向其传递一个< code>LocalTime:

LocalDate date = Instant
    // get the millis value to build the Instant
    .ofEpochMilli(input.getTime())
    // convert to JVM default timezone
    .atZone(ZoneId.systemDefault())
    // set time to midnight
    .with(LocalTime.MIDNIGHT)
    // convert to LocalDate
    .toLocalDate();

但正如我所说的,toLocalDate()方法只会忽略时间部分,因此在这种情况下不需要设置时间(LocalDatewill be same)。

您还可以检查日期类型并选择相应的操作,如下所示:

if (input instanceof java.sql.Date) {
    date = ((java.sql.Date) input).toLocalDate();
} else {
    date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}

除了使用JVM默认时区(< code > zoneid . system default()),您还可以根据需要使用任何其他时区,方法是调用< code > zoneid . of(" zone name "),其中时区名称是任何有效的IANA时区名称(格式始终为< code>Region/City,如< code>America/New_York或< code>Europe/London)。避免使用3个字母的缩写(如< code>CET或< code>PST),因为它们不明确且不标准。

您可以通过调用ZoneId.get可用区Ids()来获取可用时区列表(并选择最适合您系统的时区)。如果需要,您也可以继续使用JVM默认时区,但要提醒一下,即使在运行时也可以在不另行通知的情况下进行更改,因此最好始终明确您正在使用哪个时区。

匿名用户

myResultSet.getObject( … , LocalDate.class ) 

您的麻烦始于使用遗留的日期时间类,如java.sql.Datejava.util.DateCalendar。完全避免这些类。它们现在是遗留的,被java.time类取代。

您说该值以DATE类型的MySQL列中的存储值开始。该类型是仅日期的,没有一天中的时间。因此,您通过使用错误的类不必要地引入了一天中的时间值。

使用符合JDBC 4.2或更高版本的JDBC驱动程序,使用java.time类与数据库交换值。

LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;

并传递给PreparedStatement

myPstmt.setObject( … , myLocalDate ) ;