引言
java 8通过发布新的Date-Time API进一步加强对日期与时间的处理。在旧版的java中,日期时间APi存在诸多问题。java 8引入的新的一系列API,对时间日期处理提供了更好的支持,清楚的定义了时间日期的一些概念,比如说,瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period)。同时,借鉴了Joda库的一些优点,比如将人和机器对时间日期的理解区分开的。
旧版日期时间API的问题
- 非线程安全:java.uttil.Date 是非线程安全的,所有的日期类都是可变的,这是java日期类最大的问题之一。
- 设计很差:java的日期/时间类的定义并不一致,在java.util和java.sql的包中毒有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同提高的名字,这本身就是一个非常糟糕的设计。
- 时区处理麻烦:日期类并不提供国际化,没有时区支持,因此java引入了java.util.Caleandar和java.util.TimeZone类,但他们同样存在上述所有的问题。
新日期时间API
java8在java.time包下提供了很多新的API。以下为两个比较重要的API:
- Local(本地):简化了日期时间的处理,没有时区的问题。
- Zoned(时区):通过制定的时区处理日期时间。
具体的API: - LocalDate:存储不包含时间的日期,比如2020-01-11。可以用来存储生日,周年纪念日,入职日期等。
- LocalTime:存储不包含日期的时间,比如11:07:03.580。
LocalDate-日期
只存储年月日。格式:2020-01-11
创建LocalDate对象
//获取当前日期
LocalDate currDate=LocalDate.now();
System.out.println("当前日期:"+currDate);
//指定日期
LocalDate noeDay=LocalDate.of(2020, 1, 11);
System.out.println("当前日期:"+noeDay);
//通过字符串指定日期
LocalDate towDay=LocalDate.parse("2020-01-11");
System.out.println("当前日期:"+towDay);
运行结果输出:
当前日期:2020-01-11
当前日期:2020-01-11
当前日期:2020-01-11
获取年月日周
// 年
int year = currDate.getYear();
// 月
int month = currDate.getMonthValue();
// 一月的第几天
int day = currDate.getDayOfMonth();
// 一周的第几天
int week = currDate.getDayOfWeek().getValue();
System.out.printf("%d年%d月%d日,星期%d%n", year, month, day, week);
运行结果输出:
2020年1月11日,星期6
比较两个日期是否相同
LocalDate重写了equals方法,让日期的比较也变得简单了。
LocalDate date1 = LocalDate.parse("2020-01-11");
LocalDate date2 = LocalDate.parse("2020-01-11");
System.out.println("比较两个日期是否相同:"+date1.equals(date2));
运行结果输出:
比较两个日期是否相同:true
日期前后比较
//isBefore在之前
boolean isBefore=LocalDate.parse("2020-01-11").isBefore(LocalDate.parse("2020-01-10"));
//isAfter在之后
boolean isAfter=LocalDate.parse("2020-01-11").isAfter(LocalDate.parse("2020-01-10"));
日期加减
System.out.println("当前时间"+LocalDate.now());
System.out.println("当前时间加1天"+LocalDate.now().plusDays(1));
System.out.println("当前时间加1月"+LocalDate.now().plusMonths(1));
System.out.println("当前时间加1年"+LocalDate.now().plusYears(1));
运行结果输出:
当前时间2020-01-11
当前时间加1天2020-01-12
当前时间加1月2020-02-11
当前时间加1年2021-01-11
LocalTime-时间
存储不包含日期的时间,比如11:07:03.580。
LocalTime和LocalDate类似,区别在于LocalDate不包含具体时间,而LocalTime包含具体时间。同样可以使用now、of或parse方法来获得对象。
LocalTime time = LocalTime.now();
LocalTime time2 =LocalTime.of(11, 7,3);
LocalTime time3 =LocalTime.parse("11:07:03");
LocalDate类似它也拥有isBefore、isAfter、获取时间单元等方法,就不再赘述。
需要注意的是,LocalTime获得的时间格式为:11:41:58.904。也就是,HH:mm:ss.nnn,这里nnn是纳秒。
还有一个在实战中查询日期区间时我们经常定义的“23:59:59.99”常量再也不用自己定义了。
//最大时间23:59:59.999999999
LocalTime maxTime =LocalTime.MAX;
//最小时间00:00
LocalTime minTime =LocalTime.MIN;
LocalDateTime-日期和时间的组合
LocalDateTime表示日期和时间组合。同样可以使用now、of或parse方法来获得对象。
LocalDateTime datetime=LocalDateTime.now();
LocalDateTime datetime2=LocalDateTime.of(2020, 1,11,11,54,30);
LocalDateTime datetime3=LocalDateTime.parse("2020-01-11T12:35");
也可以调用LocalDate的atTime()方法或LocalTime的atDate()方法将LocalDate或LocalTime合并成一个LocalDateTime。
LocalDateTime datetime4=LocalDate.now().atTime(LocalTime.now());
LocalDateTime datetime5=LocalTime.now().atDate(LocalDate.now());
LocalDateTime与LocalDate和LocalTime之间可以相互转化。其他日期增减等操作与上面的类似。
Instant-时间戳
用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算。
// 默认获取UTC时区
Instant instant=Instant.now();
System.out.println("当前时间(UTC时区):"+instant);
System.out.println("获取时间戳:"+instant.toEpochMilli());
// 偏移量运算
OffsetDateTime offsetDateTime =instant.atOffset(ZoneOffset.ofHours(8));
System.out.println("当前时间:"+offsetDateTime);
// 以Unix元年为起点,进行偏移量运算
Instant instant2 = Instant.ofEpochSecond(60);
System.out.println(instant2);
运行结果输出为:
当前时间(UTC时区):2020-01-11T05:55:35.493Z
获取时间戳:1578722135493
当前时间:2020-01-11T13:55:35.493+08:00
1970-01-01T00:01:00Z
Duration-获取时间段
Duration的内部实现与Instant类似,但Duration表示时间段,通过between方法创建。
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration=Duration.between(from, to);
//总天数
long days=duration.toDays();
//小时
long hours=duration.toHours();
//分钟
long millis=duration.toMillis();
//秒数
long seconds=duration.getSeconds();
//毫秒数
long minutes=duration.toMinutes();
//纳秒数
long nanos=duration.toNanos();
System.out.println("天数"+days);
System.out.println("小时"+hours);
System.out.println("分钟"+millis);
System.out.println("秒数"+seconds);
System.out.println("毫秒数"+minutes);
System.out.println("纳秒数"+nanos);
运行结果输出:
天数1
小时24
分钟86400000
秒数86400
毫秒数1440
纳秒数86400000000000
Duration对象还可以通过of()方法创建,该方法参数为时间段长度和时间单位。
// 7天
Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
// 60秒
Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);
Period-获取日期段
Period与Duration类似,获取一个时间段,只不过单位为年月日,也可以通过of方法和between方法创建,between方法接收的参数为LocalDate。
Period period=Period.of(2020, 1, 11);
Period period1 = Period.between(LocalDate.now(), LocalDate.now().plusYears(1));
ZonedDateTime-创建时区时间
ZonedDateTime类,用于处理带时区的日期和时间。ZoneId表示不同的时区。大约有40不同的时区。
Set<String> allZoneIds=ZoneId.getAvailableZoneIds();
创建时区:
ZoneId zoneId=ZoneId.of("Asia/Shanghai");
把LocalDateTime转换成特定的时区:
ZonedDateTime zonedDateTime=ZonedDateTime.of(LocalDateTime.now(), zoneId);
获取当前时区:
//获取当前时区
ZoneId z=ZoneId.systemDefault();
获取日期时间:
ZonedDateTime dd = ZonedDateTime.now();
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
时间日期格式化
Java8对日期的格式化操作非常简单,首先看到上面的类大多都提供了parse方法,可以直接通过解析字符串得到对应的对象。
而日期和时间的格式化可通过LocalDateTime的format方法进行格式化。
LocalDateTime dateTime=LocalDateTime.now();
String str=dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(str);
可以使用DateTimeFormatter预置的格式,也可以通过DateTimeFormatter.ofPattern方法来指定格式。