Java8 那些事儿(三):Date/Time API(JSR 310)

前言

Java8 引入了新的 Date-Time API(JSR 310)来改进时间、日期的处理。时间和日期的管理一直是最令 Java 开发者痛苦的问题。java.util.Date 和后来的 java.util.Calendar 一直没有解决这个问题(甚至令开发者更加迷茫)。

因为上面这些原因,诞生了第三方库 Joda-Time,可以替代 Java 的时间管理 API。Java8 中新的时间和日期管理 API 深受 Joda-Time 影响,并吸收了很多 Joda-Time 的精华。新的 java.time 包包含了所有关于瞬时时间(Instant),持续时间(Duration),日期(Date),时间(Time),时区(Time-Zone)以及时间段(Period)操作的类。新设计的 API 认真考虑了这些类的不变性(从 java.util.Calendar 吸取的教训),如果某个实例需要修改,则返回一个新的对象。

  • ZoneId: 时区 ID,用来确定 Instant 和 LocalDateTime 互相转换的规则
  • Instant: 用来表示时间线上的一个点
  • LocalDate: 表示没有时区的日期,LocalDate 是不可变并且线程安全的
  • LocalTime: 表示没有时区的时间,LocalTime 是不可变并且线程安全的
  • LocalDateTime: 表示没有时区的日期时间,LocalDateTime 是不可变并且线程安全的
  • Clock: 用于访问当前时刻、日期、时间,用到时区
  • Duration: 用秒和纳秒表示时间的数量

最常用的就是 LocalDate、LocalTime、LocalDateTime 了,从它们的名字就可以看出是操作日期和时间的。

LocalDate

LocalDate 是一个不可变的类,它表示默认格式 (yyyy-MM-dd) 的日期,我们可以使用 now()方法得到当前时间,也可以提供输入年份、月份和日期的输入参数来创建一个 LocalDate 实例。该类为 now()方法提供了重载方法,我们可以传入 ZoneId 来获得指定时区的日期。

  1. 在 Java8 中如何获取当天的日期?
1
2
3
4
5
6
7
8
9
10
11
LocalDate localDate = LocalDate.now();
System.out.println("Today's Local date : " + localDate);

// Output
// Today's Local date : 2017-12-07

LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Today's local date in Kolkata is : " + todayKolkata);

// Output
// Today's local date in Kolkata is : 2017-12-07
  1. 在 Java8 中如何获取当前的年月日?
1
2
3
4
5
6
7
8
LocalDate today = LocalDate.parse("2017-12-07");
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
System.out.printf("Year : %d Month : %d day : %d \t %n", year, month, day);

// Output
// Year : 2017 Month : 12 day : 7
  1. 在 Java8 中如何获取某个特定的日期?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 根据年月日取日期
LocalDate dateOfBirth = LocalDate.of(1994, 11, 15);
// 根据字符串取: 严格按照 ISO yyyy-MM-dd 验证
LocalDate dateOfBirth = LocalDate.of("1994-11-15");
System.out.println("Your Date of birth is :" + dateOfBirth);

// Output
// Your Date of birth is : 1994-11-15

// 2. 获取 2017 年的第 100 天的日期
LocalDate hundredDay2017 = LocalDate.ofYearDay(2017, 100);
System.out.println("Hundred Day Of 2014 is :" + hundredDay2017);

// Output
// Hundred Day Of 2014 is : 2017-04-10

// 3. 根据有效输入创建日期, 以下代码会抛异常, 无效输入, 20172 月没有 29
LocalDate feb29_2017 = LocalDate.of(2017, Month.FEBRUARY, 29);

// Exception in thread "main" java.time.DateTimeException:Invalid date 'February 29' as '2014' is not a leap year
  1. 在 Java8 中如何检查两个日期是否相等?
1
2
3
4
5
6
7
8
LocalDate today = LocalDate.parse("2017-12-07");
LocalDate date1 = LocalDate.of(2017, 12, 7);
if (date1.equals(today)) {
System.out.printf("Today %s and date1 %s are same date %n", today, date1);
}

// Output
// Today 2017-12-07 and date1 2017-12-07 are same date
  1. 在 Java8 中如何检查重复事件,比如说生日?
1
2
3
4
5
6
7
8
9
10
11
12
LocalDate today = LocalDate.parse("2017-12-07");
LocalDate dateOfBirth = LocalDate.of(1994, 11, 15);
MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(today);
if (currentMonthDay.equals(birthday)) {
System.out.println("Many Many happy returns of the day !!");
} else {
System.out.println("Sorry, today is not your birthday");
}

// Output
// Sorry, today is not your birthday
  1. 在 Java8 中如何获取 1 周后的日期?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
LocalDate today = LocalDate.parse("2017-12-12");
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
LocalDate plusWeek = today.plusWeeks(2);
LocalDate periodWeek = today.plus(Period.ofWeeks(3));
System.out.println("Today is :" + today);
System.out.println("Date after 1 week :" + nextWeek);
System.out.println("Date after 2 week :" + plusWeek);
System.out.println("Date after 3 week :" + periodWeek);

// Output
// Today is : 2017-12-12
// Date after 1 week : 2017-12-19
// Date after 2 week : 2017-12-26
// Date after 3 week : 2017-01-02
  1. 在 Java8 中如何获取一年前后的日期?
1
2
3
4
5
6
7
8
9
LocalDate today = LocalDate.parse("2017-12-12");
LocalDate previousYear = today.minus(1, ChronoUnit.YEARS);
System.out.println("Date before 1 year :" + previousYear);
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("Date after 1 year :" + nextYear);

// Output
// Date before 1 year : 2016-12-12
// Date after 1 year : 2017-12-12
  1. 在 Java8 中如何检查闰年?
1
2
3
4
5
6
7
8
9
LocalDate today = LocalDate.now();
if (today.isLeapYear()) {
System.out.println("This year is Leap year");
} else {
System.out.println("2017 is not a Leap year");
}

// Output
// 2017 is not a Leap year
  1. 在 Java8 中如何获取这个月的第一天?
1
2
3
4
5
6
7
8
9
LocalDate today = LocalDate.parse("2017-12-12");
LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate firstDayOfMonth2 = today.withDayOfMonth(1);
System.out.println("First Day Of Month is :" + firstDayOfMonth);
System.out.println("First Day Of Month is :" + firstDayOfMonth2);

// Output
// First Day Of Month is : 2017-12-01
// First Day Of Month is : 2017-12-01
  1. 在 Java8 中如何判断某个日期是在另一个日期的前面还是后面?
1
2
3
4
5
6
7
8
9
10
11
12
13
LocalDate today = LocalDate.parse("2017-12-12");
LocalDate tomorrow = LocalDate.of(2017, 12, 13);
LocalDate yesterday = today.minus(1, ChronoUnit.DAYS);
if (tomorrow.isAfter(today)) {
System.out.println("Tomorrow comes after today");
}
if (yesterday.isBefore(today)) {
System.out.println("Yesterday is day before today");
}

// Output
// Tomorrow comes after today
// Yesterday is day before today

LocalTime

LocalTime 是一个不可变的类,它的实例代表一个符合人类可读格式的时间,默认格式是 hh:mm:ss.zzz。像 LocalDate 一样,该类也提供了时区支持,同时也可以传入小时、分钟和秒等输入参数创建实例。

  1. 在 Java8 中如何获取当天的时间?
1
2
3
4
5
6
7
8
9
10
11
12
13
// 1.Current Time
LocalTime time = LocalTime.now();
System.out.println("local time now :" + time);

// Output
// local time now : 16:16:45.219

// 2.Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Time in IST :" + timeKolkata);

// Output
// Current Time in IST : 13:47:55.482
  1. 在 Java8 中如何获取当前的小时、分钟?
1
2
3
4
5
6
7
8
LocalTime nowTime = LocalTime.parse("15:02:53");
int hour = nowTime.getHour();
int minute = nowTime.getMinute();
int second = nowTime.getSecond();
System.out.printf("Hour : %d Minute : %d Second : %d \t %n", hour, minute, second);

// Output
// Hour : 15 Minute : 2 Second : 53
  1. 在 Java8 中如何获取某个特定的时间?
1
2
3
4
5
6
7
8
9
10
11
12
13
// 1.Creating LocalTime by providing input arguments
LocalTime specificTime = LocalTime.of(12, 20, 25);
System.out.println("Specific Time of Day :" + specificTime);

// Output
// Specific Time of Day : 12:20:25

// 2.Getting date from the base date i.e 01/01/1970
LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
System.out.println("10000th second time :" + specificSecondTime);

// Output
// 10000th second time : 02:46:40

LocalDateTime

LocalDateTime 是一个不可变的日期 - 时间对象,它表示一组日期 - 时间,默认格式是 yyyy-MM-ddTHH-mm-ss.zzz。它提供了一个工厂方法,通过接收 LocalDate 和 LocalTime 输入参数,来创建 LocalDateTime 实例。

  1. 在 Java8 中如何获取当天的日期时间?
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
// 1.Current Date
LocalDateTime today = LocalDateTime.now();
System.out.println("Current DateTime :" + today);

// Output
// Current DateTime : 2017-12-12T16:35:49.504

// 2.Current Date using LocalDate and LocalTime
today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("Current DateTime :" + today);

// Output
// Current DateTime : 2017-12-12T16:35:49.504

// 3.Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("Current Date in IST :" + todayKolkata);

// Output
// Current Date in IST : 2017-12-12T14:08:39.289

// 4.Current date in systemDefault, you can get it from ZoneId javadoc
LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.systemDefault());
System.out.println("Current Date in SystemDefault :" + todayKolkata);

// Output
// Current Date in SystemDefault : 2017-12-12T17:02:20.421

// 5.Create LocalDateTime from LocalDate
LocalDate localDate = LocalDate.now();
System.out.println("Current Time :" + localDate.atTime(LocalTime.now()));

// Output
// Current Date in SystemDefault : 2017-12-12T17:02:20.421

Instant

Instant 类是用在机器可读的时间格式上的,它以 Unix 时间戳 < sup>[1] 的形式存储日期时间。java.time.Instant 类在时间线上模拟单个瞬时点。Instant 对象包含两个值:秒数和纳秒数。其中秒数指的是 epoch 时间戳,而纳秒数指的是该秒内的纳秒时间。

  1. 在 Java8 中如何获取当前的时间戳?
1
2
3
4
5
6
7
8
9
10
11
12
13
// 1.Current timestamp[Instant.now()使用等是 UTC 时间 Clock.systemUTC().instant(), 输出 2017-12-12T08:48:16.253Z 和北京时间相差 8 个时区]
Instant timestamp = Instant.now();
System.out.println("Current Timestamp :" + timestamp);

// Output
// Current Timestamp : 2017-12-12T08:48:16.253Z

// 2.Instant from timestamp
Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
System.out.println("Specific Time :" + specificTime);

// Output
// Specific Time : 2017-12-12T08:48:16.253Z
  1. 在 Java8 中如何获取从 1970 年 1 月 1 日到现在的毫秒?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 将此瞬间转换为 1970-01-01T00:00:00Z 时代的毫秒数。
Instant instant = Instant.now();
System.out.println(instant.toEpochMilli());

// Output
// 1544605635276

// 2. 获取 1970-01-01T00:00:00Z 的 Java 纪元的秒数[Instant.getEpochSecond() 获取秒数部分,Instant.getNano() 获取纳秒部分。]
Instant instant = Instant.now();
System.out.println(instant.getEpochSecond());

// Output
// 1544605635

// 3. 从时间线开始,获取从第二个开始的纳秒数
Instant instant = Instant.now();
System.out.println(instant.getNano());

// Output
// 276000000
  1. 在 Java8 中如何获取两个时间的时间差?
1
2
3
4
5
Instant instant1 = Instant.now();
Instant instant2 = instant1.plus(Duration.ofSeconds(100)); // 添加 100 秒
System.out.println(instant2.isAfter(instant1)); // true
System.out.println(instant1.until(instant2, ChronoUnit.SECONDS)); // 100
System.out.println(instant2.until(instant1, ChronoUnit.SECONDS)); // -100

Duration

java.time.Duration 类以秒和纳秒为单位模拟一个数量或时间量。可以使用其他基于持续时间的单位访问它,例如分钟和小时。Duration 表示以秒为单位的时长,精确到纳秒。

  1. 在 Java8 中如何获取持续时间?
1
2
3
4
5
6
// 表示十九小时二十六分钟三十二点二六八秒
Duration duration1 = Duration.parse("PT19H26M32.268S");
System.out.println(duration1);

// Output
// PT19H26M32.268S
  1. 在 Java8 中如何获取两个时间的持续时间?
1
2
3
4
5
6
7
8
Duration duration1 = Duration.between(Instant.EPOCH, Instant.now());
Duration duration2 = Duration.between(LocalTime.parse("00:00:00"), LocalTime.now());
System.out.println(duration1);
System.out.println(duration2);

// Output
// PT429059H31M34.837S
// PT19H31M35.163S

Period

java.time.Period 类根据年,月和日来模拟一个数量或时间量。Period 表示以天为单位的时长,精确到天。

  1. 在 Java8 中如何获取时间段?
1
2
3
4
5
6
7
8
9
// 表示一年两个月零三天
Period period1 = Period.parse("P1Y2M3D");
Period period2 = Period.of(1, 2, 3);
System.out.println(period1);
System.out.println(period2);

// Output
// P1Y2M3D
// P1Y2M3D
  1. 在 Java8 中如何获取两个日期的时间段?
1
2
3
4
5
6
7
8
9
10
11
LocalDate today = LocalDate.parse("2017-02-23");
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
Period period = today.until(lastDayOfYear);
System.out.println("Period Format =" + period); // P1Y3M15D: 表示一年三个月十五天
System.out.println("Months remaining in the year =" + period.getMonths());
System.out.println("Days remaining in the year =" + period.getDays());

// Output
// Period Format = P10M8D
// Months remaining in the year = 10
// Days remaining in the year = 8
  1. 在 Java8 中如何具体计算两个时间点之间的秒数或天数?
1
2
3
4
5
6
// 离那个什么中华民族的伟大复兴还有多少天
long days = ChronoUnit.DAYS.between(LocalDate.now(), LocalDate.of(2049, 10, 1));
System.out.println(days);

// Output
// 11251

DateTimerFormatter

将一个日期格式转换为不同的格式,之后再解析一个字符串,得到日期时间对象。

  1. 在 Java8 中如何使用预定义的格式器来对日期进行解析 / 格式化?
1
2
3
4
5
6
String dayAfterTommorrow = "20171216";
LocalDate formatted = LocalDate.parse(dayAfterTommorrow, DateTimeFormatter.BASIC_ISO_DATE);
System.out.printf("Date generated from String %s is %s %n", dayAfterTommorrow, formatted);

// Output :
// Date generated from String 20171216 is 2017-12-16
  1. 在 Java8 中如何使用自定义的格式器来解析日期?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
String goodFriday = "十月 18 2014";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
LocalDate holiday = LocalDate.parse(goodFriday, formatter);
System.out.printf("Successfully parsed String %s, date is %s%n", goodFriday, holiday);

// Output :
// Successfully parsed String 十月 18 2014, date is 2014-10-18


LocalDateTime dt = LocalDateTime.parse("27:: 四月::2014 21::39::48", DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
System.out.println("Default format after parsing =" + dt);

// Output :
// Default format after parsing = 2014-04-27T21:39:48
  1. 在 Java8 中如何对日期进行格式化,转换成字符串?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Format examples
LocalDate date = LocalDate.now();
// default format
System.out.println("Default format of LocalDate=" + date);
// specific format
System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));

LocalDateTime dateTime = LocalDateTime.now();
// default format
System.out.println("Default format of LocalDateTime=" + dateTime);
// specific format
System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));

// Output :
// Default format of LocalDate = 2017-12-12
// 12:: 十二月::2017
// 20171212
// Default format of LocalDateTime = 2017-12-12T17:51:30.569
// 12:: 十二月::2017 17::51::30
// 20171212

旧的日期时间支持

旧的日期 / 时间类已经在几乎所有的应用程序中使用,因此做到向下兼容是必须的。这也是为什么会有若干工具方法帮助我们将旧的类转换为新的类,反之亦然。

1.Date 和 Instant 互相转换

1
2
3
4
5
// Instant to Date
Date date = Date.from(Instant.now());

// Date to Instant
Instant instant = date.toInstant();

2.Date 和 LocalDateTime 相互转换

1
2
3
4
5
6
// Date to LocalDateTime
LocalDateTime localDateTime1 = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
LocalDateTime localDateTime2 = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

// LocalDateTime to Date
Date date = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());

3.Date 和 LocalDate 相互转换

1
2
3
4
5
// LocalDate to Date[LocalDate -> LocalDateTime -> Date]
Date date = Date.from(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());

// Date to LocalDate
LocalDate localDate = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

注脚

[1].Unix 时间戳: unix 时间戳是从格林威治时间 1970 年 1 月 1 日(UTC/GMT 的午夜)开始所经过的秒数,不考虑闰秒。


Java8 那些事儿系列


谢谢你长得那么好看,还打赏我!😘
0%