由于上面这些缘由,诞生了第三方库Joda-Time,能够替代Java的时间管理API。Java 8中新的时间和日期管理API深受Joda-Time影响,并吸取了不少Joda-Time的精华。新的java.time包包含了全部关于日期、时间、时区、Instant(跟日期相似可是精确到纳秒)、duration(持续时间)和时钟操做的类。新设计的API认真考虑了这些类的不变性(从java.util.Calendar吸收的教训),若是某个实例须要修改,则返回一个新的对象。html
Java 8日期/时间API是JSR-310的实现,它的实现目标是克服旧的日期时间实现中全部的缺陷,新的日期/时间API的一些设计原则是:java
Java日期/时间API包含如下相应的包:git
LocalDate类和DateTimeFormatter类都是final类型的,也就是说,它们是不可变的,一旦实例化,值就固定了。而Java8以前的Date类和SimpleDateFormat类都不是final的。github
分析一下源码。sql
Instant:时间戳
Duration:持续时间,时间差
LocalDate:只包含日期,好比:2016-10-20
LocalTime:只包含时间,好比:23:12:10
LocalDateTime:包含日期和时间,好比:2016-10-20 23:14:21
Period:时间段
ZoneOffset:时区偏移量,好比:+8:00
ZonedDateTime:带时区的时间
Clock:时钟,好比获取目前美国纽约的时间
java.time.format.DateTimeFormatter:时间格式化类
复制代码
下面介绍下如何使用:api
// 建立 LocalDate // 获取当前年月日 LocalDate localDate = LocalDate.now(); // 构造指定的年月日 LocalDate localDate1 = LocalDate.of(2019, 9, 12); // 获取年、月、日、星期几 int year = localDate1.getYear(); int year1 = localDate1.get(ChronoField.YEAR); Month month = localDate1.getMonth(); int month1 = localDate1.get(ChronoField.MONTH_OF_YEAR); // 月份中的第几天:12 int day = localDate1.getDayOfMonth(); int day1 = localDate1.get(ChronoField.DAY_OF_MONTH); // 一周的第几天:THURSDAY DayOfWeek dayOfWeek = localDate1.getDayOfWeek(); // 一周的第几天:4 int dayOfWeek1 = localDate1.get(ChronoField.DAY_OF_WEEK); // 是否为闰年:false boolean leapYear = localDate1.isLeapYear(); // 纽约日期 LocalDate todayNewYork = LocalDate.now(ZoneId.of("America/New_York")); 复制代码
// 建立 LocalTime LocalTime localTime = LocalTime.of(14, 14, 14); LocalTime localTime1 = LocalTime.now(); // 获取小时 int hour = localTime.getHour(); int hour1 = localTime.get(ChronoField.HOUR_OF_DAY); // 获取分 int minute = localTime.getMinute(); int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR); // 获取秒 int second = localTime.getMinute(); int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE); // 纽约时间 LocalTime timeNewYork = LocalTime.now(ZoneId.of("America/New_York")); 复制代码
至关于 LocalDate + LocalTime安全
// 建立 LocalDateTime LocalDateTime localDateTime = LocalDateTime.now(); LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56); LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime); LocalDateTime localDateTime3 = localDate.atTime(localTime); LocalDateTime localDateTime4 = localTime.atDate(localDate); // 获取LocalDate LocalDate localDate2 = localDateTime.toLocalDate(); // 获取LocalTime LocalTime localTime2 = localDateTime.toLocalTime(); // 纽约日期+时间 LocalDateTime timeNewYork = LocalDateTime.now(ZoneId.of("America/New_York")); 复制代码
用于表示一个时间戳(精确到纳秒)markdown
它与咱们常使用的System.currentTimeMillis()
有些相似,不过Instant
能够精确到纳秒(Nano-Second),System.currentTimeMillis()
方法只精确到毫秒(Milli-Second)。若是查看Instant
源码,发现它的内部使用了两个常量,seconds
表示从1970-01-01 00:00:00开始到如今的秒数,nanos
表示纳秒部分(nanos
的值不会超过999,999,999
)。Instant
除了使用now()
方法建立外,还能够经过ofEpochSecond
方法建立.多线程
// ofEpochSecond()方法的第一个参数为秒,第二个参数为纳秒,上面的代码表示从1970-01-01 00:00:00开始后两分钟的10万纳秒的时刻,控制台上的输出为: // 1970-01-01T00:02:00.000100Z Instant instant = Instant.ofEpochSecond(120, 100000); // 建立Instant对象 Instant instant = Instant.now(); // 获取秒数 long currentSecond = instant.getEpochSecond(); // 获取毫秒数 long currentMilli = instant.toEpochMilli(); 复制代码
// Duration.between()方法建立 Duration 对象 LocalDateTime from = LocalDateTime.of(2017, Month.JANUARY, 1, 00, 0, 0); // 2017-01-01 00:00:00 LocalDateTime to = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 28, 0); // 2019-09-15 14:28:00 Duration duration = Duration.between(from, to); // 表示从 from 到 to 这段时间 long days = duration.toDays(); // 这段时间的总天数 long hours = duration.toHours(); // 这段时间的小时数 long minutes = duration.toMinutes(); // 这段时间的分钟数 long seconds = duration.getSeconds(); // 这段时间的秒数 long milliSeconds = duration.toMillis(); // 这段时间的毫秒数 long nanoSeconds = duration.toNanos(); // 这段时间的纳秒数 Duration duration1 = Duration.of(5, ChronoUnit.DAYS); // 5天 Duration duration2 = Duration.of(1000, ChronoUnit.MILLIS); // 1000毫秒 复制代码
Period
在概念上和Duration
相似,区别在于Period
是以年月日来衡量一个时间段,好比2年3个月6天:oop
Period period = Period.of(2, 3, 6);
Period
对象也能够经过between()
方法建立,值得注意的是,因为Period
是以年月日衡量时间段,因此between()方法只能接收LocalDate类型的参数:
// 2017-01-05 到 2017-02-05 这段时间 Period period = Period.between( LocalDate.of(2017, 1, 5), LocalDate.of(2017, 2, 5)); 复制代码
LocalDate today = LocalDate.now(); LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); 复制代码
它经过指定一个时区,而后就能够获取到当前的时刻,日期与时间。Clock能够替换System.currentTimeMillis()与TimeZone.getDefault()。
Clock clock = Clock.systemUTC(); System.out.println(clock.instant() );//2020-05-26T16:54:54.141Z System.out.println(clock.millis() );//1590512094273 复制代码
Java 8中的时区操做被很大程度上简化了,新的时区类java.time.ZoneId
是原有的java.util.TimeZone
类的替代品。ZoneId
对象能够经过ZoneId.of()
方法建立,也能够经过ZoneId.systemDefault()
获取系统默认时区:
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai"); ZoneId systemZoneId = ZoneId.systemDefault(); 复制代码
of()
方法接收一个“区域/城市”的字符串做为参数,你能够经过getAvailableZoneIds()
方法获取全部合法的“区域/城市”字符串:
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
复制代码
对于老的时区类TimeZone
,Java 8也提供了转化方法:
ZoneId oldToNewZoneId = TimeZone.getDefault().toZoneId();
复制代码
有了ZoneId
,咱们就能够将一个LocalDate
、LocalTime
或LocalDateTime
对象转化为ZonedDateTime
对象:
LocalDateTime localDateTime = LocalDateTime.now(); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghaiZoneId); 复制代码
将zonedDateTime
打印到控制台为:
2017-01-05T15:26:56.147+08:00[Asia/Shanghai] 复制代码
对象由两部分构成,LocalDateTime
和ZoneId
,其中2017-01-05T15:26:56.147
部分为LocalDateTime
,+08:00[Asia/Shanghai]
部分为ZoneId
。
另外一种表示时区的方式是使用ZoneOffset
,它是以当前时间和**世界标准时间(UTC)/格林威治时间(GMT)**的误差来计算,例如:
ZoneOffset zoneOffset = ZoneOffset.of("+09:00"); LocalDateTime localDateTime = LocalDateTime.now(); OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, zoneOffset); 复制代码
GMT(格林威治时间)、CST(可视为美国、澳大利亚、古巴或中国的标准时间)、PST(太平洋时间)
GMT: UTC +0 = GMT: GMT +0
CST: UTC +8 = CST: GMT +8
PST: UTC -8 = PST: GMT -8
复制代码
从上面的图中咱们能够看出,LocalDateTime,并不能表示咱们人类世界中完整的时间,而ZonedDateTime能够。
并且上面的转换中咱们能够知道,LocalDateTime转Instant或者OffsetDatetime都是须要加上偏移时区的(ZoneOffset)。
因此能够得出 OffsetDatetime和Instant也是能够表示人类世界中完整的时间的,和ZoneDateTime是等效的。
OffsetDateTime ,ZonedDateTime 和 Instant 都会在时间线上存储一个纳秒级精度。 Instant 是最简单的,只需表明instant。 OffsetDateTime 添加到UTC / Greenwich的偏移瞬间,这容许得到本地日期时间。 ZonedDateTime 添加完整的时区规则。
所以 OffsetDateTime 和之间的区别ZonedDateTime 是后者包括涵盖夏令时调整的规则。
国际时区 TimeZone ID列表 获取方法:TimeZone.getAvailableIDs()
Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是咱们所说的公历。平年有365天,闰年是366天。闰年的定义是:非世纪年,能被4整除;世纪年能被400整除。为了计算的一致性,公元1年的前一年被当作公元0年,以此类推。
此外Java 8还提供了4套其余历法(很奇怪为何没有汉族人使用的农历),每套历法都包含一个日期类,分别是:
ThaiBuddhistDate
:泰国佛教历MinguoDate
:中华民国历JapaneseDate
:日本历HijrahDate
:伊斯兰历每一个日期类都继承ChronoLocalDate
类,因此能够在不知道具体历法的状况下也能够操做。不过这些历法通常不经常使用,除非是有某些特殊需求状况下才会使用。
这些不一样的历法也能够用于向公历转换:
LocalDate date = LocalDate.now(); JapaneseDate jpDate = JapaneseDate.from(date); 复制代码
因为它们都继承ChronoLocalDate
类,因此在不知道具体历法状况下,能够经过ChronoLocalDate
类操做日期:
Chronology jpChronology = Chronology.ofLocale(Locale.JAPANESE); ChronoLocalDate jpChronoLocalDate = jpChronology.dateNow(); 复制代码
咱们在开发过程当中应该尽可能避免使用ChronoLocalDate
,尽可能用与历法无关的方式操做时间,由于不一样的历法计算日期的方式不同,好比开发者会在程序中作一些假设,假设一年中有12个月,若是是中国农历中包含了闰月,一年有多是13个月,但开发者认为是12个月,多出来的一个月属于明年的。再好比假设年份是累加的,过了一年就在原来的年份上加一,但日本天皇在换代以后须要从新纪年,因此过了一年年份可能会从1开始计算。
在实际开发过程当中建议使用LocalDate
,包括存储、操做、业务规则的解读;除非须要将程序的输入或者输出本地化,这时可使用ChronoLocalDate
类。
LocalDate date = LocalDate.of(2017, 1, 5); // 2017-01-05 LocalDate date1 = date.withYear(2016); // 修改成 2016-01-05 LocalDate date2 = date.withMonth(2); // 修改成 2017-02-05 LocalDate date3 = date.withDayOfMonth(1); // 修改成 2017-01-01 LocalDate date4 = date.plusYears(1); // 增长一年 2018-01-05 LocalDate date5 = date.minusMonths(2); // 减小两个月 2016-11-05 LocalDate date6 = date.plus(5, ChronoUnit.DAYS); // 增长5天 2017-01-10 复制代码
像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 对象为模板,对某些状态进行修改建立该对象的副本 |
TemporalField
是一个接口,它定义了如何访问temporal对象某个字段的值。ChronoField
枚举类实现了这一接口,因此你能够很方便地使用get方法获得枚举元素的值:
int year = LocalDate.now().get(ChronoField.YEAR); LocalDate date1 = LocalDate.now(); // 下一个周二 LocalDate nextTuesday = date1.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)); // 当月的第二个周六 LocalDate firstInMonth = LocalDate.of(date1.getYear(), date1.getMonth(), 1); LocalDate secondSaturday = firstInMonth.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)).with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); 复制代码
「注:TemporalAdjusters类中有许多经常使用的特殊的日期的方法(类方法),使用时能够仔细查看,能够很大程度减小日期判断的代码量!」
TemporalAdjusters 包含许多静态方法,能够直接调用,如下列举一些:
方法名 | 描述 |
---|---|
dayOfWeekInMonth | 返回同一个月中每周的第几天 |
firstDayOfMonth | 返回当月的第一天 |
firstDayOfNextMonth | 返回下月的第一天 |
firstDayOfNextYear | 返回下一年的第一天 |
firstDayOfYear | 返回本年的第一天 |
firstInMonth | 返回同一个月中第一个星期几 |
lastDayOfMonth | 返回当月的最后一天 |
lastDayOfNextMonth | 返回下月的最后一天 |
lastDayOfNextYear | 返回下一年的最后一天 |
lastDayOfYear | 返回本年的最后一天 |
lastInMonth | 返回同一个月中最后一个星期几 |
next / previous | 返回后一个/前一个给定的星期几 |
nextOrSame / previousOrSame | 返回后一个/前一个给定的星期几,若是这个值知足条件,直接返回 |
LocalDate localDate = LocalDate.of(2019, 9, 12); String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE); String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE); System.out.println("s1:"+ s1); // 20190912 System.out.println("s2:"+ s2); // 2019-09-12 LocalDateTime localDateTime = LocalDateTime.now(); System.out.println("获取当前时间:"+localDateTime); // 2019-09-16T14:54:36.520 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:MM:SS"); String s = localDateTime.format(formatter); System.out.println("格式化当前时间:"+ s); // 2019-09-16 14:09:52 LocalDate localDate1 = LocalDate.parse("20190912", DateTimeFormatter.BASIC_ISO_DATE); LocalDate localDate2 = LocalDate.parse("2019-09-12", DateTimeFormatter.ISO_LOCAL_DATE); 复制代码
DateTimeFormatter咱们更多的是直接使用pattern来作转换,其实这个类自己已经提供了一些预约义好的实例供咱们使用。 下面把二者的具体释义和示例都贴出来供你们参考。
预约义
Predefined Formatters | Formatter Description | Example |
---|---|---|
ofLocalizedDate(dateStyle) | Formatter with date style from the locale | '2011-12-03' |
ofLocalizedTime(timeStyle) | Formatter with time style from the locale | '10:15:30' |
ofLocalizedDateTime(dateTimeStyle) | Formatter with a style for date and time from the locale | '3 Jun 2008 11:05:30' |
ofLocalizedDateTime(dateStyle,timeStyle) | Formatter with date and time styles from the locale | '3 Jun 2008 11:05' |
BASIC_ISO_DATE | Basic ISO date | '20111203' |
ISO_LOCAL_DATE | ISO Local Date | '2011-12-03' |
ISO_OFFSET_DATE | ISO Date with offset | '2011-12-03+01:00' |
ISO_DATE | ISO Date with or without offset | '2011-12-03+01:00'; '2011-12-03' |
ISO_LOCAL_TIME | Time without offset | '10:15:30' |
ISO_OFFSET_TIME | Time with offset | '10:15:30+01:00' |
ISO_TIME | Time with or without offset | '10:15:30+01:00'; '10:15:30' |
ISO_LOCAL_DATE_TIME | ISO Local Date and Time | '2011-12-03T10:15:30' |
ISO_OFFSET_DATE_TIME | Date Time with Offset | '2011-12-03T10:15:30+01:00' |
ISO_ZONED_DATE_TIME | Zoned Date Time | '2011-12-03T10:15:30+01:00[Europe/Paris]' |
ISO_DATE_TIME | Date and time with ZoneId | '2011-12-03T10:15:30+01:00[Europe/Paris]' |
ISO_ORDINAL_DATE | Year and day of year | '2012-337' |
ISO_WEEK_DATE | Year and Week | '2012-W48-6' |
ISO_INSTANT | Date and Time of an Instant | '2011-12-03T10:15:30Z' |
RFC_1123_DATE_TIME | RFC 1123 / RFC 822 | 'Tue, 3 Jun 2008 11:05:30 GMT' |
Pattern
All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The following pattern letters are defined:
Symbol | Meaning | Presentation | Examples |
---|---|---|---|
G | era | text | AD; Anno Domini; A |
u | year | year | 2004; 04 |
y | year-of-era | year | 2004; 04 |
D | day-of-year | number | 189 |
M/L | month-of-year | number/text | 7; 07; Jul; July; J |
d | day-of-month | number | 10 |
Q/q | quarter-of-year | number/text | 3; 03; Q3; 3rd quarter |
Y | week-based-year | year | 1996; 96 |
w | week-of-week-based-year | number | 27 |
W | week-of-month | number | 4 |
E | day-of-week | text | Tue; Tuesday; T |
e/c | localized day-of-week | number/text | 2; 02; Tue; Tuesday; T |
F | week-of-month | number | 3 |
a | am-pm-of-day | text | PM |
h | clock-hour-of-am-pm (1-12) | number | 12 |
K | hour-of-am-pm (0-11) | number | 0 |
k | clock-hour-of-am-pm (1-24) | number | 0 |
H | hour-of-day (0-23) | number | 0 |
m | minute-of-hour | number | 30 |
s | second-of-minute | number | 55 |
S | fraction-of-second | fraction | 978 |
A | milli-of-day | number | 1234 |
n | nano-of-second | number | 987654321 |
N | nano-of-day | number | 1234000000 |
V | time-zone ID | zone-id | America/Los_Angeles; Z; -08:30 |
z | time-zone name | zone-name | Pacific Standard Time; PST |
O | localized zone-offset | offset-O | GMT+8; GMT+08:00; UTC-08:00; |
X | zone-offset 'Z' for zero | offset-X | Z; -08; -0830; -08:30; -083015; -08:30:15; |
x | zone-offset | offset-x | +0000; -08; -0830; -08:30; -083015; -08:30:15; |
Z | zone-offset | offset-Z | +0000; -0800; -08:00; |
p | pad next | pad modifier | 1 |
' | escape for text | delimiter | |
'' | single quote | literal | ' |
[ | optional section start | ||
] | optional section end | ||
# | reserved for future use | ||
{ | reserved for future use | ||
} | reserved for future use |
// Date -> LocalDateTime/LocalDate Date date = new Date(); LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // LocalDate -> Date LocalDate nowLocalDate = LocalDate.now(); Date date = Date.from(nowLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); // LocalDateTime -> Date LocalDateTime localDateTime = LocalDateTime.now(); Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); 复制代码
// Date -> Instant Instant timestamp = new Date().toInstant(); // Instant -> Date Date date = Date.from(Instant.now()); 复制代码
ZoneId defaultZone = TimeZone.getDefault().toZoneId(); TimeZone tz = TimeZone.getTimeZone(defaultZone); 复制代码
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();//GregorianCalendar -> ZonedDateTime GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);//ZonedDateTime -> GregorianCalendar 复制代码
// Long时间戳 -> LocalDateTime long timestamp = System.currentTimeMillis(); LocalDateTime localDateTime = Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDate -> Long时间戳 LocalDate localDate = LocalDate.now(); long timestamp = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli(); // LocalDateTime -> Long时间戳 LocalDateTime localDateTime = LocalDateTime.now(); long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); // GMT +8时区 复制代码
// LocalDateTime -> Instant Instant instant = LocalDateTime.now().toInstant(ZoneOffset.of("+8")); // 或者 Instant instant1 = LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)); // Instant -> LocalDateTime LocalDateTime instantToLocalDateTime = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); 复制代码
// String -> LocalDateTime LocalDateTime stringToLocalDateTime = LocalDateTime.parse("2018-03-11 15:30:11", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // LocalDateTime -> String String localDateTimeToString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 复制代码
// String -> Date Date stringToDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-03-11 15:30:11"); //Date -> String String dateToString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); 复制代码
// Timestamp -> LocalDateTime LocalDateTime timeStampToLocalDateTime = LocalDateTime.ofInstant(new Timestamp(1520754566856L).toInstant(), ZoneId.systemDefault()); // LocalDateTime -> TimeStamp Timestamp localDateTimeToTimeStamp = Timestamp.valueOf(LocalDateTime.now()); 复制代码
// Timestamp -> Date Date timestampToDate = Date.from(new Timestamp(1520754566856L).toInstant()); // Date -> LocalDateTime LocalDateTime dateToLocalDateTime = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()); 复制代码
「参考」
Java 8 日期时间 API 指南 | Java 8 教程汇总
Java 8新特性(四):新的时间和日期API | 一书生VOID的博客
Java8新特性总结 -6.Date/Time API_java_BlueKitty的博客-CSDN博客
Java8新特性整理之新的时间和日期API(终章)_java_一大三千的博客-CSDN博客
Java8学习笔记:LocalDateTime、Instant 和 OffsetDateTime 相互转换_java_山鬼谣的专栏-CSDN博客
本文使用 mdnice 排版