如果我对恰好是java.util.Date
的变量使用toInstant()
java.sql.Date
,我会得到UnsupportedOperationException
。
try {
java.util.Date input = new java.sql.Date(System.currentTimeMillis());
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} catch (UnsupportedOperationException e) {
// grrr!
}
我关注的java.util.Date
来自mysql数据库中通过旧版API的DATE字段,实际上是java.sql.Date
。
现在,以下相关问题非常有趣:
UnsupportedOperationException - Why can't you call toInstant() on a java.sql.Date?
Convert java.util.Date to java.time.LocalDate
LocalDate to java.util.Date and vice versa simplest conversion?
但他们没有提供截断java.util.Date
的任何优雅方法来摆脱时间组件并获得Java 8 LocalDate
。
我承认存在一个问题,即一个时区中的同一时刻可能与另一个时区中的同一时刻不同。
我怀疑解决方案将涉及java.util.Calendar,而不是制定我自己的解决方案,而是建立其他人先做的事情。
我更愿意找到比这更短的东西:
来自Resetting the time part of a timestamp in 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
答案 0 :(得分:3)
如果已知input
变量是java.sql.Date
,那么您只需将其转换为toLocalDate()
方法:
LocalDate date = ((java.sql.Date) input).toLocalDate();
很遗憾,您无法在toInstant()
上致电java.sql.Date
,因为according to javadoc,它始终会引发UnsupportedOperationException
。
如果您不知道类型(可以是java.util.Date
或java.sql.Date
),则可以使用getTime()
方法返回的值来构建{{1然后将其转换为时区(下面我使用的是JVM的默认值),最后从中获取本地日期:
Instant
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();
方法获取日期部分(日/月/年),忽略其余部分,因此无需截断它:如果时间是午夜,上午10点或任何时间都无关紧要在一天中的其他时间,toLocalDate()
将忽略它并仅获取日期部分。
如果您确实想将时间设置为午夜,则可以使用with
method并将LocalTime
传递给它:
toLocalDate()
但正如我所说,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()
将是相同的。)
您还可以检查日期类型并相应地选择相应的操作,如下所示:
LocalDate
您可以根据需要使用任何其他时区,而不是使用JVM默认时区(if (input instanceof java.sql.Date) {
date = ((java.sql.Date) input).toLocalDate();
} else {
date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
),方法是调用ZoneId.systemDefault()
,其中区域名称是任何有效{{3} (始终采用ZoneId.of("zoneName")
格式,如Region/City
或America/New_York
)。
避免使用3个字母的缩写(例如Europe/London
或CET
),因为它们是IANA timezones names。
您可以致电PST
获取可用时区列表(并选择最适合您系统的时区)。如果需要,您还可以继续使用JVM默认时区,但要提醒它ambiguous and not standard,因此最好始终明确指出您正在使用的时区。
答案 1 :(得分: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();
}
答案 2 :(得分:2)
myResultSet.getObject( … , LocalDate.class )
使用java.sql.Date
,java.util.Date
和Calendar
等旧版日期时间类时,您的问题就开始了。完全避免这些课程。它们现在已经遗留下来,取而代之的是java.time类。
您说该值始于DATE
类型的MySQL列中的存储值。该类型仅限日期,没有时间。所以你通过使用错误的类不必要地引入了一个时间值。
使用符合JDBC 4.2或更高版本的JDBC驱动程序,使用java.time类与数据库交换值。
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
并传递给PreparedStatement
。
myPstmt.setObject( … , myLocalDate ) ;
答案 3 :(得分:2)
我不是为什么,但是这段代码对我有用:)
LocalDate ld = new java.util.Date(personEntity.getBirthday()。getTime())。toInstant() .atZone(ZoneId.systemDefault())。toLocalDate();
这不起作用:
LocalDate ld = personEntity.getBirthday()。getTime()。toInstant() .atZone(ZoneId.systemDefault())。toLocalDate();
答案 4 :(得分:1)
试试这个。
java.util.Date input = new java.sql.Date(System.currentTimeMillis());
Instant instant = Instant.ofEpochMilli(input.getTime());
LocalDate date = instant .atZone(ZoneId.systemDefault()).toLocalDate();
答案 5 :(得分:0)
您是否尝试使用SimpleDateFormat来帮助您?
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
public class Utils {
private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
public static void main( String[] args ) {
LocalDate ld = sqlDateToLocalDate( new java.sql.Date( System.currentTimeMillis() ) );
System.out.println( ld );
}
public static LocalDate sqlDateToLocalDate( java.sql.Date sqlDate ) {
try {
Date d = sdf.parse( sdf.format( sqlDate ) );
return d.toInstant().atZone( ZoneId.systemDefault() ).toLocalDate();
} catch ( ParseException exc ) {
}
return null;
}
}
答案 6 :(得分:0)
您在java.sql.Date
和java.util.Date
之间交叉的前提是不正确的。
如果我在一个恰好是java.sql.Date的变量上使用java.util.Date ...
您违反了课程文档中规定的合同。您被告知忽略java.sql.Date
是java.util.Date
的子类的事实。
但解决方案可能很简单。
ZonedDateTime zdt = myJavaSqlDate.toLocalDate().atStartOfDay( ZoneId.of( "America/Montreal" ) ) ;
显然你手头有java.sql.Date
。该类表示没有时间且没有时区的仅日期值。至少那是那个班级的意图。一个悲惨的设计选择来自java.util.Date
的子类,尽管有名称,它代表一个日期和一个UTC时间。这种继承关系是一种黑客,一种糟糕的黑客攻击,他们将内部时间设置为" 00:00:00"。文档清楚地指示我们忽略这种继承关系,并忽略了拥有一个时间组件的事实,并假装两个Date
类是无关的。但是许多人如作者并没有仔细阅读该文档,并根据类名进行了大量假设。
两个Date
类都是可怕混乱的一部分,这是最早版本的Java附带的日期时间框架。这些已被Java 8及更高版本中内置的java.time类所取代。
遇到旧的日期时间对象时的第一步:使用添加到旧类的新方法转换为java.time。
将我们的假日期对象java.sql.Date
转换为java.util.LocalDate
类型的真正仅日期对象。 “本地”字表示没有时区或偏离UTC。
LocalDate ld = myJavaSqlDate.toLocalDate() ;
显然,您希望将该仅限日期的值转换为具有某个时间的日期。也许你想要一天的第一时刻。确定第一个时刻需要一个时区。例如,Europe/Paris
中的新日期早于America/Montreal
,而Asia/Kolkata
中更早。
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;