1+ # Java 8 新的日期时间 API
2+
3+ ## ZoneId
4+
5+ Java 8中的时区操作被很大程度上简化了,新的时区类 ` java.time.ZoneId ` 是原有的 ` java.util.TimeZone ` 类的替代品。
6+ ZoneId对象可以通过 ` ZoneId.of() ` 方法创建,也可以通过 ` ZoneId.systemDefault() ` 获取系统默认时区:
7+
8+ ``` java
9+ ZoneId shanghaiZoneId = ZoneId . of(" Asia/Shanghai" );
10+ ZoneId systemZoneId = ZoneId . systemDefault();
11+ ```
12+
13+ ` of() ` 方法接收一个“区域/城市”的字符串作为参数,你可以通过 ` getAvailableZoneIds() ` 方法获取所有合法的“区域/城市”字符串:
14+
15+ ``` java
16+ Set<String > zoneIds = ZoneId . getAvailableZoneIds();
17+ ```
18+
19+ 对于老的时区类 ` TimeZone ` ,Java 8也提供了转化方法:
20+
21+ ``` java
22+ ZoneId oldToNewZoneId = TimeZone . getDefault(). toZoneId();
23+ ```
24+
25+ 有了 ` ZoneId ` ,我们就可以将一个 ` LocalDate ` 、` LocalTime ` 或 ` LocalDateTime ` 对象转化为 ` ZonedDateTime ` 对象:
26+
27+ ``` java
28+ LocalDateTime localDateTime = LocalDateTime . now();
29+ ZonedDateTime zonedDateTime = ZonedDateTime . of(localDateTime, shanghaiZoneId);
30+ ```
31+
32+ ` ZonedDateTime ` 对象由两部分构成,` LocalDateTime ` 和 ` ZoneId ` ,其中 ` 2018-03-03T15:26:56.147 ` 部分为 ` LocalDateTime ` ,` +08:00[Asia/Shanghai] ` 部分为ZoneId。
33+
34+ 另一种表示时区的方式是使用 ` ZoneOffset ` ,它是以当前时间和 ** 世界标准时间(UTC)/格林威治时间(GMT)** 的偏差来计算,例如:
35+
36+ ``` java
37+ ZoneOffset zoneOffset = ZoneOffset . of(" +09:00" );
38+ LocalDateTime localDateTime = LocalDateTime . now();
39+ OffsetDateTime offsetDateTime = OffsetDateTime . of(localDateTime, zoneOffset);
40+ ```
141
242## Instant
343
@@ -43,6 +83,20 @@ Instant earlier = now.minusSeconds(3);
4383
4484第一行获得了一个Instant对象,表示当前时间。第二行创建了一个Instant表示三秒后,第三行创建了一个Instant表示三秒前。
4585
86+ > seconds 表示从 ` 1970-01-01 00:00:00 ` 开始到现在的秒数,nanos 表示纳秒部分(nanos的值不会超过999,999,999)
87+
88+ ## Clock
89+
90+ Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 ` System.currentTimeMillis() ` 来获取当前的微秒数。
91+ 某一个特定的时间点也可以使用Instant类来表示,Instant 类也可以用来创建老的 ` java.util.Date ` 对象。
92+
93+ ``` java
94+ Clock clock = Clock . systemDefaultZone();
95+ long millis = clock. millis();
96+ Instant instant = clock. instant();
97+ Date legacyDate = Date . from(instant); // legacy java.util.Date
98+ ```
99+
46100## LocalDate
47101
48102LocalDate类是Java 8中日期时间功能里表示一个本地日期的类,它的日期是无时区属性的。
@@ -61,7 +115,7 @@ LocalDate localDate = LocalDate.now();
61115另一种方法是使用年月日信息构造出LocalDate对象:
62116
63117``` java
64- LocalDate localDate2 = LocalDate . of(2015 , 12 , 31 );
118+ LocalDate localDate2 = LocalDate . of(2018 , 3 , 3 );
65119```
66120
67121LocalDate 的 ` of() ` 方法创建出一个指定年月日的日期,并且没有时区信息。
@@ -161,55 +215,6 @@ LocalTime localTimeEarlier = localTime.minusHours(3);
161215
162216LocalTime类的对象也是不可变的,所以计算方法会返回一个新的LocalTime实例。
163217
164- ** 创建一个LocalTime实例**
165-
166- 有多种方式可以新建LocalTime实例。比如使用当前时间作为值新建对象:
167-
168- ``` java
169- LocalTime localTime = LocalTime . now();
170- ```
171-
172- 另一种方式是使用指定的时分秒和纳秒来新建对象:
173-
174- ``` java
175- LocalTime localTime2 = LocalTime . of(21 , 30 , 59 , 11001 );
176- ```
177-
178- 也有另一种版本的 ` of() ` 方法只需要小时分钟两项,或时分秒三项值作为参数。
179-
180- ** 访问LocalTime对象的时间**
181-
182- 你可以通过这些方法访问其时、分、秒、纳秒:
183-
184- - ` getHour() `
185- - ` getMinute() `
186- - ` getSecond() `
187- - ` getNano() `
188-
189- ** LocalTime的计算**
190-
191- LocalTime类包含一系列方法,能帮你完成时间计算:
192-
193- - ` plusHours() `
194- - ` plusMinutes() `
195- - ` plusSeconds() `
196- - ` plusNanos() `
197- - ` minusHours() `
198- - ` minusMinutes() `
199- - ` minusSeconds() `
200- - ` minusNanos() `
201-
202- 以下举一个例子:
203-
204- ``` java
205- LocalTime localTime2 = LocalTime . of(21 , 30 , 59 , 11001 );
206- LocalTime localTimeLater = localTime. plusHours(3 );
207- LocalTime localTimeEarlier = localTime. minusHours(3 );
208- ```
209-
210- 1 . 第一行新建一个LocalTime实例,表示21:30:50的第11001纳秒。
211- 2 . 第二行新建了一个LocalTime实例表示这个时间的三小时后,第三行表示三小时前。
212-
213218## LocalDateTime
214219
215220LocalDateTime类是Java 8中日期时间功能里,用于表示当地的日期与时间的类,它的值是无时区属性的。你可以将其视为Java 8中LocalDate与LocalTime两个类的结合。
@@ -227,7 +232,7 @@ LocalDateTime localDateTime = LocalDateTime.now();
227232另一种方式是使用指定的年月日、时分秒、纳秒来新建对象:
228233
229234``` java
230- LocalDateTime localDateTime2 = LocalDateTime . of(2015 , 11 , 26 , 13 , 55 , 36 , 123 );
235+ LocalDateTime localDateTime2 = LocalDateTime . of(2018 , 11 , 26 , 13 , 55 , 36 , 123 );
231236```
232237
233238** 访问LocalDateTime对象的时间**
@@ -480,4 +485,133 @@ Duration subtracted = start.minusDays(3);
4804851 . 第一行创建了一个Duration对象叫start,具体怎么创建可以参考前面的代码。
4814862 . 第二三行样例创建了两个新的Duration,通过调用start的加减操作,使得added对象表示的时间比start多三天,而substracted则少三天。
482487
483- 所有的计算方法都会返回一个新的Duration,以保证Duration的不可变属性。
488+ 所有的计算方法都会返回一个新的Duration,以保证Duration的不可变属性。
489+
490+ ``` java
491+ long days = duration. toDays(); // 这段时间的总天数
492+ long hours = duration. toHours(); // 这段时间的小时数
493+ long minutes = duration. toMinutes(); // 这段时间的分钟数
494+ long seconds = duration. getSeconds(); // 这段时间的秒数
495+ long milliSeconds = duration. toMillis(); // 这段时间的毫秒数
496+ long nanoSeconds = duration. toNanos(); // 这段时间的纳秒数
497+ ```
498+
499+ ## 其他操作
500+
501+ ### 增加和减少日期
502+
503+ Java 8中的日期/时间类都是不可变的,这是为了保证线程安全。当然,新的日期/时间类也提供了方法用于创建对象的可变版本,比如增加一天或者减少一天:
504+
505+ ``` java
506+ LocalDate date = LocalDate . of(2017 , 1 , 5 ); // 2017-01-05
507+
508+ LocalDate date1 = date. withYear(2016 ); // 修改为 2016-01-05
509+ LocalDate date2 = date. withMonth(2 ); // 修改为 2017-02-05
510+ LocalDate date3 = date. withDayOfMonth(1 ); // 修改为 2017-01-01
511+
512+ LocalDate date4 = date. plusYears(1 ); // 增加一年 2018-01-05
513+ LocalDate date5 = date. minusMonths(2 ); // 减少两个月 2016-11-05
514+ LocalDate date6 = date. plus(5 , ChronoUnit . DAYS ); // 增加5天 2017-01-10
515+ ```
516+
517+ 上面例子中对于日期的操作比较简单,但是有些时候我们要面临更复杂的时间操作,比如将时间调到下一个工作日,
518+ 或者是下个月的最后一天,这时候我们可以使用 ` with() ` 方法的另一个重载方法,它接收一个TemporalAdjuster参数,
519+ 可以使我们更加灵活的调整日期:
520+
521+ ``` java
522+ LocalDate date7 = date. with(nextOrSame(DayOfWeek . SUNDAY )); // 返回下一个距离当前时间最近的星期日
523+ LocalDate date9 = date. with(lastInMonth(DayOfWeek . SATURDAY )); // 返回本月最后一个星期六
524+ ```
525+
526+ 要使上面的代码正确编译,你需要使用静态导入 ` TemporalAdjusters ` 对象:
527+
528+ ` import static java.time.temporal.TemporalAdjusters.*; `
529+
530+ ` TemporalAdjusters ` 类中包含了很多静态方法可以直接使用,下面的表格列出了一些方法:
531+
532+ | 方法名 | 描述 |
533+ | :-----:| :-------|
534+ | ` dayOfWeekInMonth ` | 返回同一个月中每周的第几天 |
535+ | ` firstDayOfMonth ` | 返回当月的第一天 |
536+ | ` firstDayOfNextMonth ` | 返回下月的第一天 |
537+ | ` firstDayOfNextYear ` | 返回下一年的第一天 |
538+ | ` firstDayOfYear ` | 返回本年的第一天 |
539+ | ` firstInMonth ` | 返回同一个月中第一个星期几 |
540+ | ` lastDayOfMonth ` | 返回当月的最后一天 |
541+ | ` lastDayOfNextMonth ` | 返回下月的最后一天 |
542+ | ` lastDayOfNextYear ` | 返回下一年的最后一天 |
543+ | ` lastDayOfYear ` | 返回本年的最后一天 |
544+ | ` lastInMonth ` | 返回同一个月中最后一个星期几 |
545+ | ` next / previous ` | 返回后一个/前一个给定的星期几 |
546+ | ` nextOrSame / previousOrSame ` | 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回 |
547+
548+ 如果上面表格中列出的方法不能满足你的需求,你还可以创建自定义的 ` TemporalAdjuster ` 接口的实现,
549+ ` TemporalAdjuster ` 也是一个函数式接口,所以我们可以使用Lambda表达式:
550+
551+ ``` java
552+ @FunctionalInterface
553+ public interface TemporalAdjuster {
554+ Temporal adjustInto (Temporal temporal );
555+ }
556+ ```
557+
558+ 比如给定一个日期,计算该日期的下一个工作日(不包括星期六和星期天):
559+
560+ ``` java
561+ LocalDate date = LocalDate . of(2017 , 1 , 5 );
562+ date. with(temporal - > {
563+ // 当前日期
564+ DayOfWeek dayOfWeek = DayOfWeek . of(temporal. get(ChronoField . DAY_OF_WEEK ));
565+
566+ // 正常情况下,每次增加一天
567+ int dayToAdd = 1 ;
568+
569+ // 如果是星期五,增加三天
570+ if (dayOfWeek == DayOfWeek . FRIDAY ) {
571+ dayToAdd = 3 ;
572+ }
573+
574+ // 如果是星期六,增加两天
575+ if (dayOfWeek == DayOfWeek . SATURDAY ) {
576+ dayToAdd = 2 ;
577+ }
578+
579+ return temporal. plus(dayToAdd, ChronoUnit . DAYS );
580+ });
581+ ```
582+
583+ ### 其他历法
584+
585+ Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366天。闰年的定义是:非世纪年,能被4整除;世纪年能被400整除。为了计算的一致性,公元1年的前一年被当做公元0年,以此类推。
586+
587+
588+ 此外Java 8还提供了4套其他历法(很奇怪为什么没有汉族人使用的农历),每套历法都包含一个日期类,分别是:
589+
590+ - ` ThaiBuddhistDate ` :泰国佛教历
591+ - ` MinguoDate ` :中华民国历
592+ - ` JapaneseDate ` :日本历
593+ - ` HijrahDate ` :伊斯兰历
594+
595+ 每个日期类都继承 ` ChronoLocalDate ` 类,所以可以在不知道具体历法的情况下也可以操作。不过这些历法一般不常用,除非是有某些特殊需求情况下才会使用。
596+
597+ 这些不同的历法也可以用于向公历转换:
598+
599+ ``` java
600+ LocalDate date = LocalDate . now();
601+ JapaneseDate jpDate = JapaneseDate . from(date);
602+ ```
603+
604+ 由于它们都继承ChronoLocalDate类,所以在不知道具体历法情况下,可以通过ChronoLocalDate类操作日期:
605+
606+ ``` java
607+ Chronology jpChronology = Chronology . ofLocale(Locale . JAPANESE );
608+ ChronoLocalDate jpChronoLocalDate = jpChronology. dateNow();
609+ ```
610+
611+ 我们在开发过程中应该尽量避免使用 ` ChronoLocalDate ` ,尽量用与历法无关的方式操作时间,因为不同的历法计算日期的方式不一样,
612+ 比如开发者会在程序中做一些假设,假设一年中有12个月,如果是中国农历中包含了闰月,一年有可能是13个月,
613+ 但开发者认为是12个月,多出来的一个月属于明年的。
614+ 再比如假设年份是累加的,过了一年就在原来的年份上加一,但日本天皇在换代之后需要重新纪年,所以过了一年年份可能会从1开始计算。
615+
616+ 在实际开发过程中建议使用 ` LocalDate ` ,包括存储、操作、业务规则的解读;除非需要将程序的输入或者输出本地化,
617+ 这时可以使用 ` ChronoLocalDate ` 类。
0 commit comments