Java 8有一个全新的日期和时间API。 此API中最有用的类之一是LocalDateTime,用于保存与时区无关的date-with-time值。
为此目的,可能有数百万行代码使用遗留类java.util.Date。 因此,当连接新旧代码时,需要在两者之间进行转换。 由于似乎没有直接的方法来实现这一点,怎么办呢?
-
请参见http://stackoverflow.com/questions/33066904/simpliest-java8-localdate-to-java-util-date-conversion-and-vice-versa
-
docs.oracle.com/javase/tutorial/datetime/iso/legacy.html
-
Convert java.util.Date可能重复到java.time.LocalDate
简短回答:
1 2 3
| Date in = new Date();
LocalDateTime ldt = LocalDateTime. ofInstant(in. toInstant(), ZoneId. systemDefault());
Date out = Date. from(ldt. atZone(ZoneId. systemDefault()). toInstant()); |
说明:
(基于这个关于LocalDate的问题)
尽管它的名字,java.util.Date代表时间线上的瞬间,而不是"日期"。存储在对象中的实际数据是自1970-01-01T00:00Z(格林威治标准时间1970 /格林威治标准时间1970年初午夜)以来的long毫秒计数。
JSR-310中java.util.Date的等价类是Instant,因此有方便的方法来提供转换:
1 2 3
| Date input = new Date();
Instant instant = input. toInstant();
Date output = Date. from(instant ); |
java.util.Date实例没有时区概念。如果在java.util.Date上调用toString(),这可能看起来很奇怪,因为toString是相对于时区的。但是,该方法实际上使用Java的默认时区来提供字符串。时区不是java.util.Date的实际状态的一部分。
Instant也不包含有关时区的任何信息。因此,要从Instant转换为本地日期时间,必须指定时区。这可能是默认区域 - ZoneId.systemDefault() - 或者它可能是应用程序控制的时区,例如来自用户首选项的时区。 LocalDateTime有一个方便的工厂方法,可以同时使用即时区和时区:
1 2
| Date in = new Date();
LocalDateTime ldt = LocalDateTime. ofInstant(in. toInstant(), ZoneId. systemDefault()); |
反之,通过调用atZone(ZoneId)方法指定LocalDateTime时区。然后可以将ZonedDateTime直接转换为Instant:
1 2 3
| LocalDateTime ldt = ...
ZonedDateTime zdt = ldt. atZone(ZoneId. systemDefault());
Date output = Date. from(zdt. toInstant()); |
请注意,从LocalDateTime到ZonedDateTime的转换可能会引入意外行为。这是因为夏令时不会存在每个本地日期时间。在秋季/秋季,当地时间线存在重叠,其中相同的本地日期时间出现两次。在春天,有一个缺口,一小时消失。有关转换将执行的更多定义,请参阅atZone(ZoneId)的Javadoc。
总结一下,如果您将java.util.Date往返到LocalDateTime并返回到java.util.Date,由于夏令时,您可能会得到不同的瞬间。
附加信息:还有另一个区别会影响很久的日期。 java.util.Date使用的日历在1582年10月15日更改,日期之前使用Julian日历而不是Gregorian日历。相比之下,java.time.*始终使用ISO日历系统(相当于格里高利)。在大多数用例中,ISO日历系统是您想要的,但在比较1582年之前的日期时,您可能会看到奇怪的效果。
-
非常感谢您的明确解释。特别是为什么java.util.Date不包含时区,而是在toString()期间打印它。活动官方文档在您发布时并未明确说明。
-
警告:LocalDateTime.ofInstant(date.toInstant()...表现不像人们想象的那样。例如,new Date(1111-1900,11-1,11,0,0,0);将使用此方法变为1111-11-17 23:53:28。如果在上一个示例中需要结果为1111-11-11 00:00:00,请查看java.sql.Timestamp#toLocalDateTime()的实现。
-
我添加了一个关于非常古老的日期(1582年之前)的部分。 FWIW,您建议的修复可能是错误的,因为java.util.Date中的1111-11-11与java.time中的1111-11-18历史相同,因为不同的日历系统(6.5分钟差异)发生在1900年之前的许多时区)
-
另外值得注意的是java.sql.Date#toInstant会抛出UnsupportedOperationException。所以不要在java.sql.ResultSet#getDate的RowMapper中使用toInstant。
这就是我想出来的(和所有日期时间难题一样,它可能会因为一些奇怪的时区 - 白昼 - 日光调整而被反驳:D)
往返:Date << - >> LocalDateTime
鉴于:Date date = [some date]
(1)LocalDateTime << Instant << Date
1 2
| Instant instant = Instant.ofEpochMilli(date.getTime());
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); |
(2)Date << Instant << LocalDateTime
1 2
| Instant instant = ldt. toInstant(ZoneOffset. UTC);
Date date = Date. from(instant ); |
例:
鉴于:
1 2
| Date date = new Date();
System. out. println(date +" long:" + date. getTime()); |
(1)LocalDateTime << Instant << Date:
从Date创建Instant:
1 2 3
| Instant instant = Instant. ofEpochMilli(date. getTime());
System. out. println("Instant from Date:
" + instant ); |
从Instant创建Date(不是必需的,但为了说明):
1 2 3
| date = Date. from(instant );
System. out. println("Date from Instant:
" + date +" long:" + date. getTime()); |
从Instant创建LocalDateTime
1 2 3
| LocalDateTime ldt = LocalDateTime. ofInstant(instant, ZoneOffset. UTC);
System. out. println("LocalDateTime from Instant:
" + ldt ); |
(2)Date << Instant << LocalDateTime
从LocalDateTime创建Instant:
1 2 3
| instant = ldt. toInstant(ZoneOffset. UTC);
System. out. println("Instant from LocalDateTime:
" + instant ); |
从Instant创建Date:
1 2 3
| date = Date. from(instant );
System. out. println("Date from Instant:
" + date +" long:" + date. getTime()); |
输出是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Fri Nov 01 07 :13:04 PDT 2013 long: 1383315184574
Instant from Date:
2013-11-01T14 :13:04.574Z
Date from Instant :
Fri Nov 01 07 :13:04 PDT 2013 long: 1383315184574
LocalDateTime from Instant :
2013-11-01T14 :13:04.574
Instant from LocalDateTime :
2013-11-01T14 :13:04.574Z
Date from Instant :
Fri Nov 01 07 :13:04 PDT 2013 long: 1383315184574 |
-
我认为这不像你想象的那么大。使用java.util.Date的旧代码从未依赖于java.time库,如果没有它,可能会很好。对于许多商店而言,改变新API的需求可能不是一个高优先级。新代码可以从头使用新的API。边缘情况是使用旧的内部或第三方库的新代码,它们产生Date s的结果,需要使用java.time与新代码交互。我认为这种情况并不常见。
-
@scottb你是在向我或提出问题的人发表这个问题吗?至于我的想法,那么在jdk 8 API中明确说明转换会很好 - 他们至少可以做到这一点。无论如何,有许多库将被重新考虑以包含新的Java-8功能,并且知道如何做到这一点非常重要。
-
@scottb为什么第三方案件不常见?它该死的常见。一个例子:JDBC 4和更少(希望不是5)。
-
作为一般的JSR-310规则,不需要使用epoch-millis在类型之间进行转换。使用对象存在更好的选择,请参阅下面的完整答案。如果使用像UTC这样的区域偏移,上面的答案也是完全有效的 - 答案的某些部分不适用于像America / New_York这样的完整时区。
-
而不是Instant.ofEpochMilli(date.getTime())做date.toInstant()
如果您确定需要默认时区,那么更方便的方法:
-
当然,它更容易,但我不喜欢混合jdbc相关的东西与简单的日期处理,至少恕我直言。
-
对不起,这是解决一个简单问题的可怕办法。这就像定义5等于奥林匹克标志中的戒指数量。
从新的API LocalDateTime转换为java.util.date时,以下似乎有效:
1
| Date. from(ZonedDateTime. of({time as LocalDateTime }, ZoneId. systemDefault()). toInstant()); |
反向转换可以(希望)以类似的方式实现......
希望能帮助到你...
一切都在这里:http://blog.progs.be/542/date-to-java-time
"往返"的答案并不准确:当你这样做时
1
| LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); |
如果您的系统时区不是UTC / GMT,则更改时间!
-
只有你每年1小时,秋天,当LocalDateTime重叠两次时才这样做。弹簧向前移动不会引起问题。大多数时候都会正确转换两个方向。
我不确定这是最简单或最好的方式,还是有任何陷阱,但它有效:
1 2 3 4 5 6 7 8 9 10 11 12
| static public LocalDateTime toLdt (Date date ) {
GregorianCalendar cal = new GregorianCalendar();
cal. setTime(date );
ZonedDateTime zdt = cal. toZonedDateTime();
return zdt. toLocalDateTime();
}
static public Date fromLdt (LocalDateTime ldt ) {
ZonedDateTime zdt = ZonedDateTime. of(ldt, ZoneId. systemDefault());
GregorianCalendar cal = GregorianCalendar. from(zdt );
return cal. getTime();
} |
-
从LocalDateTime到Date肯定存在一个陷阱。在夏令时转换时,LocalDateTime可能不存在,或发生两次。你需要弄清楚你想要在每种情况下发生什么。
-
BTW,GregorianCalendar属于新的java.time API旨在取代的旧的尴尬API
LocalDateTime - > Date的最快方法是:
Date.from(ldt.toInstant(ZoneOffset.UTC))
如果您使用的是android并使用threetenbp,则可以使用DateTimeUtils。
例如:
1
| Date date = DateTimeUtils. toDate(localDateTime. atZone(ZoneId. systemDefault()). toInstant()); |
您不能使用Date.from,因为它仅支持api 26+