前言 Java 8 中引入了新的一套日期API,相对于之前的Date或者Calendar类,这套API更好的解决了日期和时间的问题。
我们来简单看下。
正文 LocalDateTime,LocalDate,LocalTime 开始使用日期API时,最先碰到的也是这三个类。
他们的静态工厂of方法可以创建日期实例。如下代码:
1 2 3 4 5 6 7 LocalDate date=LocalDate.of(2018 ,11 ,22 ); LocalTime time=LocalTime.of(13 ,45 ,20 ); LocalDateTime dateTime=LocalDateTime.of(2018 ,Month.NOVEMBER,22 ,13 ,45 ,20 );
是不是很简单。
要注意这三个类都是final的,即不可被改变的。
LocalDate 表示年月日,LocalTime表示时分秒,LocalDateTime表示年月日时分秒。
他们三个之间的转换也是比较容易的。如下:
1 2 3 4 5 6 7 8 9 10 11 LocalDateTime dateTime1=LocalDateTime.of(date,time); LocalDateTime dateTime2=date.atStartOfDay(); LocalDateTime dateTime3=date.atTime(LocalTime.of(12 ,12 ,12 )); LocalDateTime dateTime4=time.atDate(LocalDate.of(2018 ,11 ,22 )); LocalDate date1=dateTime.toLocalDate(); LocalTime time1=dateTime.toLocalTime();
对于固定的日期,我们可以获取它的时间信息,也是比较容易的。如具体年月日的数值,等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int year=dateTime.getYear();int year1=dateTime.get(ChronoField.YEAR);Month month=dateTime.getMonth(); int month1=month.getValue();int month2=dateTime.get(ChronoField.MONTH_OF_YEAR);int day=dateTime.getDayOfMonth();int day1=dateTime.get(ChronoField.DAY_OF_MONTH);DayOfWeek dow=dateTime.getDayOfWeek(); int len=date.lengthOfMonth();int hour=dateTime.getHour();int minute=dateTime.getMinute();int second=dateTime.getSecond();
他们还有一些常用的方法,如时间和字符串时间之间的转化、判断闰年、获取当前时间信息、时间的比较、时间的加减天数(年数等)等方法。
相比于我们单独封装处理Date,或者Calendar类,更简便和安全了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 boolean leap=date.isLeapYear();LocalDateTime localDateTimeNow=LocalDateTime.now(); LocalDate localDateNow=LocalDate.now(); LocalTime localTimeNow=LocalTime.now(); LocalDate localDate1=LocalDate.parse("2014-03-18" ); LocalTime localTime1=LocalTime.parse("13:45:20" ); LocalDateTime localDateTime1=LocalDateTime.parse("2018/11/22 11:22:33" , DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss" )); String localDateStr=localDate1.toString(); String localTimeStr=localTime1.format(DateTimeFormatter.ofPattern("HH:mm:ss" )); String localDateTimeStr=localDateTime1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" )); LocalDateTime localDateTime2=localDateTime1.minusDays(10 ); LocalDateTime localDateTime3=localDateTime1.plusYears(1 ); LocalDateTime localDateTime4=localDateTime1.minus(-1 , ChronoUnit.MONTHS); LocalDateTime localDateTime5=localDateTime1.plus(1 , ChronoUnit.MONTHS); LocalDate localDate=date1.with(ChronoField.MONTH_OF_YEAR,9 ); boolean flag=localDateTime2.isAfter(localDateTime3);boolean flag1=localDateTime2.isBefore(localDateTime3);boolean flag2=localDateTime2.isEqual(localDateTime3);
这里面对于时间的加减(plus,minus方法),赋值(with方法)都会生成新的LocalDateTime对象,不会对原来的对象做修改。
Period、Duration 类 这两个类都可以表示日期时间的差值,Period的话表示年月日,如两个时间差1年或者-1个月,Duration 的话用来表示天时分秒,比如它可以表示两个时间差相差34.5s这样的数据。
1 2 3 4 5 6 7 8 9 Duration d1=Duration.between(localDateTime2,localDateTime3); long days1=d1.toDays();Duration d2=Duration.between(localDateTime4,localDateTime5); long hours=d2.toHours();Period period=Period.between(LocalDate.of(2014 ,3 ,8 ),LocalDate.of(2014 ,3 ,18 )); int days2=period.getDays();
Instant和 ZoneId 从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的 java.time.Instant 类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。 而每个ZoneId由该地区的ID标识。地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA)的时区数据库提供。
看下它们的用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Instant.ofEpochSecond(3 ); Instant.ofEpochSecond(3 ,0 ); Instant.ofEpochSecond(2 ,1000000 ); Instant.ofEpochSecond(4 ,-1000000 ); Instant.now(); Instant instantFormDateTime=localDateTime1.toInstant(ZoneOffset.UTC); ZoneId romeZone= TimeZone.getDefault().toZoneId(); Instant instant1=Instant.now(); LocalDateTime timeFromInstant=LocalDateTime.ofInstant(instant1,romeZone); ZoneId romeZone1 = ZoneId.of("Europe/Rome" );
一般传统的Date和LocalDate之间的转换会用到它们(Instant,ZoneId)。
其他 在一些项目中,应用Date还是很多的。我们想使用新的日期API,又不太想改动源代码。 可以写一些转换的工具类,或者使用Java8的一些日期API对Date进行处理。
如Date转化为LocalDateTime,LocalDateTime转换为Date等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static Date localDateTime2Date (LocalDateTime localDateTime) { ZoneId zone = ZoneId.systemDefault(); Instant instant = localDateTime.atZone(zone).toInstant(); return Date.from(instant); } public static LocalDateTime date2LocalDateTime (Date date) { Instant instant = date.toInstant(); ZoneId zone = ZoneId.systemDefault(); return LocalDateTime.ofInstant(instant, zone); }
将字符串日期格式化为LocalDateTime,或者将LocalDateTime转换为字符串时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static String localDateTime2String (Date date) { LocalDateTime localDateTime=date2LocalDateTime(date); return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ).format(localDateTime); } public static LocalDateTime string2LocalDateTime (String string) { DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ); return LocalDateTime.parse(string,df); }
有了这些比较基础的方法,我们某些日期便可以使用Java8处理。比如项目使用Date,在判断闰年,或者时间的加减等,可以封装Java8工具类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static long diffDays (Date date1,Date date2) { LocalDateTime localDateTime1=date2LocalDateTime(date1); LocalDateTime localDateTime2=date2LocalDateTime(date2); return Duration.between(localDateTime1,localDateTime2).toDays(); } public static Date addDays (Date date,int days) { LocalDateTime localDateTime1=date2LocalDateTime(date); LocalDateTime localDateTime2=localDateTime1.minusDays(days); return localDateTime2Date(localDateTime2); } public static boolean isLeapYear (Date date) { LocalDateTime localDateTime=date2LocalDateTime(date); return localDateTime.toLocalDate().isLeapYear(); }
这样对于Date的处理,均使用LocalDateTime处理,虽然整个项目把Date改造成LocalDateTime较困难,但是工具类相当于黑匣子,这样慢慢使用LocalDateTime去处理Date,也是蛮不错的一次体验。
总结 Java8 新的日期API比较优秀的地方是更直观了,使用更简洁。而且不用担心变量污染问题,想想一般的Date,对日期进行操作,如果不小心没有创建新对象,会把传入的Date改变掉,是很不安全的。
而且它也没有时间从1900年起那种莫名其妙的限制,而且获取到的月份值是1-12,不是0-11,也是符合自然的。
其实Java 8 的日期API不单单完成了对于Java日期的优化,其更多的方法更像是一种工具API,如判断是不是闰年,求两日期之差等等常用方法,都被封装在了Java8的新的日期API里面了。