一文带你学会Java8的时间API!

2,777 阅读18分钟

前言

Java8之前日期/时间API存在的问题

  • 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
  • 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
  • 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

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

Java8新日期/时间API的改变

Java 8日期/时间API是JSR-310的实现,它的实现目标是克服旧的日期时间实现中所有的缺陷,新的日期/时间API的一些设计原则是:

  • 不变性:新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处。
  • 关注点分离:新的API将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
  • 清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用now()方法,在所有的类中都定义了format()和parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
  • 实用操作:所有新的日期/时间API类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。
  • 可扩展性:新的日期/时间API是工作在ISO-8601日历系统上的,但我们也可以将其应用在非IOS的日历上。

Java日期/时间API包含以下相应的包:

  • java.time包:这是新的Java日期/时间API的基础包,所有的主要基础类都是这个包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。所有这些类都是不可变的和线程安全的,在绝大多数情况下,这些类能够有效地处理一些公共的需求。
  • java.time.chrono包:这个包为非ISO的日历系统定义了一些泛化的API,我们可以扩展AbstractChronology类来创建自己的日历系统。
  • java.time.format包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为java.time包中相应的类已经提供了格式化和解析的方法。
  • java.time.temporal包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。
  • java.time.zone包:这个包包含支持不同时区以及相关规则的类。

Java8提供的LocalDate和DateTimeFormat是如何保证线程安全的?

LocalDate类和DateTimeFormatter类都是final类型的,也就是说,它们是不可变的,一旦实例化,值就固定了。而Java8之前的Date类和SimpleDateFormat类都不是final的。

分析一下源码。

LocalDateTime-Src
LocalDateTime-Src
DateTimeFormatter-Src
DateTimeFormatter-Src

Java8日期/时间API介绍

Java8新增类型

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:时间格式化类

下面介绍下如何使用:

LocalDate 只会获取年月日

// 创建 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 = 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"));

LocalDateTime 获取年月日时分秒

相当于 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"));

Instant 获取秒数

用于表示一个时间戳(精确到纳秒)

它与我们常使用的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();

Period/Duration 时间差

  • Period - 处理有关基于时间的日期数量。
  • Duration - 处理有关基于时间的时间量。

Duration 表示一个时间段

// 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 表示日期上的时间差

Period在概念上和Duration类似,区别在于Period是以年月日来衡量一个时间段,比如2年3个月6天:

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));

ChronoUnit 时间枚举类

LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);

Clock

它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。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,我们就可以将一个LocalDateLocalTimeLocalDateTime对象转化为ZonedDateTime对象:

LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghaiZoneId);

zonedDateTime打印到控制台为:

2017-01-05T15:26:56.147+08:00[Asia/Shanghai]

ZonedDateTime 带时区日期时间处理

对象由两部分构成,LocalDateTimeZoneId,其中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
ZonedDateTime
ZonedDateTime

从上面的图中我们可以看出,LocalDateTime,并不能表示我们人类世界中完整的时间,而ZonedDateTime可以。

而且上面的转换中我们可以知道,LocalDateTime转Instant或者OffsetDatetime都是需要加上偏移时区的(ZoneOffset)。

所以可以得出 OffsetDatetime和Instant也是可以表示人类世界中完整的时间的,和ZoneDateTime是等效的。

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-复杂操作

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 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回

DateTimeFormatter 格式化日期

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 和 LocalDate 互转

// 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 互转

// Date -> Instant
Instant timestamp = new Date().toInstant();
// Instant -> Date
Date date = Date.from(Instant.now());

TimeZone 和 ZoneId 互转

ZoneId defaultZone = TimeZone.getDefault().toZoneId();
TimeZone tz = TimeZone.getTimeZone(defaultZone);

ZonedDateTime 和 GregorianCalendar 互转

ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();//GregorianCalendar -> ZonedDateTime
GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);//ZonedDateTime -> GregorianCalendar

其他相关转换

Long时间戳 和 LocalDateTime 互转

// 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时区

Instant 和 LocalDateTime互转

// 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 互转

// 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 互转

// 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 互转

// Timestamp -> LocalDateTime
LocalDateTime timeStampToLocalDateTime = LocalDateTime.ofInstant(new Timestamp(1520754566856L).toInstant(), ZoneId.systemDefault());

// LocalDateTime -> TimeStamp
Timestamp localDateTimeToTimeStamp = Timestamp.valueOf(LocalDateTime.now());

Timestamp 和 Date 互转

// 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 教程汇总

Java8新特性之日期和时间 - mrjade - 博客园

Java 8新特性(四):新的时间和日期API | 一书生VOID的博客

Java8新特性总结 -6.Date/Time API_java_BlueKitty的博客-CSDN博客

Java8新特性整理之新的时间和日期API(终章)_java_一大三千的博客-CSDN博客

Java8学习笔记:LocalDateTime、Instant 和 OffsetDateTime 相互转换_java_山鬼谣的专栏-CSDN博客


求关注、分享、在看!!! 你的支持是我创作最大的动力。

本文使用 mdnice 排版