本篇文章,承接上文,主要介绍的是java 8中的时间API
JAVA 8
自从 Java 8 发布以后,古老的 java.util.Date 不再是我们 Java 里操作日期时间的唯一选择。Java 8 借鉴第三方开源库 Joda-Time 的优秀设计,重新设计了一套新的日期时间 API,这套新的日期 API 是 JSR-310 规范的实现。
需要强调的一点Java 8中的类基本上都是不可变得类,类似于String,不存在线程安全问题
JAVA 8 API中常用的类
- LocalDate:表示不带时间的日期
- LocalTime:表示不带日期的时间
- LocalDateTime:日期和时间类
- ZoneId:时区
- ZonedDateTime:一个带时区的完整时间
- Instant:Unix 时间,它代表的是时间戳,比如 2018-01-14T02:20:13.592Z
- Clock:获取某个时区下当前的瞬时时间,日期或者时间
- Duration:表示一个绝对的精确跨度,使用毫秒为单位
- Period:这个类表示与 Duration 相同的概念,但是以人们比较熟悉的单位表示,比如年、月、周
- DateTimeFormatter:格式化输出
- TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等,比较复杂的时间操作
LocalDate、LocalTime、LocalDateTime
Java 8 中将日期和时间进行分离,LocalDate 是用来表示无时间的日期的,也不附带任何与时区相关的信息。
LocalTime 类关注时分秒,而 LocalDateTime是两者的结合体。
它们的实例都是一个不可变的对象,因此任何修改操作都会返回一个新的实例。
@Test
public void test4() {
LocalDate date = LocalDate.of(2014, Month.JUNE, 10);
int year = date.getYear(); // 2014
Month month = date.getMonth(); // 6月
int dom = date.getDayOfMonth(); // 10
DayOfWeek dow = date.getDayOfWeek(); // 星期二
int len = date.lengthOfMonth(); // 30 (6月份的天数)
boolean leap = date.isLeapYear(); // false (不是闰年)
}
@Test
public void test5() {
LocalDate date = LocalDate.of(2019, Month.JANUARY, 10);
date = date.withYear(2020);
System.out.println(date);
date = date.plusMonths(2);
System.out.println(date);
date = date.minusDays(1);
System.out.println(date);
//ChronoUnit 则用来表示这个时间单位
date = date.plus(3, ChronoUnit.DAYS);
System.out.println(date);
}
结果:
2020-01-10
2020-03-10
2020-03-09
2020-03-12
/**
* LocalDate、LocalTime、LocalDateTime三个类组合方法
*/
@Test
public void test8() {
LocalDate date = LocalDate.of(2018,11,16);
LocalTime time = LocalTime.of(12,23,20);
LocalDateTime dateTime = LocalDateTime.of(date,time);
System.out.println(dateTime);
//LocalDate结合LocalTime成一个LocalDateTime
LocalDateTime dateTime2 = date.atTime(time);
System.out.println(dateTime2); //2018-11-16T12:23:20
}
结果:
2018-11-16T12:23:20
2018-11-16T12:23:20
LocalDateTime的其他方法跟LocalDate和LocalTime相似。这种相似的方法模式非常有利于API的学习。
下面总结了用到的方法前缀:
- of: 静态工厂方法,从组成部分中创建实例
- from: 静态工厂方法,尝试从相似对象中提取实例。from()方法没有of()方法类型安全
- now: 静态工厂方法,用当前时间创建实例
- parse: 静态工厂方法,总字符串解析得到对象实例
- get: 获取时间日期对象的部分状态
- is: 检查关于时间日期对象的描述是否正确
- with: 返回一个部分状态改变了的时间日期对象拷贝
- plus: 返回一个时间增加了的、时间日期对象拷贝
- minus: 返回一个时间减少了的、时间日期对象拷贝
- to: 把当前时间日期对象转换成另外一个,可能会损失部分状态
- at: 用当前时间日期对象组合另外一个,创建一个更大或更复杂的时间日期对象
- format: 提供格式化时间日期对象的能力
TemporalAdjuster
该类主要用于复杂的时间计算操作功能
@Test
public void test9() {
LocalDate date = LocalDate.now();
date = date.with(lastDayOfMonth());
System.out.println(date);
date = date.with(nextOrSame(WEDNESDAY));
System.out.println(date);
}
2019-01-31
2019-02-06
import static java.time.DayOfWeek.WEDNESDAY;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
@Test
public void test12() {
LocalDate localDate = LocalDate.now();
TemporalAdjuster firstDayOfMonth = (temporal) -> temporal.with(DAY_OF_MONTH, 10);
System.out.println(localDate.plusMonths(1).with(firstDayOfMonth));
// 等价于下面语句
System.out.println(localDate.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth()));
// 计算下一个工作日的日期
TemporalAdjuster nextWorkDay = TemporalAdjusters.ofDateAdjuster(
tdate->{
DayOfWeek work = tdate.getDayOfWeek();
int addDays=0;
if (work.equals(DayOfWeek.FRIDAY)) {
addDays=3;
}else if(work.equals(DayOfWeek.SATURDAY)){
addDays=2;
}else {
addDays=1;
}
return tdate.plusDays(addDays);
}
);
LocalDate localDate1 = LocalDate.now().with(nextWorkDay);
System.out.println(localDate1);
}
2019-02-10
2019-02-01
2019-01-11
Duration 与 Period
Duration 和 Period代表时间上的间隔,Duration代表间隔是以秒数位单位,而Period 的间隔是以年月日来衡量一个时间段
@Test
public void test10() {
LocalDateTime from = LocalDateTime.of(2018, Month.JANUARY, 5, 10, 7, 0); // 2018-01-05 10:07:00
LocalDateTime to = LocalDateTime.of(2018, Month.FEBRUARY, 5, 10, 7, 0); // 2018-02-05 10:07:00
Duration duration = Duration.between(from, to); // 表示从 2018-01-05 10:07:00 到 2018-02-05 10:07:00 这段时间
Duration d = Duration.ofSeconds(6000);
System.out.println("6000秒相当于" + d.toMinutes() + "分");
System.out.println("6000秒相当于" + d.toHours() + "小时");
System.out.println("6000秒相当于" + d.toDays() + "天");
LocalDate start = LocalDate.of(2018, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2020, Month.NOVEMBER, 11);
System.out.println("相隔月数:"+Period.between(start, end).getMonths());
System.out.println("相隔天数:"+Period.between(start, end).getDays());
/**
* Period 得到的是差值的绝对值(对应年月日直接计算数学上的差值),而并不表示真正的区间距离。
*/
long distanceMonth = start.until(end, ChronoUnit.MONTHS);
long distanceDay= start.until(end, ChronoUnit.DAYS);
System.out.println(distanceMonth);
System.out.println(distanceDay);
}
运行结果:
6000秒相当于100分
6000秒相当于1小时
6000秒相当于0天
相隔月数:10
相隔天数:10
34
1045
Instant
Instant 表示时间线上的一点(与 Date 类似),而不需要任何上下文信息,例如时区。概念上讲,它只是简单地表示自 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC)开始的秒数。不提供处理人类意义上的时间单位
需要注意的是:这里存储的是与UTC1970的秒数间隔,其实我现在是2019-01-09早上10点,在UTC下的凌晨2点
@Test
public void test11() {
Instant instant1 = Instant.now();
System.out.println(instant1); //1970-01-01T00:00:00Z
Instant instant2 = Instant.parse("2018-11-11T10:12:35.342Z");
System.out.println(instant2); //2018-11-11T10:12:35.342Z
// 在instant3的基础上添加5小时4分钟
Instant instant3 = instant2.plus(Duration.ofHours(5).plusMinutes(4));
System.out.println(instant3); //2018-11-11T15:16:35.342Z
//java.util.Date与Instant可相互转换
Instant timestamp = new Date().toInstant();
Date.from(Instant.now());
}
2019-01-10T02:46:10.532Z
2018-11-11T10:12:35.342Z
2018-11-11T15:16:35.342Z
ZoneId 和 ZonedDateTime
Java 8 不仅将日期和时间进行了分离,同时还有时区。Java 使用 ZoneId 来标识不同的时区。
ZonedDateTime 是存储了与时区关联的 LocalDateTime
// 所有可用的zoneid
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
//所有合法的“区域/城市”字符串
zoneIds.forEach(System.out::println);
ZoneId china = ZoneId.of("Asia/Shanghai");
ZonedDateTime dateAndTimeInChina = ZonedDateTime.of(LocalDateTime.now(), china);
System.out.println("特定时区下的日期和时间 : " + dateAndTimeInChina);
ZoneId america = ZoneId.of("America/New_York");
System.out.println(ZonedDateTime.now(america));
解析和格式化
java.time.format包是专门用来格式化输出时间/日期的。这个包围绕DateTimeFormatter类和它的辅助创建类DateTimeFormatterBuilder展开
静态方法加上DateTimeFormatter中的常量,是最通用的创建格式化器的方式。包括:
- 常用ISO格式常量,如ISO_LOCAL_DATE
- 字母模式,如ofPattern(“dd/MM/uuuu”)
- 本地化样式,如ofLocalizedDate(FormatStyle.MEDIUM)
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/uuuu");
LocalDate date = LocalDate.parse("24/06/2014", f);
String str = date.format(f);