JSR310新日期API(二)-日期时间API 前提 这篇文章主要介绍一下日期时间API中最常用的类库,分别是:
java.time.Clock
:时钟。
java.time.Instant
:瞬时时间,时间戳java.sql.Timestamp
的替代类。
java.time.LocalDate
:本地日期,ISO-8601日历系统下的日期表示,不包含时区的概念,只能表示年月日。
java.time.LocalDateTime
:本地日期时间,ISO-8601日历系统下的日期时间表示,不包含时区的概念,只能表示年月日时分秒。
java.time.LocalTime
:本地时间,ISO-8601日历系统下的时间表示,不包含时区的概念,只能表示时分秒。
java.time.OffsetTime
:带有时间偏移量的时间,ISO-8601日历系统下的带有UTC/GMT时间偏移量的时间表示。
java.time.OffsetDateTime
:带有时间偏移量的日期时间,ISO-8601日历系统下的带有UTC/GMT时间偏移量(不包含基于ZoneRegion
的时间偏移量)的日期时间表示。
java.time.ZonedDateTime
:带有时间偏移量的日期时间,ISO-8601日历系统下的带有UTC/GMT时间偏移量(包含基于ZoneRegion
的时间偏移量)的日期时间表示。
其他的类库还有Year
、Month
、DayOfWeek
、MonthDay
、YearMonth
等。值得注意的是:JSR-310
增加的日期API是严格区分年月日-时分秒格式的日期表示类,例如XXXDateTime一定表示为年月日时分秒(纳秒),XXXTime只能表示时分秒(纳秒),XXXDate只能表示年月日。
值得注意的是 :这些新增的日期时间类都是不可变类,每次通过其方法更变或者修改都是返回一个全新的对象 ,因此它们都是线程安全 的。
Clock java.time.Clock
是一个抽象类,它表示时钟,一般情况下,它需要结合时区使用,提供获取当前时刻的功能。Clock
主要提供下面四个方法,其他方法都是静态工厂方法:
public abstract ZoneId getZone () public abstract Instant instant () public long millis () public abstract Clock withZone (ZoneId zone)
静态工厂方法如下:
方法
功能
public static Clock systemUTC()
获取可以返回当前时刻的系统时钟,使用UTC(零)时区进行进行时间转换[SystemClock]
public static Clock systemDefaultZone()
获取可以返回当前时刻的系统时钟,使用默认时区进行时间转换[SystemClock]
public static Clock system(ZoneId zone)
获取可以返回当前时刻的系统时钟,使用指定时区ID进行时间转换[SystemClock]
public static Clock tickMillis(ZoneId zone)
获取以整数毫秒返回当前时刻的时钟,使用指定时区ID进行时间转换[TickClock]
public static Clock tickSeconds(ZoneId zone)
获取以整数秒返回当前时刻的时钟,使用指定时区ID进行时间转换[TickClock]
public static Clock tickMinutes(ZoneId zone)
获取以整数分钟返回当前时刻的时钟,使用指定时区ID进行时间转换[TickClock]
public static Clock tick(Clock baseClock, Duration tickDuration)
返回一个以基础时钟和时钟记录基础单位为构造的时钟[TickClock]
public static Clock fixed(Instant fixedInstant, ZoneId zone)
获得一个始终返回同一时刻的时钟,使用指定时区ID进行时间转换[FixedClock]
offset(Clock baseClock, Duration offsetDuration)
返回一个以基础时钟和固定时间偏移量为构造的时钟[OffsetClock]
java.time.Clock
主要有四个实现,它们都是java.time.Clock
的内部类,上面的工厂方法创建的实例一定是这四个实现之一:
SystemClock
:总是基于System#currentTimeMillis()
返回最新的时间SystemClock.UTC
是典型的实现。
FixedClock
:总是返回相同的瞬时时间,可以认为是一个固定时刻的时钟,通常使用于测试。
OffsetClock
:基于一个确定的Clock实现,为它添加一个时间偏移量,时间偏移量的单位是Duration
。
TickClock
:基于一个确定的Clock实现,为它添加一个时间偏移量,时间偏移量的单位是纳秒。
上面比较难理解的是TickClock
,这里举个简单的例子:
public class TickClockMain { public static void main (String[] args) throws Exception { Clock tickMillis = Clock.tickMillis(ZoneId.systemDefault()); Clock tickSeconds = Clock.tickSeconds(ZoneId.systemDefault()); System.out.println(tickMillis.millis()); System.out.println(tickSeconds.millis()); } } 1546010945575 1546010945000
简单来说,Clock#tickMillis()
构造的时钟的计时单位是毫秒,而Clock#tickSeconds()
构造的时钟的计时单位是秒(毫秒部分会被截断),以此类推。
FixedClock
是一个固定时刻的时钟,一般用于测试 :
public class FixedClockMain { public static void main (String[] args) throws Exception { Clock fixed = Clock.fixed(Instant.now(), ZoneId.systemDefault()); System.out.println(fixed.millis()); System.out.println(fixed.millis()); System.out.println(fixed.millis()); } } 1546011492590 1546011492590 1546011492590
最常用的是默认ZoneId下的系统时钟SystemClock
和UTC系统时钟:
public class SystemClockMain { public static void main (String[] args) throws Exception { Clock clock = Clock.systemDefaultZone(); System.out.println(clock.millis()); Clock utc = Clock.systemUTC(); System.out.println(utc.millis()); System.out.println(System.currentTimeMillis()); } } 1546011686413 1546011686413 1546011686413
Instant java.time.Instant
字面意思是瞬时时间,它是java.sql.Timestamp
的对应类,代表时间线(Time-Line)上的一个瞬时时间点,准确来说,它内部持有一个long
类型的纪元秒属性(seconds)和一个int
类型的纳秒属性(nanos,nanos的取值范围是[0,999_999_999]),纪元秒如果为正数,表示该瞬时时间点位于格林威治新纪元1970-01-01T00:00:00Z
之后,而纪元秒如果为负数,则表示该瞬时时间点位于格林威治新纪元之前。因此Instant
能表示的时间点其实是有上下界的,逻辑上的界限就是1970-01-01T00:00:00Z - 31557014167219200秒
到1970-01-01T00:00:00Z + 31556889864403199秒 + 999_999_999纳秒
或者表示为Instant#MIN
到Instant#MAX
,这个范围很大,因此暂时不需要考虑超限的问题。Instant
中已经提供了一个公有静态实例用于表示格林威治新纪元,它就是Instant#EPOCH
,代表1970-01-01T00:00:00Z这个瞬时时间点。先看一下Instant
的常用静态工厂方法(Instant
没有公有构造器,必须通过工厂方法构造实例):
public static Instant now () public static Instant now (Clock clock) public static Instant ofEpochSecond (long epochSecond) public static Instant ofEpochSecond (long epochSecond, long nanoAdjustment) public static Instant ofEpochMilli (long epochMilli) public static Instant from (TemporalAccessor temporal) public static Instant parse (final CharSequence text)
当然还有其他常用的方法:
public long getLong (TemporalField field) public long getEpochSecond () public int getNano () public long toEpochMilli () public Instant with (TemporalField field, long newValue) public Instant truncatedTo (TemporalUnit unit) public Instant plus (long amountToAdd, TemporalUnit unit) public Instant minus (long amountToSubtract, TemporalUnit unit) public long until (Temporal endExclusive, TemporalUnit unit)
举个使用例子:
public class InstantMain { public static void main (String[] args) throws Exception { Instant instant = Instant.now(); System.out.println(String.format("Second:%d,Nano:%d" , instant.getEpochSecond(), instant.getNano())); instant = Instant.now(Clock.systemDefaultZone()); System.out.println(String.format("Second:%d,Nano:%d" , instant.getEpochSecond(), instant.getNano())); instant = Instant.ofEpochSecond(new Date().toInstant().getEpochSecond()); System.out.println(String.format("Second:%d,Nano:%d" , instant.getEpochSecond(), instant.getNano())); instant = Instant.ofEpochMilli(System.currentTimeMillis()); System.out.println(String.format("Second:%d,Nano:%d" , instant.getEpochSecond(), instant.getNano())); instant = Instant.from(Instant.now()); System.out.println(String.format("Second:%d,Nano:%d" , instant.getEpochSecond(), instant.getNano())); instant = Instant.parse("2018-12-31T10:15:30.00Z" ); System.out.println(String.format("Second:%d,Nano:%d" , instant.getEpochSecond(), instant.getNano())); } } Second:1546187685 ,Nano:261861900 Second:1546187685 ,Nano:291941900 Second:1546187685 ,Nano:0 Second:1546187685 ,Nano:292000000 Second:1546187685 ,Nano:292946400 Second:1546251330 ,Nano:0
LocalDate java.time.LocalDate
代表ISO-8601日历系统中不包含时区的日期(当然也不包含具体的时间)表示 ,例如2007-12-03。LocalDate
是一个不可变的日期对象,也就是只能表示日期,通常的表示格式为年-月-日,同时提供其他日期字段的访问,例如一年中的第几日(day-of-year)、星期几(day-of-week)和一年中的第几周(week-of-year)等。不同的LocalDate
之间的比较只能通过LocalDate#equals()
方法,其他比较操作如==
或者hash()
方法会产生无法预知的结果。LocalDate
提供的常量:
public static final LocalDate MIN = LocalDate.of(Year.MIN_VALUE, 1 , 1 )public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12 , 31 )public static final LocalDate EPOCH = LocalDate.of(1970 , 1 , 1 )
LocalDate
的工厂方法比较多,这里只列举部分常用的:
public static LocalDate now () public static LocalDate now (ZoneId zone) public static LocalDate now (Clock clock) public static LocalDate of (int year, Month month, int dayOfMonth) public static LocalDate of (int year, int month, int dayOfMonth) public static LocalDate ofYearDay (int year, int dayOfYear) public static LocalDate ofEpochDay (long epochDay)
LocalDate
的实例方法也比较多,这里也列举部分常用的:
public int getYear () public int getMonthValue () public Month getMonth () public int getDayOfYear () public DayOfWeek getDayOfWeek () public boolean isLeapYear () public int lengthOfMonth () public int lengthOfYear () public LocalDate with (TemporalField field, long newValue) public LocalDate plus (long amountToAdd, TemporalUnit unit) public LocalDate minus (long amountToSubtract, TemporalUnit unit) public long until (Temporal endExclusive, TemporalUnit unit) public long toEpochDay () public boolean isEqual (ChronoLocalDate other) public boolean equals (Object obj)
举个简单的使用例子:
public class LocalDateMain { public static void main (String[] args) throws Exception { LocalDate localDate = LocalDate.now(); System.out.println(localDate); localDate = LocalDate.of(2018 , 12 , 31 ); System.out.println(localDate); localDate = localDate.plus(1 , ChronoUnit.DAYS); System.out.println(localDate); System.out.println(localDate.equals(LocalDate.of(2019 ,1 ,1 ))); System.out.println(localDate.toEpochDay()); } } 2018 -12 -31 2018 -12 -31 2019 -01 -01 true 17897
LocalTime java.time.LocalTime
代表ISO-8601日历系统中不包含时区的时间(当然也不包含具体的日期)表示 ,例如10:15:30。LocalTime
是一个不可变的时间对象,也就是只能表示时间,通常的表示格式为时:分:秒,也可以包含一个纳秒属性(nano取值范围[0,999999999]),通俗来说,它表示的就是挂钟上所见的时间的描述。同样,不同的LocalTime
实例必须通过LocalTime#equals()
方法比较。LocalTime
提供的静态实例如下:
public static final LocalTime MINpublic static final LocalTime MAXpublic static final LocalTime MIDNIGHTpublic static final LocalTime NOON
LocalTime
常用的工厂方法:
public static LocalTime now () public static LocalTime now (ZoneId zone) public static LocalTime now (Clock clock) public static LocalTime of (int hour, int minute) public static LocalTime of (int hour, int minute, int second) public static LocalTime of (int hour, int minute, int second, int nanoOfSecond) public static LocalTime ofInstant (Instant instant, ZoneId zone) public static LocalTime ofSecondOfDay (long secondOfDay) public static LocalTime ofNanoOfDay (long nanoOfDay)
LocalTime
常用的实例方法有很多,套路和上面章节提到过的方法类似,这里不啰嗦分析:
public int getHour () public int getMinute () public int getSecond () public int getNano ()
举个简单的例子:
public class LocalTimeMain { public static void main (String[] args) throws Exception { LocalTime localTime = LocalTime.now(); System.out.println(localTime); localTime = LocalTime.of(23 , 59 ); System.out.println(localTime); localTime = LocalTime.MAX; System.out.println(String.format("Hour:%d,minute:%d,second:%d,nano:%d" , localTime.getHour(), localTime.getMinute(), localTime.getSecond(), localTime.getNano())); } } 00 :46 :08.845848800 23 :59 Hour:23 ,minute:59 ,second:59 ,nano:999999999
LocalDateTime java.time.LocalDateTime
实际上就是LocalDate
和LocalTime
的结合版本,代表ISO-8601日历系统中不包含时区(LocalDateTime
不存储时区信息,但是可以使用时区ID构造LocalDateTime
实例)的日期时间表示 ,例如2007-12-03T10:15:30。LocalDateTime
是一个不可变的时间对象,也就是只能表示日期时间,通常的表示格式为年-月日 时:分:秒,也可以包含一个纳秒属性(nano取值范围[0,999999999])。不同的LocalDateTime
实例必须通过LocalDateTime#equals()
方法比较。LocalDateTime
内部持有一个LocalDate
实例和一个LocalTime
实例。它定义了两个公有的静态常量:
public static final LocalDateTime MIN = LocalDateTime.of(LocalDate.MIN, LocalTime.MIN)public static final LocalDateTime MAX = LocalDateTime.of(LocalDate.MAX, LocalTime.MAX)
LocalDateTime
常用的静态工厂方法如下:
public static LocalDateTime now () public static LocalDateTime now (ZoneId zone) public static LocalDateTime now (Clock clock) public static LocalDateTime of (int year, Month month, int dayOfMonth, int hour, int minute) public static LocalDateTime of (int year, Month month, int dayOfMonth, int hour, int minute, int second) public static LocalDateTime of (int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) public static LocalDateTime of (int year, int month, int dayOfMonth, int hour, int minute) public static LocalDateTime of (int year, int month, int dayOfMonth, int hour, int minute, int second) public static LocalDateTime of (int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) public static LocalDateTime of (LocalDate date, LocalTime time) public static LocalDateTime ofInstant (Instant instant, ZoneId zone) public static LocalDateTime ofEpochSecond (long epochSecond, int nanoOfSecond, ZoneOffset offset)
LocalDateTime
的实例方法和前面介绍过的类差不多,这里不做详细展开,举个简单的使用例子:
public class LocalDateTimeMain { public static void main (String[] args) throws Exception { LocalDateTime localDateTime = LocalDateTime.now(ZoneId.systemDefault()); System.out.println(localDateTime); localDateTime = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); System.out.println(localDateTime); localDateTime = localDateTime.plus(1 , ChronoUnit.YEARS); System.out.println(localDateTime); } } 2019 -01 -01T17:43 :48.260517400 2019 -01 -01T17:43 :48.260517400 2020 -01 -01T17:43 :48.260517400
OffsetTime java.time.OffsetTime
表示ISO-8601日历系统中带有基于UTC/Greenwich时间偏移量的时间,例如10:15:30+01:00。OffsetTime
也是一个不可变的时间对象,通常表示格式为时:分:秒-时间偏移量,当然它也可以包含一个纳秒属性(nano取值范围[0,999999999])。相比LocalTime
,它多存储了一个时区时间偏移量(zone offset)属性。OffsetTime
的公有静态实例如下:
public static final OffsetTime MIN = LocalTime.MIN.atOffset(ZoneOffset.MAX)public static final OffsetTime MAX = LocalTime.MAX.atOffset(ZoneOffset.MIN)
OffsetTime
的常用工厂方法如下:
public static OffsetTime now () public static OffsetTime now (ZoneId zone) public static OffsetTime now (Clock clock) public static OffsetTime of (LocalTime time, ZoneOffset offset) public static OffsetTime of (int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) public static OffsetTime ofInstant (Instant instant, ZoneId zone)
举个简单的使用例子:
public class OffsetTimeMain { public static void main (String[] args) throws Exception { OffsetTime offsetTime = OffsetTime.now(); System.out.println(offsetTime); offsetTime = OffsetTime.ofInstant(Instant.now(), ZoneId.systemDefault()); System.out.println(offsetTime); offsetTime = OffsetTime.of(LocalTime.now(), ZoneOffset.UTC); System.out.println(offsetTime); } } 18 :08:26.263710800 +08:00 18 :08:26.264713600 +08:00 18 :08:26. 264713600Z
OffsetDateTime java.time.OffsetDateTime
表示ISO-8601日历系统中带有基于UTC/Greenwich时间偏移量的日期时间,例如2007-12-03T10:15:30+01:00。OffsetDateTime
也是一个不可变的日期时间对象,通常表示格式为年-月-日 时:分:秒-时间偏移量,当然它也可以包含一个纳秒属性(nano取值范围[0,999999999])。相比LocalDateTime
,它多存储了一个时区时间偏移量(zone offset)属性。OffsetDateTime
提供的公有静态实例常量如下:
public static final OffsetDateTime MIN = LocalDateTime.MIN.atOffset(ZoneOffset.MAX)public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN)
OffsetDateTime
的常用静态工厂方法如下:
public static OffsetDateTime now () public static OffsetDateTime now (ZoneId zone) public static OffsetDateTime now (Clock clock) public static OffsetDateTime of (LocalDate date, LocalTime time, ZoneOffset offset) public static OffsetDateTime of (LocalDateTime dateTime, ZoneOffset offset) public static OffsetDateTime of ( int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) public static OffsetDateTime ofInstant (Instant instant, ZoneId zone)
举个简单的使用例子:
public class OffsetDateTimeMain { public static void main (String[] args) throws Exception { OffsetDateTime offsetDateTime = OffsetDateTime.now(); System.out.println(offsetDateTime); offsetDateTime = OffsetDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); System.out.println(offsetDateTime); offsetDateTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.ofHours(8 )); System.out.println(offsetDateTime); } } 2019 -01 -01T20:38 :03.388846400 +08:00 2019 -01 -01T20:38 :03.388846400 +08:00 2019 -01 -01T20:38 :03.388846400 +08:00
ZonedDateTime java.time.ZonedDateTime
应该是JSR-310中最复杂但是最全面的日期时间类(它的API文档中注释也是最多的,从这点也可以看出它的复杂性)。ZonedDateTime
可以简单理解为LocalDateTime
,时区ID和一个可处理的ZoneOffset
三者的共同实现,或者更简单理解为日期时间、时间偏移量、区域时区等时区规则的多重实现。ZonedDateTime
也是一个不可变的日期时间对象,常用的格式为:年-月-日 时:分:秒-时区偏移量-区域,例如2007-12-03T10:15:30+01:00 Europe/Paris。除了包含所有的日期时间属性之外,ZonedDateTime
还包含一个纳秒属性(nano取值范围[0,999999999])。ZonedDateTime
的常用静态工厂方法如下:
public static ZonedDateTime now () public static ZonedDateTime now (ZoneId zone) public static ZonedDateTime now (Clock clock) public static ZonedDateTime of (LocalDate date, LocalTime time, ZoneId zone) public static ZonedDateTime of (LocalDateTime localDateTime, ZoneId zone) public static ZonedDateTime of ( int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) public static ZonedDateTime ofLocal (LocalDateTime localDateTime, ZoneId zone, ZoneOffset preferredOffset)
举个简单的使用例子:
public class ZonedDateTimeMain { public static void main (String[] args) throws Exception { ZonedDateTime zonedDateTime = ZonedDateTime.now(); System.out.println(zonedDateTime); zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), ZoneId.systemDefault()); System.out.println(zonedDateTime); zonedDateTime = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); System.out.println(zonedDateTime); } } 2019 -01 -01T21:00 :03.193242200 +08:00 [Asia/Shanghai]2019 -01 -01T21:00 :03.193242200 +08:00 [Asia/Shanghai]2019 -01 -01T21:00 :03.193242200 +08:00 [Asia/Shanghai]
其他 Year java.time.Year
基于ISO-8601日期系统下表示年份,支持的范围是[-999_999_999,999_999_999]。举个简单的例子:
public class YearMain { public static void main (String[] args) throws Exception { Year year = Year.now(); System.out.println(year); System.out.println(year.isLeap()); year = Year.of(2016 ); System.out.println(year); System.out.println(year.isLeap()); } } 2019 false 2016 true
Month java.time.Month
是一个枚举,代表ISO-8601日期系统中的月份。枚举的成员一共有12个,就是JANUARY到DECEMBER一共12个月份的英文大写表示。举个简单的使用例子:
public class MonthMain { public static void main (String[] args) throws Exception { Month month = Month.of(12 ); System.out.println(month); month = Month.JANUARY; System.out.println(month); } } DECEMBER JANUARY
DayOfWeek java.time.DayOfWeek
是一个枚举,表示一个星期中具体是星期几。枚举成员一共有7个,就是从MONDAY到SUNDAY一共7个指代具体星期几的英文大写表示。举个简单的使用例子:
public class DayOfWeekMain { public static void main (String[] args) throws Exception { DayOfWeek dayOfWeek = DayOfWeek.of(1 ); System.out.println(dayOfWeek); dayOfWeek = DayOfWeek.SUNDAY; System.out.println(dayOfWeek); } } MONDAY SUNDAY
MonthDay java.time.MonthDay
代表月份和对应月份一共存在的天数,内部维护着整型的属性month和整型的属性day。举个例子:
public class MonthDayMain { public static void main (String[] args) throws Exception { MonthDay monthDay = MonthDay.now(); System.out.println(monthDay); monthDay = MonthDay.of(2 , 29 ); System.out.println(monthDay); } } --01 -01 --02 -29
MonthDay
通过静态工厂方法构建实例的时候会判断月份或者天数是否超过实际的限制,如果超限会抛异常。
YearMonth java.time.YearMonth
代表年份和月份,内部维护着整型的属性month和整型的属性month。举个例子:
public class YearMonthMain { public static void main (String[] args) throws Exception { YearMonth yearMonth = YearMonth.now(); System.out.println(yearMonth); yearMonth = YearMonth.of(2019 , 12 ); System.out.println(yearMonth); } } 2019 -01 2019 -12
类型转换 这里主要总结一下JSR-310的日期时间类之间的转换以及JSR-310的日期时间类和已经存在的旧Java日期时间类之间的转换关系。
Instant和其他日期时间类互转 如果有注意到上面介绍日期时间类的时候会发现每个类的工厂方法都包含ofInstant()
方法,也就是Instant
实例可以转化为其他日期时间类实例,这里总结一下:
public class InstantConvertTo { public static void main (String[] args) throws Exception { Instant instant = Instant.now(); ZoneId zoneId = ZoneId.systemDefault(); LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId); LocalDate localDate = LocalDate.ofInstant(instant, zoneId); LocalTime localTime = LocalTime.ofInstant(instant, zoneId); OffsetTime offsetTime = OffsetTime.ofInstant(instant, zoneId); OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(instant,zoneId); ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId); } }
其实很好理解,即使在旧的Java日期时间API中,长整型的时间戳毫秒也可以通过各种日期时间类的构造或者静态工厂方法创建对应的实例。值得注意的是,只有同时包含日期和时间的类才能转换为Instant
实例,这一点也很好理解,只包含时间或者只包含日期的类转换成瞬时时间会丢失部分时间值。这里举个简单例子:
public class InstantConvertFrom { public static void main (String[] args) throws Exception { LocalDateTime localDateTime = LocalDateTime.now(); Instant instant = localDateTime.toInstant(ZoneOffset.UTC); instant = Instant.ofEpochMilli(localDateTime.toEpochSecond(ZoneOffset.UTC) * 1000 ); } }
JSR-310日期时间类之间互相转换 日期时间类本身就包含日期和时间的维度,一般它们直接保存时间类实例作为成员属性,所以转换也十分方便:
public class DateTimeToTime { public static void main (String[] args) throws Exception { LocalDateTime localDateTime = LocalDateTime.now(); LocalDate localDate = localDateTime.toLocalDate(); LocalTime localTime = localDateTime.toLocalTime(); } }
日期类不包含时间部分,所以日期类转换为日期时间类的时候,时间部分会取最小,例如:
public class DateToDateTime { public static void main (String[] args) throws Exception { LocalDate localDate = LocalDate.now(); System.out.println(localDate); LocalDateTime localDateTime = localDate.atStartOfDay(); System.out.println(localDateTime); ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault()); System.out.println(zonedDateTime); } } 2019 -01 -01 2019 -01 -01T00:00 2019 -01 -01T00:00 +08:00 [Asia/Shanghai]
带有时区ID(时间偏移量或者地区)的类型可以轻易转变为不带有时区ID的类型,如果要反过来,则需要添加对应的时区ID属性,例如:
public class ZoneIdDateTimeMain { public static void main (String[] args) throws Exception { ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), ZoneId.systemDefault()); System.out.println(zonedDateTime); LocalDateTime localDateTime = zonedDateTime.toLocalDateTime(); LocalDate localDate = zonedDateTime.toLocalDate(); LocalTime localTime = zonedDateTime.toLocalTime(); zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault()); OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.UTC); localDateTime = offsetDateTime.toLocalDateTime(); localDate = offsetDateTime.toLocalDate(); localTime = offsetDateTime.toLocalTime(); offsetDateTime = OffsetDateTime.of(localDateTime, ZoneOffset.UTC); } }
JSR-310中的类和旧的日期时间相关类之间的转换 java.sql.Timestamp
和java.time.LocalDateTime
之间的转换:
public class TimestampLocalDateTime { public static void main (String[] args) throws Exception { LocalDateTime localDateTime = LocalDateTime.now(); Timestamp timestamp = Timestamp.valueOf(localDateTime); LocalDateTime ldt = timestamp.toLocalDateTime(); } }
java.sql.Date
和java.time.LocalDate
之间的转换:
public class DateLocalDate { public static void main (String[] args) throws Exception { Date date = new Date(2018 , 1 , 1 ); LocalDate localDate = date.toLocalDate(); date = new Date(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); } }
只要是能使用毫秒表示的旧的日期时间类,都可以和java.time.Instant
相互转换,例如:
public class ToInstant { public static void main (String[] args) throws Exception { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); Instant instant = timestamp.toInstant(); java.util.Date date = new Date(System.currentTimeMillis()); instant = date.toInstant(); timestamp = new Timestamp(instant.toEpochMilli()); date = new Date(instant.toEpochMilli()); } }
日期时间API之间的关系
LocalDateTime
尽管可以使用ZoneId
构造实例,但是它只能表示本地日期时间,LocalDateTime
转换到Instant
或者OffsetDatetime
都需要添加ZoneOffset
用于指定时区的偏移量。原则上,Instant
、OffsetDatetime
和ZonedDateTime
都可以表示时间线上任意的一个时间点,OffsetDatetime
的计算规则只包含了时区的偏移量ZoneOffset
,而ZonedDateTime
的计算规则包括了时区的偏移量ZoneOffset
和基于区域表示的偏移量ZoneRegion
,因此ZonedDateTime
可以表示涵盖夏令时Daylight Saving Time(DST)
等日期时间表示方式。换言之,OffsetDatetime
已经可以满足大多数场景下的日期时间表示。
小结 JSR-310
的新时间日期类库的设计相比已经存在的旧的日期时间类库来说,个人认为有以下的优点:
线程安全。
类的职责更加分明,时间、日期、日期时间需要使用明确的类去表示。
API封装更加合理,使得易用性提高。
不过会存在一些问题,最明显的是已有的旧类库存在兼容性问题,例如JDBC模块里面处理日期时间需要进行新的日期时间类和java.sql.Timestamp
进行转换的问题,不过转换成本并不高。
(本文完 c-3-d e-a-20181230 r-a-20200302)