有没有一种简单的方法将 Java 8 中引入的 LocalDate
转换为 java.util.Date
对象?
我所说的“简单”是指比这更简单的方法:
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
这对我来说有些别扭。
由于我们只对日期部分感兴趣,而且两个对象中都没有时区信息,为什么要明确引入时区?应该隐式地采用午夜时间和系统默认时区进行转换。
有没有一种简单的方法将 Java 8 中引入的 LocalDate
转换为 java.util.Date
对象?
我所说的“简单”是指比这更简单的方法:
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
这对我来说有些别扭。
由于我们只对日期部分感兴趣,而且两个对象中都没有时区信息,为什么要明确引入时区?应该隐式地采用午夜时间和系统默认时区进行转换。
有没有一种简单的方法将Java 8中引入的LocalDate转换为java.util.Date对象? '简单'的意思是比这个更简单。
没有。您已经正确地完成了操作,并且尽可能简洁。
java.util.Date.from( // Convert from modern java.time class to troublesome old legacy class. DO NOT DO THIS unless you must, to inter operate with old code not yet updated for java.time.
myLocalDate // `LocalDate` class represents a date-only, without time-of-day and without time zone nor offset-from-UTC.
.atStartOfDay( // Let java.time determine the first moment of the day on that date in that zone. Never assume the day starts at 00:00:00.
ZoneId.of( "America/Montreal" ) // Specify time zone using proper name in `continent/region` format, never 3-4 letter pseudo-zones such as “PST”, “CST”, “IST”.
) // Produce a `ZonedDateTime` object.
.toInstant() // Extract an `Instant` object, a moment always in UTC.
)
java.util.Date
→ java.time.LocalDate
请注意,java.util.Date
是一个误称,因为它代表了一个带有时间和日期的 UTC 时间。相比之下,LocalDate
类表示仅包含日期值而不包含时间和时区。
从 java.util.Date
转换到 java.time 意味着转换为等效的 java.time.Instant
类。Instant
类表示时间线上的瞬间,在 UTC 时区内,精度为 纳秒(小数部分最多九位数字)。
Instant instant = myUtilDate.toInstant();
LocalDate
类表示仅包含日期而不包含时间和时区的值。
时区对于确定日期非常重要。对于任何给定的时刻,日期因时区而异。例如,在法国巴黎午夜过后几分钟是新的一天,而在加拿大魁北克省蒙特利尔仍然是“昨天”。
因此,我们需要将那个Instant
转换为一个时区。我们使用ZoneId
来获取一个ZonedDateTime
。
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
从那里开始,要求仅日期,即LocalDate
。
LocalDate ld = zdt.toLocalDate();
java.time.LocalDate
→ java.util.Date
如果要从java.time.LocalDate
转换为java.util.Date
,意味着我们从仅包含日期的对象转换为日期和时间的对象。因此,我们必须指定一天中的某个时间。您可能想选择一天中的第一时刻作为转换后的时间。但是,请不要假设这个时间就是00:00:00
。由于夏令时等原因,第一时刻可能是其他时间,例如01:00:00
。让java.time通过调用atStartOfDay
方法来决定该值。
ZonedDateTime zdt = myLocalDate.atStartOfDay( z );
Instant
。Instant instant = zdt.toInstant();
通过调用 from(Instant)
将那个Instant
转换为java.util.Date
。
java.util.Date d = java.util.Date.from( instant );
java.time 框架是内置于 Java 8 及更高版本中的。这些类替代了老旧且麻烦的 传统 日期时间类,如 java.util.Date
、Calendar
和 SimpleDateFormat
。
要了解更多,请查看Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
您可以直接使用与您的数据库兼容的JDBC 4.2或更高版本的JDBC驱动程序,无需使用字符串或java.sql.*
类。Hibernate 5和JPA 2.2支持java.time对象。
如何获取java.time类?
ThreeTen-Extra 项目扩展了 java.time 的附加类。该项目是可能未来添加到 java.time 的试验场。你可以在这里找到一些有用的类,例如 Interval
、YearWeek
、YearQuarter
和 更多。
免责声明:虽然下面的答案可行,但不建议在生产代码中使用。在这种情况下,应该遵循Basil的答案。
实际上是有方法的。在java.sql.Date
对象中有一个静态方法valueOf,正好可以做到这一点。因此我们有:
java.util.Date date = java.sql.Date.valueOf(localDate);
就是这样。没有明确设置时区,因为本地时区被隐式地采用。
来自文档:
提供的 LocalDate 被解释为本地时区中的本地日期。
java.sql.Date
是 java.util.Date
的子类,因此结果也是一个 java.util.Date
。
对于反向操作,java.sql.Date
类中有一个 toLocalDate 方法。所以我们有:
LocalDate ld = new java.sql.Date(date.getTime()).toLocalDate();
java.sql.Date
只应在JDBC上下文中使用。最佳答案请查看@Basil Bourque的回答。 - Meno HochschildvalueOf
方法并没有避开时区问题,它只是在将时间设置为午夜时,悄悄地应用JVM当前默认时区来隐藏这个问题。因此,“没有时区,没有复杂性”的说法是不正确的:确实涉及到了时区,也确实有复杂性,你只是选择忽略它们。Java 8引入了java.time类库,它们是有原因存在的,建议使用它们。 - Basil Bourquejava.time.*
库的作者,我对有40多个人认为这篇文章是正确答案感到遗憾。正确答案在这里和下面的Basil的回答中。使用 java.sql.Date
是一个可怕的hack,正如其他评论所述,这种方法隐藏了时区转换,这是一个不好的事情。多年来已经知道 java.sql.Date
不应该扩展 java.util.Date
。最后,在 Java 9 中使用模块时,java.sql
类将成为单独的依赖项,因此使用这种 hack 将产生后果。 - JodaStephenjava.sql.Dates
,这些日期可以轻松地被不知情的中间类向下转换为java.util.Date
,然后当您调用java.util.Date::toInstant()
时,您会从java.sql.Date
得到一个UnsupportedOperationException
:( - Adam日期 -> 本地日期:
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
将LocalDate转换为Date:
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
java.util.Date
实际上是一个 java.sql.Date
,那么这将会给你一个 UnsupportedOperationException
异常。 - Adam将LocalDateTime转换为java.util.Date
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneOffset.systemDefault());
Instant instant = zonedDateTime.toInstant();
Date date = Date.from(instant);
System.out.println("Result Date is : "+date);
将日期转换为本地日期
Date date = new Date();
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate转换为Date
LocalDate localDate = LocalDate.now();
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
import org.joda.time.LocalDate;
Date myDate = new Date();
LocalDate localDate = LocalDate.fromDateFields(myDate);
System.out.println("My date using Date" Nov 18 11:23:33 BRST 2016);
System.out.println("My date using joda.time LocalTime" 2016-11-18);
java.time.LocalDate
的。Joda-Time项目现在处于维护模式,团队建议迁移到java.time。 - Basil Bourque您可以将java.util.Date
对象转换为String
对象,这将按照yyyy-mm-dd格式化日期。
LocalDate具有一个parse
方法,它将其转换为LocalDate
对象。字符串必须表示有效日期,并使用DateTimeFormatter.ISO_LOCAL_DATE进行解析。
从Date到LocalDate
LocalDate.parse(Date.toString())
java.time.LocalDate
期望一个ISO格式的字符串,而不是java.util.Date
的标准输出。虽然应该被点踩,但今天我心情很好;-) 这里最好的答案是由Basil Bourque给出的。 - Meno Hochschild
java.util.Date
。 - Reimeusjava.util.Date
默认情况下不仅希望表示日期(年/月/日)的信息,还要包括一天中的时间信息,因此我们需要以某种方式提供此信息(这里使用atStartOfDay
来帮助)。 - PshemoDate
。例如,JPA 2.1的@Basic
注解直接识别Date
和Calendar
,但仅支持将Instant
作为可序列化类型。我认为他在新代码中使用新的日期时间API,并使用Date
进行互操作是正确的做法。 - scottbDate
和Instant
的通用合同都包括时间概念。因此,在LocalDate
和Date
之间相互转换意味着您必然涉及时间概念,即使您不打算使用时间信息。如果没有知道 "某个本地时区" 是什么,就无法将 "2015年9月7日在某个本地时区" (LocalDate
) 转换为 "自 1970 年 1 月 1 日 UTC 午夜以来的毫秒数" (Date
)。 - scottb