新的日期和时间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
}
}