前言
从现实上看,Java8以前似乎没有对日期API特别重视,如比较常用的Date类,并没有一个专门的包进行囊括,而SimpleDateFormat作为一个常用的日期格式化工具类,居然放在了java.text包下。
甚至SimpleDateFormat在多线程访问的情况下可能还会引起异常,JDK的原文如是If multiple threads access a format concurrently, it must be synchronized。
这些类中,对于日期的操作方法也是很让人费解,不好掌握,而且很多的方法都是已经过时。
天下早已苦之旧矣!(难怪我之前学这块学的不好,原来是有原因的)
而在Java8之后,对于日期的相关操作已经容易了很多,让我们一起来了解一下吧!
日期时间
关于日期、时间有三个常用的类:
- LocalDateTime:默认的日期格式,yyyy-MM-dd HH:mm:ss.fff
- LocalDate:yyyy-MM-dd
- LocalTime:HH:mm:ss.fff
从上面日期的格式化上看,LocalDateTime = LocalDate + LocalTime
LocalDate就是年月日类
LocalTime就是时分秒类
需要注意的一点是,这三个类默认都是取的本地时区(系统的默认时区),因为地理位置的不同,时间的表示也存在差异,目前国内使用的是 上海的时区,Asia/Shanghai
从LocalDateTime 中,可以很简单的获得LocalDate和LocalTime
LocalDateTime now = LocalDateTime.now();
LocalDate localDate = now.toLocalDate();
LocalTime localTime = now.toLocalTime();
复制代码
LocalDate也可以加上LocalTime,进而转为LocalDateTime
LocalDateTime localDateTime = localDate.atTime(localTime);
复制代码
LocalTime亦然
LocalDateTime localDateTime = localTime.atDate(localDate);
复制代码
三个日期类的方法比较雷同,这里演示一下LocalDateTime的使用
LocalDateTime now = LocalDateTime.now();
System.out.println("也可以指定时间:" + LocalDateTime.of(2020, 4, 15, 9, 30, 2));
System.out.println("当前时刻:" + now);
System.out.println("当前年:" + now.getYear());
System.out.println("当前月:" + now.getMonthValue());
System.out.println("当前日:" + now.getDayOfMonth());
System.out.println("当前时:" + now.getHour());
System.out.println("当前分:" + now.getMinute());
System.out.println("当前秒:" + now.getSecond());
System.out.println("加2年:" + now.plusYears(2l));
System.out.println("减3个月:" + now.minusMonths(3l));
System.out.println("减50天:" + now.minusDays(50l));
System.out.println("跳到2035年基本实现社会主义现代化:" + now.withYear(2035));
System.out.println("每次计算都会返回一个新对象,原来的对象不会受影响哦!" + now);
复制代码
这些简单的API调用各位小伙伴了解一下,有个印象即可,在编码的时候,有代码提示,编程障碍比较小。而日期、时间、时间戳的互相转换和格式化,才是重点,这方面不熟悉,会造成比较大的编程障碍。
时区
Java8提供了ZoneId标识各个国家所使用的时区
可以使用systemDefault()方法获得当前系统的默认时区,如Asia/Shanghai
ZoneId zoneId = ZoneId.systemDefault();
复制代码
获取所有的时区
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
复制代码
根据时区名获取时区对象
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
复制代码
带时区的日期时间,ZonedDateTime,该类的日期格式与LocalDateTime的区别是,在尾部增加了时区。没有衍生的ZonedDate和ZonedTime。
如:获取当前带时区的日期时间并打印
System.out.println(ZonedDateTime.now());
复制代码
打印结果:
2020-07-07T15:17:23.737+08:00[Asia/Shanghai]
复制代码
时间戳
计算机中的时间戳,是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
时间戳用Instant类进行表示
Unix元年的时间戳
Instant.EPOCH
复制代码
获得当前的时间戳
Instant now = Instant.now();
复制代码
将时间戳转为总秒数
long second = now.getEpochSecond();
复制代码
将时间转为总毫秒值
long second = now.toEpochMilli();
复制代码
转秒数的方法是getEpochSecond(),转毫秒值的方法是toEpochMilli(),方法名规范好像有问题,不知道是什么高深的原因?
时间戳配合时区生成LocalDateTime
Instant now = Instant.now();
LocalDateTime dateTime = LocalDateTime.ofInstant(now, ZoneId.systemDefault());
复制代码
转换的示例图
格式化
对比以往格式化使用的SimpleDateFormat, DateTimeFormatter是最新的日期格式化类,提供很多了预定义的时间格式。
以LocalDateTime举例,下面是一些简单的日期时间格式化操作:
LocalDateTime now = LocalDateTime.now();
System.out.println("yyyy-MM-dd HH:mm:ss.fff : " + now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
System.out.println("yyyy-MM-dd : " + now.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println("HH:mm:ss.fff : " + now.format(DateTimeFormatter.ISO_LOCAL_TIME));
// 自定义格式化
System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")));
复制代码
打印结果:
yyyy-MM-dd HH:mm:ss.fff : 2020-07-07T19:21:24.728 //相当于没格式化哈哈
yyyy-MM-dd : 2020-07-07
HH:mm:ss.fff : 19:21:24.728
2020年07月07日 19:21:24
复制代码
从上面可以看出来
ISO_LOCAL_DATE_TIME = ISO_LOCAL_DATE + ISO_LOCAL_TIME,其中的ISO是指国际标准化组织的缩写
DateTimeFormatter也可以格式化时间戳,不过需要注意的是,必须指定时区,没有时区,格式化程序将不知道如何将即时转换为人类日期时间字段,因此会引发异常。
示例代码:
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.systemDefault());
System.out.println(formatter.format(Instant.now()));
复制代码
打印结果:
2020-07-07T19:42:28.805
复制代码
时间戳自定义格式化与上面LocalDateTime的相同,不过多赘述。
时间段
关于时间段,有两个常用的操作类,一个是Duration,另一个是Period,Period用于计算日期, Duration用于计算时间,如下:
- Duration:时秒分
- Period:年月日
这两个类的方法都差不多,我们以Duration进行演示:
获得当前时间与未来时分秒的时间段:
LocalTime now = LocalTime.now();
LocalTime hour = now.plusHours(2);
LocalTime minute = now.plusMinutes(10);
LocalTime second = now.plusSeconds(30);
System.out.println(Duration.between(now, hour));
System.out.println(Duration.between(now, minute));
System.out.println(Duration.between(now, second));
复制代码
打印结果:
PT2H
PT10M
PT30S
复制代码
注意,默认的打印结果为ISO国际标准化组织规定的日期格式,PT2H中的H,表示Hour小时,M代表Minute分钟,S代表Second秒数
其他示例操作:
LocalTime now = LocalTime.now();
LocalTime hour = now.plusHours(2);
Duration duration = Duration.between(now, hour);
System.out.println("获取时间段的秒数:" + duration.getSeconds());
System.out.println("获取时间段的毫秒数:" +duration.toMillis());
System.out.println("获取时间段的天数:" +duration.toDays());
复制代码
打印结果:
获取时间段的秒数:7200
获取时间段的毫秒数:7200000
获取时间段的天数:0
复制代码
Duration类也有相关的时分秒加减方法,不过多赘述。
调整器
LocalDateTime、Duration、Instant等,尽管已经提供了很多日期加减的方法,但还是比较局限性,如下个星期天,下个结婚生日等需求便难以满足。
为此,我们可以使用,调整器进行日期的调整。
日期校正器类为TemporalAdjuster,该类是一个接口,而Java8 也提供了其实现的工具类,TemporalAdjusters一些的使用方法如下:
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:"+now);
System.out.println("下周日:" + now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)));
System.out.println("下个月的第一天:" + now.with(TemporalAdjusters.firstDayOfNextMonth()));
System.out.println("下一个工作日:" + now.with(l -> {
LocalDateTime dateTime = (LocalDateTime) l;
//先获取周几
DayOfWeek dayOfWeek = dateTime.getDayOfWeek();
if (DayOfWeek.FRIDAY.equals(dayOfWeek)){
//周五加三天等于工作日
return dateTime.plusDays(3);
}else if(DayOfWeek.SATURDAY.equals(dayOfWeek)){
//周六加两天
return dateTime.plusDays(2);
}
//其他均加一天
return dateTime.plusDays(1);
}));
复制代码
打印结果:
当前时间:2020-07-08T00:10:05.549
下周日:2020-07-12T00:10:05.549
下个月的第一天:2020-08-01T00:10:05.549
下一个工作日:2020-07-09T00:10:05.549
复制代码
DayOfWeek这个类,有七个常量值,分别为周一到周日,如MONDAY,TUESDAY,WEDNESDAY等。
Date转换
Date类目前有很多过时、不推荐的方法,如果项目中有使用到该类的话,可以考虑进行替换。
新的日期API可以使用时间戳的毫秒值通过Date类的构造方法,new一个Date对象
如下:
long milli = Instant.now().toEpochMilli();
Date date = new Date(milli);
复制代码
特别注意!该构造方法传的单位是毫秒值
Date可以通过toInstant方法转换为一个时间戳
Instant instant = new Date().toInstant();
复制代码




近期评论