Bas*_*que 34 java datetime date java-time
我有一个java.util.Date
对象或一个java.util.Calendar
对象.如何在java.time框架中将其转换为正确的类型?
我听说我们现在应该使用java.time类型来处理大部分业务逻辑.使用尚未针对java.time更新的旧代码时,我需要能够来回转换.什么类型映射到java.util.Date
或java.util.Calendar
?
Bas*_*que 100
是的,你肯定应该尽可能使用java.time框架.
旧的日期时间类,包括java.util.Date
,java.util.Calendar
等等java.text.SimpleTextFormat
,已被证明设计不当,令人困惑和麻烦.尽可能避免使用它们.但是当您必须与这些旧类型进行互操作时,您可以在旧的和新的之间进行转换.
继续阅读一些基本的介绍,有些过于简化,以指导您在旧的和新的日期时间类之间来回移动.
该java.time框架由定义JSR 310,由大获成功的启发乔达时库,并通过扩展ThreeTen-EXTRA项目.的功能的大部分被后移植到Java 6和7在ThreeTen-反向移植项目,在为Android进一步适配ThreeTenABP项目.
什么java.time类型匹配java.util.Date
?好吧,一个java.util.Date
对象基本上代表UTC时间轴上的一个时刻,它是日期和时间的组合.我们可以将它转换为java.time中的几种类型中的任何一种.每个都在下面讨论.请注意,旧的日期时间类中添加了一些新方法以方便转换.
通常,您应该在UTC中执行大部分业务逻辑.在这样的工作中,Instant
会经常使用.传递Instant
对象,仅应用时区以呈现给用户.如果确实需要应用偏移或时区,请使用下面进一步介绍的类型.
java.util.Date
到Instant
鉴于这两个Instant
和java.util.Date
在UTC时间线上的时刻,我们可以很容易地从一个移动java.util.Date
到Instant
.老班学获得了一种新方法java.util.Date::toInstant
.
Instant instant = myUtilDate.toInstant();
Run Code Online (Sandbox Code Playgroud)
你可以走另一个方向,从一个Instant
到一个java.util.Date
.但是你可能会失去关于小数秒的信息.一个Instant
跟踪纳秒,小数点后最多九位数,如2016-01-23T12:34:56.123456789Z
.java.util.Date和.Calendar都限制为毫秒,小数点后最多三位数,例如2016-01-23T12:34:56.123Z
.在这个例子中,从Instant
去往Date
意味着截断456789
.
java.util.Date myUtilDate = java.util.Date.from(instant);
Run Code Online (Sandbox Code Playgroud)
java.util.Calendar
到Instant
怎么样java.util.Calendar
而不是java.util.Date
?在Calendar
对象内部,日期时间被跟踪为UTC()中1970年第一时刻的历元参考日期时间的毫秒数.所以这个值可以很容易地转换为.1970-01-01T00:00:00.0Z
Instant
Instant instant = myUtilCalendar.toInstant() ;
Run Code Online (Sandbox Code Playgroud)
java.util.GregorianCalendar
到ZonedDateTime
更好的是,如果您的java.util.Calendar
对象实际上是一个,java.util.GregorianCalendar
您可以轻松地直接进入ZonedDateTime
.该方法具有保留嵌入时区信息的优点.
垂头丧气从接口的Calendar
到具体类的GregorianCalendar
.然后调用toZonedDateTime
和from
方法来回走动.
if (myUtilCalendar instanceof GregorianCalendar) {
GregorianCalendar gregCal = (GregorianCalendar) myUtilCalendar; // Downcasting from the interface to the concrete class.
ZonedDateTime zdt = gregCal.toZonedDateTime(); // Create `ZonedDateTime` with same time zone info found in the `GregorianCalendar`
}
Run Code Online (Sandbox Code Playgroud)
走向另一个方向......
java.util.Calendar myUtilCalendar = java.util.GregorianCalendar.from(zdt); // Produces an instant of `GregorianCalendar` which implements `Calendar` interface.
Run Code Online (Sandbox Code Playgroud)
如上所述,请注意您可能会丢失有关秒数的信息.在纳秒在java.time类型(ZonedDateTime
)被截断,以毫秒为单位的.Calendar
/ .GregorianCalendar
.
OffsetDateTime
从Instant
我们可以应用一个从UTC的偏移量进入某个地方的挂钟时间.偏移量是UTC(向东)之前或UTC之后(向西)之前的小时数,可能是分钟数和秒数.这个ZoneOffset
类代表了这个想法.结果是一个OffsetDateTime
对象.
ZoneOffset offset = ZoneOffset.of("-04:00");
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);
Run Code Online (Sandbox Code Playgroud)
你可以走另一个方向,从一个OffsetDateTime
到一个java.util.Date
.提取一个Instant
然后按照我们在上面的代码中看到的那样继续.如上所述,任何纳秒都会被截断为毫秒(数据丢失).
java.util.Date myUtilDate = java.util.Date.from(odt.toInstant());
Run Code Online (Sandbox Code Playgroud)
ZonedDateTime
更好的是,应用一个全时区.时区是偏移加上用于处理诸如夏令时(DST)之类的异常的规则.
应用a ZoneId
可以获得一个ZonedDateTime
对象.使用适当的时区名称(大陆/地区).不要使用常见的如3-4个字母的缩写EST
或IST
因为它们既不规范,也不是唯一的.
ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);
Run Code Online (Sandbox Code Playgroud)
你可以走另一个方向,从一个ZonedDateTime
到一个java.util.Date
.提取一个Instant
然后按照我们在上面的代码中看到的那样继续.如上所述,任何纳秒都会被截断为毫秒(数据丢失).
java.util.Date myUtilDate = java.util.Date.from( zdt.toInstant() );
Run Code Online (Sandbox Code Playgroud)
我们进一步看到a ZonedDateTime
可以转换为a GregorianCalendar
.
LocalDate
有时您可能需要一个仅限日期的值,没有时间和没有时区.为此,请使用java.time.LocalDate
对象.
有关更多讨论,请参阅此问题,将java.util.Date转换为java.time.LocalDate,尤其是此答案由Joda-Time和java.time发明背后的主要人员撰写.
关键是要经过一个ZonedDateTime
(如上面代码中生成的那样).我们需要一个时区来确定日期.世界各地的日期各不相同,东部早些时候开始新的一天.例如,在巴黎的午夜之后是新的一天,而在蒙特利尔仍然是"昨天".因此,虽然a LocalDate
不包含时区,但需要时区来确定 a LocalDate
.
LocalDate localDate = zdt.toLocalDate();
Run Code Online (Sandbox Code Playgroud)
从另一个方向转换LocalDate
到日期时间意味着发明一个时间.您可以选择在您的业务场景中有意义的任何时间.对于大多数人来说,一天的第一时刻是有道理的.您可能很想将第一时刻的硬编码作为时间00:00:00.0
.在某些时区,由于夏令时(DST)或其他异常,该时间可能无效作为第一时刻.所以让java.time通过调用来确定正确的时间atStartOfDay
.
ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
Run Code Online (Sandbox Code Playgroud)
LocalTime
在极少数情况下,您可能只需要一个没有日期且没有时区的时间.这个概念由LocalTime
班级代表.如上所述LocalDate
,我们需要一个时区来确定LocalTime
即使LocalTime
对象不包含(不"记住")该时区.因此,我们再次通过ZonedDateTime
从Instant
上面看到的对象获得.
LocalTime localTime = zdt.toLocalTime();
Run Code Online (Sandbox Code Playgroud)
LocalDateTime
与其他两种Local…
类型一样,a LocalDateTime
没有分配时区和偏移量.因此,您可能很少使用它.它可以让您大致了解日期时间,但不是时间轴上的一个点.如果您指的是某个通用日期和可能应用于时区的某个时间,请使用此选项.
例如,"今年圣诞节开始"将是2016-12-25T00:00:00.0
.请注意,在该文本表示中缺少任何偏移或时区LocalDateTime
.印度德里的圣诞节比法国巴黎的圣诞节开始得更早,后来仍然在蒙特利尔魁北克加拿大.应用这些区域的每个时区将在时间轴上产生不同的时刻.
LocalDateTime ldt = zdt.toLocalDateTime();
Run Code Online (Sandbox Code Playgroud)