Java8 之时间处理

1. 本地时间

1. 概念

LocalDateLocalTimeLocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。

2. 常用方法

  • now:静态方法,根据当前时间创建对象
  • of:静态方法,根据指定日期/时间创建对象
  • plusDays,plusWeeks,plusMonths,plusYears:向当前 LocalDate 对象添加几天、几周、几个月、几年
  • minusDays,minusWeeks,minusMonths,minusYears:从当前 LocalDate 对象减去几天、几周、几个月、几年
  • plus,minus:添加或减少一个 DurationPeriod
  • withDayOfMonth,withDayOfYear,withMonth,withYear:将月份天数、年份天数、月份、年份修改为指定的值并返回新的 LocalDate 对象
  • getDayOfYear:获得年份天数(1~366)
  • getDayOfWeek:获得星期几(返回一个 DayOfWeek 枚举值)
  • getMonth:获得月份, 返回一个 Month 枚举值
  • getMonthValue:获得月份(1~12)
  • getYear:获得年份
  • until:获得两个日期之间的 Period 对象,或者指定 ChronoUnits 的数字
  • isBefore,isAfter:比较两个 LocalDate
  • isLeapYear:判断是否是闰年
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1. 获取当前系统时间
LocalDateTime localDateTime1 = LocalDateTime.now();
System.out.println(localDateTime1); // 运行结果:2019-10-27T13:49:09.483

// 2. 指定日期时间
LocalDateTime localDateTime2 = LocalDateTime.of(2019, 10, 27, 13, 45,10);
System.out.println(localDateTime2); // 运行结果:2019-10-27T13:45:10

// 3. 计算时间:+3年-3月
LocalDateTime localDateTime3 = localDateTime1.plusYears(3).minusMonths(3);
System.out.println(localDateTime3); // 运行结果:2022-07-27T13:49:09.483

System.out.println(localDateTime1.getYear()); // 运行结果:2019
System.out.println(localDateTime1.getMonthValue()); // 运行结果:10
System.out.println(localDateTime1.getDayOfMonth()); // 运行结果:27
System.out.println(localDateTime1.getHour()); // 运行结果:13
System.out.println(localDateTime1.getMinute()); // 运行结果:52
System.out.println(localDateTime1.getSecond()); // 运行结果:6

// 4. 计算时间:替换月份
LocalDateTime localDateTime4 = LocalDateTime1.withDayOfMonth(10);
System.out.println(localDateTime4); // 2019-10-10T14:19:56.884

2. 时间戳

2.1. 概念

Instant 它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算 。

2.2. 常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 获取当前时间戳
Instant instant1 = Instant.now(); // 默认获取UTC时区
System.out.println(instant1); // 运行结果:2019-10-27T05:59:58.221Z

// 2. 偏移量运算
OffsetDateTime offsetDateTime = instant1.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime); // 运行结果:2019-10-27T13:59:58.221+08:00

// 3. 获取时间戳:毫秒
System.out.println(instant1.toEpochMilli()); // 运行结果:1572156145000

// 4. 以Unix元年为起点,进行偏移量运算
Instant instant2 = Instant.ofEpochSecond(60);
System.out.println(instant2); // 运行结果:1970-01-01T00:01:00Z

3. 间隔

3.1. 概念

Duration:用于计算两个 “时间” 间隔。
Period:用于计算两个 “日期” 间隔 。

3.2. 常用方法

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
Instant instant_1 = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant instant_2 = Instant.now();

Duration duration = Duration.between(instant_1, instant_2);
System.out.println(duration.toMillis()); // 运行结果:1000

LocalTime localTime_1 = LocalTime.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LocalTime localTime_2 = LocalTime.now();

System.out.println(Duration.between(localTime_1, localTime_2).toMillis()); // 运行结果:1000
LocalDate localDate_1 = LocalDate.of(2018,9, 9);
LocalDate localDate_2 = LocalDate.now();

Period period = Period.between(localDate_1, localDate_2);
System.out.println(period.getYears()); // 运行结果:1
System.out.println(period.getMonths()); // 运行结果:1
System.out.println(period.getDays()); // 运行结果:18

3.3. 注意

1. Period.between()

Period.between(LocalDate startDateInclusive, LocalDate endDateExclusive) 方法计算时间间隔时,会将计算结果拆成年、月、日,3个数字,换算之后的和,才是传统意义上的间隔多少天。

1
2
3
4
5
6
7
8
// 间隔 1年2月21天,因此分别存储在年、月、日中
LocalDate start = LocalDate.of(2022, 8, 10);
LocalDate end = LocalDate.of(2023, 10, 31);
Period pd = Period.between(start, end);
System.out.println(pd); // P1Y2M21D
System.out.println(pd.getYears()); // 1
System.out.println(pd.getMonths()); // 2
System.out.println(pd.getDays()); // 21

2. 如何计算间隔

  • ChronoUnit: 时间单位的枚举类,其 between() 方法,可以计算相应单位时间
1
2
3
4
5
6
7
8
LocalDateTime startTime = LocalDateTime.of(2022, 8, 10, 15, 2, 3);
LocalDateTime endTime = LocalDateTime.of(2023, 10, 31, 10, 24, 59);
long year = ChronoUnit.YEARS.between(startTime, endTime); // 1
long month = ChronoUnit.MONTHS.between(startTime, endTime); // 14
long days = ChronoUnit.DAYS.between(startTime, endTime); // 447
long hour = ChronoUnit.HOURS.between(startTime, endTime); // 10737
long minute = ChronoUnit.MINUTES.between(startTime, endTime); // 644242
long second = ChronoUnit.SECONDS.between(startTime, endTime); // 38654576
  • toEpochDay: LocalDate 类中方法,可以计算相差多少天
1
2
3
LocalDate start = LocalDate.of(2022, 8, 10);
LocalDate end = LocalDate.of(2023, 10, 31);
long between = end.toEpochDay() - start.toEpochDay(); // 447

4. 日期操作

4.1. 概念

TemporalAdjuster:时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters:该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现。

4.2. 常用方法

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
LocalDateTime localDateTime1 = LocalDateTime.now();
System.out.println(localDateTime1); // 2019-10-27T14:19:56.884

// 获取这个第一天的日期
System.out.println(localDateTime1.with(TemporalAdjusters.firstDayOfMonth())); // 2019-10-01T14:22:58.574
// 获取下个周末的日期
System.out.println(localDateTime1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY))); // 2019-11-03T14:22:58.574

// 本周周一
LocalDate monday = now.with(TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY));
// 本周周日
LocalDate sunday = now.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY ));

// 自定义:下一个工作日
LocalDateTime localDateTime2 = localDateTime1.with(l -> {
LocalDateTime localDateTime = (LocalDateTime) l;
DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();

if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return localDateTime.plusDays(3);
} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return localDateTime.plusDays(2);
} else {
return localDateTime.plusDays(1);
}
});
System.out.println(localDateTime2); // 运行结果:2019-10-28T14:30:17.400

4.3. 注意

  1. 四个方法区别, 带 OrSame 的方法在所求的周时间与输入相同时则会返回输入时间, 否则返回上一个或下一个周期时间
  • TemporalAdjusters.previous(DayOfWeek dayOfWeek)
  • TemporalAdjusters.previousOrSame(DayOfWeek dayOfWeek)
  • TemporalAdjusters.next(DayOfWeek dayOfWeek)
  • TemporalAdjusters.nextOrSame(DayOfWeek dayOfWeek)
1
2
3
4
5
// 如果今天是周一, 再求上或下周周一时间, OrSame 会返回今天, 其他则不会
LocalDate today = LocalDate.now();
LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); // 等于今天时间 today
LocalDate monday_pre = today.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)); // 上周一时间
LocalDate monday_nxt = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 下周一时间

5. 解析与格式化

5.1. 概念

java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

  • 预定义的标准格式
  • 语言环境相关的格式
  • 自定义的格式

5.2. 常用方法

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
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ISO_DATE;
LocalDateTime localDateTime = LocalDateTime.now();
String strDate1 = localDateTime.format(dateTimeFormatter1);
System.out.println(strDate1); // 运行结果:2019-10-27

// LocalDateTime -> String
DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String strDate2 = dateTimeFormatter2.format(localDateTime);
System.out.println(strDate2); // 运行结果:2019-10-27 14:36:11

// String -> LocalDateTime
LocalDateTime localDateTime1 = localDateTime.parse(strDate2, dateTimeFormatter2);
System.out.println(localDateTime1); // 运行结果:2019-10-27T14:37:39

// date -> LocalDate/LocalDateTime
Date date = ...;
ZoneId zoneId = ZoneId.systemDefault();
Instant instant = date.toInstant();
LocalDate localDate = instant.atZone(zoneId).toLocalDate();
LocalDateTime localDateTime = LocalDate localDate = instant.atZone(zoneId).toLocalDateTime();

// LocalDate/LocalDateTime -> date
LocalDate localDate = ...;
LocalDateTime localDateTime = ...;
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
ZonedDateTime zdt = localDateTime.atZone(ZoneId);
Date date = Date.from(zdt.toInstant());

6. 时区

6.1. 概念

带时区的时间为分别为:ZonedDate、 ZonedTime、 ZonedDateTime。其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式,例如 :Asia/Shanghai 等。

  • ZoneId:该类中包含了所有的时区信息
  • getAvailableZoneIds():可以获取所有时区时区信息
  • of(id):用指定的时区信息获取 ZoneId 对象

6.2. 常用方法

1
2
3
4
5
6
7
8
9
10
11
12
// 获取所有的时区
Set<String> set = ZoneId.getAvailableZoneIds();
System.out.println(set); // [Asia/Aden, America/Cuiaba, ...]

// 通过时区构建LocalDateTime
LocalDateTime localDateTime1 = LocalDateTime.now(ZoneId.of("America/El_Salvador"));
System.out.println(localDateTime1); // 2019-10-27T00:46:21.268

// 以时区格式显示时间
LocalDateTime localDateTime2 = LocalDateTime.now();
ZonedDateTime zonedDateTime1 = localDateTime2.atZone(ZoneId.of("Africa/Nairobi"));
System.out.println(zonedDateTime1); // 2019-10-27T14:46:21.273+03:00[Africa/Nairobi]

7. 与传统时间转换

to from
iava.time.LocalDate <-> java.util.Date Date.valueOf(localDate) date.toLocalDate()
iava.time.LocalTime <-> java.util.Date Date.valueOf(localDate) date.toLocalTime()
iava.time.LocalDateTime <-> java.sgl.Timestamp Timestamp.valueOf(localDateTime) timestamp.toLocalDateTime()
java.time.Instant <-> java.util.Date Date.from(instant) date.tolnstant()
java.time.Instant <-> java.sgl.Timestamp Timestamp.from(instant) timestamp.tolnstant()
java.time.Zoneld <-> java.util.TimeZone Timezone.getTimeZone(id) timeZone.toZoneld()
java.time.format.DateTimeFormatter <-> java.text DateFormat formatter.toFormat()
java.time.ZonedDateTime <-> java.util.GregorianCalendar GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()

8. 预定义格式

可以直接使用的 format :

1
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
Formatter 实例 举例
BASIC_ISO_DATE 20181203
ISO_LOCAL_DATE 2018-12-03
ISO_OFFSET_DATE 2018-12-03+01:00
ISO_DATE 2018-12-03+01:00; 2018-12-03
ISO_LOCAL_TIME 11:15:30
ISO_OFFSET_TIME 11:15:30+01:00
ISO_TIME 11:15:30+01:00; 11:15:30
ISO_LOCAL_DATE_TIME 2018-12-03T11:15:30
ISO_OFFSET_DATE_TIME 2018-12-03T11:15:30+01:00
ISO_ZONED_DATE_TIME 2018-12-03T11:15:30+01:00[Europe/Paris]
ISO_DATE_TIME 2018-12-03T11:15:30+01:00[Europe/Paris]
ISO_ORDINAL_DATE 2018-337
ISO_WEEK_DATE 2018-W48-6
ISO_INSTANT 2018-12-03T11:15:30Z
RFC_1123_DATE_TIME Tue, 3 Jun 2018 11:05:30 GMT

9. 自定义格式

自定义日期格式模式

符号 含义 举例 备注
G era AD; Anno Domini; A 公元
u year 2018; 18 可正可负, 表示公元前后
y year-of-era 2018; 18 总是正
D day-of-year 180 日, 取值 1 -366
d day-of-month 11
M/L month-of-year 7; 07; Jul; July; J
g modified-julian-day 2451334
Q/q quarter-of-year 3; 03; Q3; 3rd quarter 季度
Y week-based-year 1999; 99
w week-of-week-based-year 25
W week-of-month 3
E day-of-week Tue; Tuesday; T
e/c localized day-of-week 2; 02; Tue; Tuesday; T
F day-of-week-in-month 2
a am-pm-of-day AM
h clock-hour-of-am-pm (1-12) 12
K hour-of-am-pm (0-11) 0
k clock-hour-of-day (1-24) 24
H hour-of-day (0-23) 0
m minute-of-hour 35
s second-of-minute 50
S fraction-of-second 970
A milli-of-day 1234
n nano-of-second 987654321
N nano-of-day 1234000000
V time-zone ID America/Los_Angeles; Z; -08:30
v generic time-zone name Pacific Time; PT
z time-zone name Pacific Standard Time; PST
O localized zone-offset GMT+8; GMT+08:00; UTC-08:00
X zone-offset ‘Z’ for zero Z; -08; -0830; -08:30; -083015; -08:30:15
x zone-offset +0000; -08; -0830; -08:30; -083015; -08:30:15
Z zone-offset +0000; -0800; -08:00
p pad next 1
' escape for text
'' single quote
[ optional section start
] optional section end