新的日期和时间API

LocalDate、 LocalTime、 Instant、 Duration 以及 Period

使用 LocalDate 和 LocalTime

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
//2019-12-04
LocalDate date = LocalDate.of(2019, 12, 4);
//2019
int year = date.getYear();
//DECEMBER
Month month = date.getMonth();
//4
int day = date.getDayOfMonth();
//WEDNESDAY
DayOfWeek dayOfWeek = date.getDayOfWeek();
//31
int len = date.lengthOfMonth();
//false
boolean leap = date.isLeapYear();
LocalDate today = LocalDate.now();
//2019
int year1 = today.get(ChronoField.YEAR);
//12
int month1 = today.get(ChronoField.MONTH_OF_YEAR);
//5
int day1 = today.get(ChronoField.DAY_OF_MONTH);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LocalTime time = LocalTime.of(14, 56, 30);
//14
int hour = time.getHour();
//56
int minute = time.getMinute();
//30
int second = time.getSecond();
//2019-12-04
LocalDate date2 = LocalDate.parse("2019-12-04");
//14:56:30
LocalTime time2 = LocalTime.parse("14:56:30");

LocalDateTime

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
//2014-03-18 13:45:20
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
System.out.println(dt1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//2014-03-18 13:45:20
LocalDateTime dt2 = LocalDateTime.of(LocalDate.parse("2014-03-18"), LocalTime.parse("13:45:20"));
System.out.println(dt2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//2014-03-18 13:45:20
LocalDate date = LocalDate.parse("2014-03-18");
LocalDateTime dt3 = date.atTime(LocalTime.parse("13:45:20"));
System.out.println(dt3.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//2014-03-18 13:45:20
LocalDateTime dt4 = date.atTime(13, 45, 20);
System.out.println(dt4.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//2014-03-18 13:45:20
LocalTime time = LocalTime.of(13, 45, 20);
LocalDateTime dt5 = time.atDate(date);
System.out.println(dt5.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//2019-12-04 14:56:30
LocalDateTime dateTime = LocalDateTime.parse("2019-12-04 14:56:30",DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//提取LocalDate或者LocalTime组件:
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();

Instant 机器的日期和时间格式

1
2
3
4
5
6
7
8
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
//2秒之后再加上100万纳秒(1秒)
Instant.ofEpochSecond(2, 1_000_000_000);
//4秒之前的100万纳秒(1秒)
Instant.ofEpochSecond(4, 1_000_000_000);
//获取当前时刻的时间戳
System.out.println(Instant.now().getEpochSecond());

定义 Duration 或 Period

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
LocalTime time1 = LocalTime.parse("11:27:01");
LocalTime time2 = LocalTime.parse("11:27:02");
//1
Duration d1 = Duration.between(time1, time2);
System.out.println(d1.getSeconds());
//-86400
LocalDateTime dateTime1 = LocalDateTime.parse("2019-12-05 13:37:04", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime dateTime2 = LocalDateTime.parse("2019-12-04 13:37:04", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Duration d2 = Duration.between(dateTime1,dateTime2);
System.out.println(d2.getSeconds());
//1
Period period = Period.between(LocalDate.of(2019, 12, 5), LocalDate.of(2020, 1, 25));
System.out.println(period.getMonths());
//创建Duration Period对象
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes1 = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

Duration类和Period类共享了很多相似的方法,参见表所示。

方法名 是否是静态方法 方法描述
between 是 创建两个时间点之间的interval
from 是 由一个临时时间点创建interval
of 是 由它的组成部分创建interval的实例
parse 是 由字符串创建interval的实例
addTo 否 创建该interval的副本,并将其叠加到某个指定的temporal对象
get 否 读取该interval的状态
isNegative 否 检查该interval是否为负值,不包含零
isZero 否 检查该interval的时长是否为零
minus 否 通过减去一定的时间创建该interval的副本
multipliedBy 否 将interval的值乘以某个标量创建该interval的副本
negated 否 以忽略某个时长的方式创建该interval的副本
plus 否 以增加某个指定的时长的方式创建该interval的副本
subtractFrom 否 从指定的temporal对象中减去该interval

操纵、 解析 和 格式化 日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//2014-03-18
LocalDate date1 = LocalDate.of(2014, 3, 18);
//以比 较 直观 的 方式 操纵 LocalDate 的 属性
//2011-03-18
LocalDate date2 = date1.withYear(2011);
//2011-03-25
LocalDate date3 = date2.withDayOfMonth(25);
//2011-09-25
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);
//以相对方式修改LocalDate对象的属性
//2014-03-18
LocalDate localDate1 = LocalDate.of(2014, 3, 18);
//2014-03-25
LocalDate localDate2 = localDate1.plusWeeks(1);
//2011-03-25
LocalDate localDate3 = localDate2.minusYears(3);
//2011-09-25
LocalDate localDate4 = localDate3.plus(6, ChronoUnit.MONTHS);

像LocalDate、LocalTime、LocalDateTime以及Instant这样表示时间点的日期-时间类提供了大量通用的方法,表对这些通用的方法进行了总结。

方法名 是否是静态方法 方法描述
from 是 依据传入的Temporal对象创建对象实例now是依据系统时钟创建Temporal对象
of 是 由Temporal对象的某个部分创建该对象的实例
parse 是 由字符串创建Temporal对象的实例
atOffset 否 将Temporal对象和某个时区偏移相结合
atZone 否 将Temporal对象和某个时区相结合
format 否 使用某个指定的格式器将Temporal对象转换为字符串(Instant类不提供该方法)
get 否 读取Temporal对象的某一部分的值minus否创建Temporal对象的一个副本,通过将当前Temporal对象的值减去一定的时长创建该副本
plus 否 创建Temporal对象的一个副本,通过将当前Temporal对象的值加上一定的时长创建该副本
with 否 以该Temporal对象为模板,对某些状态进行修改创建该对象的副本

使用 TemporalAdjuster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//2014-03-18
LocalDate date1 = LocalDate.of(2014, 3, 18);
//创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象
//2014-03-23
LocalDate date2 = date1.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY));
//2014-03-31
LocalDate date3 = date2.with(TemporalAdjusters.lastDayOfMonth());
//定制的TemporalAdjuster
//能够计算明天的日期,同时过滤掉周六和周日这些节假日。
LocalDate date4 = date3.with(temporal -> {
DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) {
dayToAdd = 3;
} else if (dow == DayOfWeek.SATURDAY) {
dayToAdd = 2;
}
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
//2014-04-01
System.out.println(date4.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

TemporalAdjuster类中的工厂方法

方法名 方法描述
dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天
firstDayOfMonth 创建一个新的日期,它的值为当月的第一天
firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天
firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天
firstDayOfYear 创建一个新的日期,它的值为当年的第一天
firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值
lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天
lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天
lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天
lastDayOfYear 创建一个新的日期,它的值为今年的最后一天
lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值next/previous创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期
nextOrSame/previousOrSame 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象

打印输出及解析日期-时间对象

字符 含义 示例
y 年 yyyy-1996
M 月 MM-07
d 月中的天数 dd-02
D 年中的天数 121
E 星期几 星期四
H 小时数(0-23) HH-23
h 小时数(1-12) hh-11
m 分钟数 mm-02
s 秒数 ss-03
Z 时区 +0800
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
32
33
34
35
36
37
38
LocalDate date = LocalDate.of(2014, 3, 18);
//创建格式器最简单的方法是通过它的静态工厂方法以及常量。像BASIC_ISO_DATE和ISO_LOCAL_DATE这样的常量是DateTimeFormatter类的预定义实例。
//20140318
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
//2014-03-18
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
//可以通过解析代表日期或时间的字符串重新创建该日期对象。所有的日期和时间API都提供了表示时间点或者时间段的工厂方法,你可以使用工厂方法parse达到重创该日期对象的目的:
LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE);
//和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。
// 所以,你能够以单例模式创建格式器实例,就像DateTimeFormatter所定义的那些常量,并能在多个线程间共享这些实例。
// DateTimeFormatter类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器。
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
//18/03/2014
String formattedDate = date.format(dateTimeFormatter);
LocalDate localDate = LocalDate.parse(formattedDate, dateTimeFormatter);
//创建一个本地化的DateTimeFormatter
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
//18. marzo 2014
String italianFormatDate = date.format(italianFormatter);
//更加细粒度的控制,DateTimeFormatterBuilder类还提供了更复杂的格式器,你可以选择恰当的方法,一步一步地构造自己的格式器。
//另外,它还提供了非常强大的解析功能,比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不精确地匹配指定的模式)、填充,以及在格式器中指定可选节。
DateTimeFormatter italianFormatter1 = new DateTimeFormatterBuilder().appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive().toFormatter(Locale.ITALIAN);
//18. marzo 2014
String italianFormatDate1 = date.format(italianFormatter1);

处理不同的时区和历法

之前你看到的日期和时间的种类都不包含时区信息。时区的处理是新版日期和时间API新增加的重要功能,使用新版日期和时间API时区的处理被极大地简化了。新的java.time.ZoneId类是老版java.util.TimeZone的替代品。它的设计目标就是要让你无需为时区处理的复杂和繁琐而操心,比如处理日光时(DaylightSavingTime,DST)这种问题。跟其他日期和时间类一样,ZoneId类也是无法修改的。时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。你可以简单地通过调用ZoneId的getRules()得到指定时区的规则。每个特定的ZoneId对象都由一个地区ID标识,比如:

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
//地区ID都为"{区域}/{城市}"的格式,这些地区集合的设定都由英特网编号分配机构(IANA)的时区数据库提供。
ZoneId romeZone = ZoneId.of("Europe/Rome");
//你可以通过Java8的新方法toZoneId将一个老的时区对象转换为ZoneId:
ZoneId zoneId = TimeZone.getDefault().toZoneId();
//为时间点添加时区信息
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 01);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
//2014-03-18 13-45-01
String formatDateTime = zdt2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss"));
ZonedDateTime zdt3 = dateTime.atZone(zoneId);
//2014-03-18 13-45-01
String formatDateTime1 = zdt3.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss"));
Instant instant = Instant.now();
ZonedDateTime zdt4 = instant.atZone(romeZone);
//2019-12-09 09-55-29
String formatDateTime2 = zdt4.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss"));
ZonedDateTime zdt5 = instant.atZone(zoneId);
//2019-12-09 16-55-29
String formatDateTime13 = zdt5.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss"));

下图对ZonedDateTime的组成部分进行了说明,相信能够帮助你理解LocaleDate、LocalTime、LocalDateTime以及ZoneId之间的差异。

java8-1

Convert between LocalDateTime to Date

Date to LocalDateTime

1
2
3
4
5
6
7
Date todayDate = new Date();
LocalDateTime ldt = Instant.ofEpochMilli( todayDate.getTime() )
.atZone( ZoneId.systemDefault() )
.toLocalDateTime();
System.out.println(ldt); //2018-07-15T11:53:31.863

LocalDateTime to Date

1
2
3
4
5
LocalDateTime localDateTime = LocalDateTime.now();
Date date = Date.from( localDateTime.atZone( ZoneId.systemDefault()).toInstant());
System.out.println(date); //Sun Jul 15 11:58:26 IST 2018

DateUtils Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class DateUtils {
public static Date asDate(LocalDate localDate) {
return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
public static Date asDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
public static LocalDate asLocalDate(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
public static LocalDateTime asLocalDateTime(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}

How to use

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.time.LocalDateTime;
import java.util.Date;
public class Main
{
public static void main(String[] args)
{
Date date = DateUtils.asDate(LocalDateTime.now());
System.out.println(date); //Sun Jul 15 12:08:44 IST 2018
LocalDateTime today = DateUtils.asLocalDateTime(new Date());
System.out.println(today); //2018-07-15T12:08:44.492
}
}