一、Date?Calendar?
-
Java 1.0中,对日期和时间的支持只能依赖java.util.Date类。由于Date糟糕的易用性,Java 1.1中Date的很多方法被废弃了,取而代之的是Calendar类。
-
但是Calendar也并不好用,如月份从0开始,非线程安全,使得代码很容易出错。我们最常用的SimpleDateFormat中相信每个人项目中都有,很多人对这个SimpleDateFormat线程安全的问题没有很直观的认识,下面看这么一个场景
//相信每个人项目中都有这么一个DateUtil、SimpleDateFormat public class DateUtil{ private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd HH"); } //调用方 public class Test{ //20191228 14 System.out.println(DateUtil.simpleDateFormat.format(new Date())); //某个线程按照自己的时区,影响了全局的simpleDateFormat,其他调用者就得出错误结果 //20191228 06 new Thread(() -> { DateUtil.simpleDateFormat.setTimeZone(TimeZone.getTimeZone("+7")); System.out.println(DateUtil.simpleDateFormat.format(new Date())); }).start(); //20191228 06 new Thread(() -> { System.out.println(DateUtil.simpleDateFormat.format(new Date())); }).start(); } 复制代码
-
不仅如此,代码中同时存在Date和Calendar,广大Java程序员更加困惑。因此涉及到日期时间大部分情况下都是借助于第三方库来实现日期时间功能,如joda-time。其实Java 8 的日期时间API能看到很多joda-time的特性。
-
顺应潮流,Java 8的大版本中提供了一系列日期和时间支持的API。本文主要介绍这些新增的API,让你不用从上到下new Date()一把梭。
二、Java 8中日期、时间
- 本文主要从Java 8中的日期、时间两大块来介绍这些新增的API。
LocalDate
- LocalDate类实例是一个不可变对象,只包含日期,不含有时间信息,也不带时区信息
//今天
LocalDate now = LocalDate.now();
System.out.println(now);
//年、月、日构造
LocalDate date = LocalDate.of(2018, 12, 12);
System.out.println(date);
复制代码
LocalTime
- LocalTime只包含时间信息,不包含日期信息。
LocalTime time = LocalTime.now();
LocalTime time1 = LocalTime.parse("12:4:20");
复制代码
LocalDateTime
- LocalDate和LocalTime的结合体,既有时间信息,又有日期信息。
//今天当前时间
LocalDateTime ltime1 = LocalDateTime.now();
//指定年、月、日、时、分、秒
LocalDateTime time = LocalDateTime.of(2019, 12, 26, 12, 11, 34);
//LocalDate + 时间信息
LocalDateTime ltime2= LocalDate.now().atTime(12,11,23);
//LocalTime + 日期信息
LocalTime.now().atDate(LocalDate.now());
复制代码
Duration、Period
- Duration/Period均不是一个时间点,而是时间间隔。Period类表示年、月、日时间间隔,Duration是一个表示秒/纳秒的时间间隔。即Period基于日期值,而Duration基于时间值。
- 这两个类是为了日期、时间的度量服务的。
//几天前的实现
LocalDate date1 =LocalDate.now();
LocalDate date2 = LocalDate.of(2019, 12, 11);
Period period = Period.between(date1, date2);
//-17
System.out.println(period.getDays());
复制代码
TemporalAdjuster
- Java 8 中内置的一个工具类,可以更加灵活处理日期。
- Java 8中提供了大量预定义的TemporalAdjuster,在一些复杂的时间操作下非常好用。
//可以看出这个类的作用是将一个Temporal转成另一个Temporal
//LocalDate、LoclaTime、LocalDateTime等都实现了Temporal接口
@FunctionInterface
public function TemporalAdjuster{
Temporal adjustInto(Temporal temporal);
}
复制代码
方法名 | 描述 |
---|---|
dayOfWeekInMonth | 返回同一个月中每周第几天 |
firstDayOfMonth | 返回当月第一天 |
firstDayOfNextMonth | 返回下月第一天 |
firstDayOfNextYear | 返回下一年的第一天 |
firstDayOfYear | 返回本年的第一天 |
firstInMonth | 返回同一个月中第一个星期几 |
lastDayOfMonth | 返回当月的最后一天 |
lastDayOfNextMonth | 返回下月的最后一天 |
lastDayOfNextYear | 返回下一年的最后一天 |
nextOrSame / previousOrSame | 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回 |
DateTimeFormatter
- 主要用于日期格式化操作,format()/parse()。
//format
LocalDateTime dateTime = LocalDateTime.now();
String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE); // 20170105
String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2017-01-05
String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME); // 14:20:16.998
String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));// 2017-01-05
//parse
String strDate6 = "2017-01-05";
String strDate7 = "2017-01-05 12:30:05";
LocalDate date = LocalDate.parse(strDate6, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDateTime dateTime1 = LocalDateTime.parse(strDate7, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
复制代码
三、常用功能
LocalDate、LocalTime
//今天12:00:00时间戳
LocalDate.now().atTime(12,0,0).toInstant(ZoneOffset.of("+8")).getEpochSecond());
//今天0:10:00时间戳
LocalDate.now().atTime(0,10,0).toInstant(ZoneOffset.of("+8")).getEpochSecond());
//几天/周/年前、后
//5天前
LocalDate.now().minusDays(5);
//1天后
LocalDate.now().plusDays(1);
//2周后
LocalDate.now().plusWeeks(2);
//几小时/分钟前、后
//2小时前
LocalTime.now().minusHours(2);
//5分钟后
LocalTime.now().plusMinutes(5);
复制代码
Date、LocalDate、LocalTime、LocalDateTime
//Date转LocalDateTime
Date date = new Date();
LocalDateTime time1 = LocalDateTime.ofInstant(date.toInstant(),ZoneOffset.of("+8"))
//Date转LocalDate
LocalDate date1 = LocalDateTime.ofInstant(date.toInstant(),ZoneOffset.of("+8"))
.toLocalate()
//LocalDate无法直接转Date,因为LocalDate不含时间信息
//LocalDateTime转Date
LocalDateTime localDateTime3 = LocalDateTime.now();
Instant instant3 = LocalDateTime3.atZone(ZoneId.systemDefault()).toInstant();
Date date3 = Date.from(instant);
复制代码
DateTimeFormatter的使用
//前端传递过来的字符串20191212,后端需要 2019-12-12
//除了replace(),更优雅的方式
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
LocalDate formatted = LocalDate.parse("20191212",formatter);
//2019-12-12
System.out.println(formatted);
//前端传递过来的字符串2019-12-12,后端需要 20191212
LocalDate.parse("2019-12-12").format(DateTimeFormatter.BASIC_ISO_DATE));
//前端传递过来的字符串2019年12月12日,后端需要 2019-12-12
System.out.println(LocalDate.parse("2019年12月12日",DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
复制代码
TemporalAdjuster
//场景中需要使用距当前最近的周一
//返回上一个周一,如果今天是周一则返回今天
LocalDate date1 =LocalDate.now().with(previousOrSame(DayOfWeek.MONDAY));
//下一个周一/当天
LocalDate date1 =LocalDate.now().with(nextOrSame(DayOfWeek.MONDAY));
//本月最后一天
System.out.println(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()));
//明年第一天
System.out.println(LocalDate.now().with(TemporalAdjusters.firstDayOfNextYear()));
复制代码
四、小结
- 本文主要介绍了Java 8中的日期时间API中常用的LocalDate、LocalTime、LocalDateTime、DateTimeFormatter、TemporalAdjuster,以及与Date之间的互转。极大地丰富了我们在日期时间开发中的选择,而且解决了Date、SimpleDateFormat等线程安全的问题。
- 在复杂日期时间场景下善于使用TemporalAdjuster,真香。