乐趣区

全面解析Java日期时间API

时区

  • GMT(Greenwich Mean Time):格林尼治时间,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。
  • UTC(Universal Time Coordinated):统一协调时间,其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间,标准 UTC 时间格式 yyyy-MM-dd'T'HH:mm:ss.SSSXXX

格林尼治时间已经不再被作为标准时间使用,UTC 是最主要的世界时间标准。

Java 提供了获取当前时间的方法

  • System.currentTimeMillis(),返回当前时间,以毫秒为单位,表示的就是当前时刻至 1970-01-01 00:00:00.000 的毫秒差值。返回的 long 值可以用来初始化 java.util.Date, java.sql.Date, java.sql.Timestamp 和 java.util.GregorianCalendar 对象。
  • System.nanoTime(),返回一个时间值(系统计时器的当前值),精确到纳秒。它是由 JVM 提供的一个时间,主要用来精确衡量两个时间段之间的时间

例如,要测量一些代码需要执行多长时间,实现如下,

long startTime = System.nanoTime();
//...the code being measured
long estimatedTime = System.nanoTime() - startTime;

时间粒度
事实上 System.currentTimeMillis()方法的时间粒度是大于 1 毫秒的。如果你反复执行这个方法,你会发现短时间内得到的结果是相同的,随后又突然在某一次结果增加了几十毫秒(也可能更多)。这是很正常的,毕竟这个方法肯定不是世界上最精密的计时器。

旧的时间 API

旧的时间 API 存在诸多问题,例如

  • Java 的日期 / 时间类的定义并不一致,在 java.util 和 java.sql 的包中都有日期类,此外用于格式化和解析的类在 java.text 包中定义。
  • java.util.Date 同时包含日期和时间,而 java.sql.Date 仅包含日期,将其纳入 java.sql 包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
  • 对于时间、时间戳、格式化以及解析,并没有一些明确定义的类。对于格式化和解析的需求,我们有 java.text.DateFormat 抽象类,但通常情况下,SimpleDateFormat 类被用于此类需求。
  • 所有的日期类都是可变的,因此他们都不是线程安全的,这是 Java 日期类最大的问题之一。
  • 日期类并不提供国际化,没有时区支持,因此 Java 引入了 java.util.Calendar 和 java.util.TimeZone 类,但他们同样存在上述所有的问题。

java.util.Date 类

java.util.Date 类用于封装日期及时间信息,一般仅用它显示某个日期,不对他作任何操作处理,作处理推荐用 Calendar 类,计算方便。以下已过时的方法没有列出,可自行查看 jdk 文档

构造方法

  • Date():分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
  • Date(long date):分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
//1、使用 Date 类获取当前系统时间
Date date = new Date();
System.out.println(date);
// 由于 Date 类覆盖了 toString()方法,所以可以直接输出 Date 类型的对象
// 输出结果为 Fri May 31 10:51:18 GMT+08:00 2019

/* 给 Date 设置年份需要减去 1900
* 输出结果 Tue Jul 01 00:00:00 GMT+08:00 3919
* 原来这里存在一个起始年份 1900,实际年份是要在你的年份参数上加上个起始年份。*/
Date date1 = new Date(2019,5,31);
System.out.println(date1);

//2. 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。Date date2 = new Date(System.currentTimeMillis());// 参数为 19701 月 1 日以来的毫秒数
Date date3 = new Date(1559284293556l); //long 类型要加 l
System.out.println(date2);
System.out.println(date3);
// 其他 Date 方法摘要可查看 api

Calendar 类与 GregorianCalendar

java.util.Calendar 类用于封装日历信息,其主作用在于其方法可以对时间分量进行运算。Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。

由于 Calendar 类是抽象类,且 Calendar 类的构造方法是 protected 的,所以无法使用 Calendar 类的构造方法来创建对象,API 中提供了 getInstance 方法用来创建对象。

Java 只提供 java.util.GregorianCalendar 这一种 java.util.Calendar 的实现类,可以直接新建出来使用:

Calendar calendar = new GregorianCalendar();// 新建出来的 calendar 默认时间为当前时间,或者说创建出这个对象的时间。

通过 Calendar 的静态方法获取一个实例该方法会根据当前系统所在地区来自行决定时区,帮我们创建 Calendar 实例,这里要注意,实际上根据不同的地区,Calendar 有若干个子类实现。而 Calendar 本身是抽象类,不能被实例化!我们不需要关心创建的具体实例为哪个子类,我们只需要根据 Calendar 规定的方法来使用就可以了。

日历类所解决的根本问题是简化日期的计算,要想表示某个日期还应该使用 Date 类描述。Calendar 是可以将其描述的时间转化为 Date 的,我们只需要调用其 getTime()方法就可以获取描述的日期的 Date 对象了。

通过日历类计算时间:为日历类设置时间,日历类设置时间使用通用方法 set。set(int field,int value),field 为时间分量,Calendar 提供了相应的常量值,value 为对应的值。

只有月份从 0 开始:0 为 1 月,以此类推,11 为 12 月,其他时间是正常的从 1 开始。也可以使用 Calendar 的常量 calendar.NOVEMBER……等。

Calendar.DAY_OF_MONTH  月里边的天 --- 号;Calendar.DAY_OF_WEEK    星期里的天 --- 星期几
Calendar.DAY_OF_YEAR     年里的天
Calendar calendar=Calendar.getInstance();// 构造出来表示当前时间的日历类
Date now=calendar.getTime();// 获取日历所描述的日期
calendar.set(Calendar.YEAR, 2019);// 设置日历表示 2019 年 calendar.set(Calendar.DAY_OF_MONTH,15);// 设置日历表示 15 号
calendar.add(Calendar.DAY_OF_YEAR, 22);// 想得到 22 天以后是哪天
calendar.add(Calendar.DAY_OF_YEAR, -5);// 5 天以前是哪天
calendar.add(Calendar.MONTH, 1); 得到 1 个月后是哪天
System.out.println(calendar.getTime());

获取当前日历表示的日期中的某个时间单位可以使用 get 方法。

int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH)
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year+"年"+(month+1)+"月"+day+"日");//month 要处理

SimpleDateFormat 类

常用构造方法

SimpleDateFormat(String pattern),pattern - 为描述日期和时间格式的模式

用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。注:此构造方法可能不支持所有语言环境。要覆盖所有语言环境,请使用 DateFormat 类中的工厂方法。

常用方法

public final String format(Date date)将一个 Date 格式化为日期 / 时间字符串
public Date parse(String source)throws ParseException 从给定字符串的开始解析文本,以生成一个日期。

字符串转成 Date 对象

// 创建一个 SimpleDateFormat 并且告知它要读取的字符串格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateFormat = "2019-05-31";// 创建一个日期格式字符串
// 将一个字符串转换为相应的 Date 对象
Date date = sdf.parse(dateFormat);// 要先捕获异常
System.out.println(date);// 输出这个 Date 对象

Date 对象转成字符串

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
Date date = new Date(); 
String dateStr = sdf.format(date);// 把日期对象传进去,继承自 DateFormat 类的方法。将一个 Date 格式化为日期 / 时间字符串

在日期格式中,- 和空格无特殊意义。无特殊含义的都原样输出

// 将当前系统时间转换为 2012/05/14 17:05:22 的效果
SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
dateStr=format1.format(date);
System.out.println(dateStr);

DateFormat 类

java.text.DateFormat 类(抽象类)是 SimpleDateFormat 类的父类,用的少,没 SimpleDateFormat 灵活。

java.sql.Date

java.sql.Date 继承 java.util.Date, 为了把前者转为后者,需要以下代码

Date date = new Date();
//java.sql.Date 不支持 Date 参数的构造器,传入 long 类型的时间
java.sql.Date d = new java.sql.Date(date.getTime());
System.out.println(d);
System.out.println(d.getHours());

输出结果

2019-05-31
Exception in thread "main" java.lang.IllegalArgumentException
    at java.sql.Date.getHours(Unknown Source)
    at DateTest.DateTest1.main(DateTest1.java:40)

这是由于 java.sql.Date 是 SQL 中的单纯的日期类型,没有时分秒。故 一般通过 JDBC 插入 java.sql.Date 数据时 ,会发现时分秒都没有。因此,如果同时需要日期和时间, 应该使用 Timestamp。它也是 java.util.Date 的子类,Timestamp 则包含时间戳的完整信息。

java.sql.Timestamp 是 java.util.Date 的派生类(继承),所以在 java.util.Date 上能做的事,也可以在 java.sql.Timestamp 上做。
如果当前是 2019-06-01 14:35,你创建一个 java.sql.Date 将只记下 2019-06-01 这个信息。若你需要保留时间进行 JDBC 操作,请使用 java.sql.Timestamp 代替。

long time = System.currentTimeMillis();
java.sql.Timestamp timestamp = new java.sql.Timestamp(time);
timestamp.setNanos(123456);
int nanos = timestamp.getNanos(); // nanos = 123456

现在的 Date 类中大部分方法已经弃用,现在一般使用旧的 API,Date 只负责存储一个时间,并对 Calendar 和 DateFormat 提供操作接口。Calendar 获取 Date 中特定的信息,对日期时间进行操作,SimpleDateFormat 对日期时间进行格式化输入输出。

总的来说,Date、Calendar 和 DateFormat 已经能够处理一般的时间日期问题了。但是不可避免的是,它们依然很繁琐,不好用并且这些日期类都是可变且线程不安全的

Joda-Time

详情请查看 https://www.ibm.com/developer…

Java 8 时间日期 API

特性

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

Java8 日期时间的默认格式如下:yyyy-MM-dd-HH-mm-ss.zzz

几个主要的核心类:

  • LocalDate:日期类,不带时间
  • LocalTime:时间类,不带日期
  • LocalDateTime:日期和时间类
  • ZonedDateTime:时区日期时间类
  • OffsetDateTime:按 UTC 时间偏移来得到日期时间
  • Clock:获取某个时区下当前的瞬时时间,日期或者时间
  • Instant:Unix 时间,代表时间戳,比如 2018-01-14T02:20:13.592Z
  • Duration:两个时间之间,表示一个绝对的精确跨度,使用毫秒为单位
  • Period:两个日期之间
  • ZoneId:时区
  • DateTimeFormatter:格式化输出
  • TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等等

下面看看这些类具体如何使用

LocalDate、LocalTime、LocalDateTime

LocalDate 是不变的日期时间对象代表一个日期,往往被视为年月日。其他日期字段,如一年中的一天,一周和一周的一天,也可以访问。例如,“2007 年 10 月 2 日”的值可以被存储在一个 LocalDate。

LocalTime 是不变的日期时间对象代表一个时间,往往被视为小时分钟秒。时间为代表的纳秒级精度。例如,值“13:45.30.123456789”可以存储在一个 LocalTime。

LocalDateTime 是不变的日期时间对象代表一个日期时间,往往被视为年、月、日、时、分、秒。其他日期和时间字段,如一年中的一天,一周和一周的一天,也可以访问。时间为代表的纳秒级精度。例如,值“2007 年 10 月 2 日在 13:45.30.123456789”可以存储在一个 LocalDateTime。

//now()在默认时区中从系统时钟获取当前日期。LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); // 等价于 today.plusWeeks(1)

//of(int year, int month, int dayOfMonth) 获得 LocalDate 实例从一年、月、日。LocalDate date = LocalDate.of(2019,5,31);
LocalTime time = LocalTime.of(20,31,20);

//of(LocalDate date, LocalTime time) 获得 LocalDateTime 实例的日期和时间。LocalDateTime dateTime = LocalDateTime.of(date,time);
System.out.println(dateTime);

//LocalDate 结合 LocalTime 成一个 LocalDateTime
LocalDateTime dateTime2 = date.atTime(time);
System.out.println(dateTime2); //2019-05-31T20:31:20

DateTimeFormatter

格式器用于解析日期字符串和格式化日期输出,创建格式器最简单的方法是通过 DateTimeFormatter 的静态工厂方法以及常量。创建格式器一般有如下三种方式:

  1. 常用 ISO 格式常量,如 ISO_LOCAL_DATE
  2. 字母模式,如 ofPattern(“yyyy/MM/dd”)
  3. 本地化样式,如 ofLocalizedDate(FormatStyle.MEDIUM)

和旧的 java.util.DateFormat 相比较,所有的 DateTimeFormatter 实例都是线程安全的。

使用 DateTimeFormatter 完成格式化

// 获取默认时区中从系统时钟获取当前日期时间。LocalDateTime localDateTime = LocalDateTime.now();
// 创建一个格式化程序使用指定的模式。DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = localDateTime.format(formatter);
System.out.println(formatDateTime);

//DateTimeFormatter 提供了一些默认的格式化器,DateTimeFormatter.ISO_LOCAL_DATE_TIME 格式 yyyy-MM-ddTHH:mm:ss.SSS
String dateTime2 = localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(dateTime2);

使用 DateTimeFormatter 完成解析字符串

// 获得 LocalDate 实例, 从使用特定格式的文本字符串解析,文字使用格式化程序解析,返回一个日期。LocalDate localDate = LocalDate.parse("2018/11/11",DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(localDate); //2018-11-11

Instant

Instant 表示时间线上的一点(与 Date 类似),它只是简单地表示自 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC)开始的秒数。

Instant 由两部分组成,一是从原点开始到指定时间点的秒数 s,二是距离该秒数 s 的纳秒数。它以 Unix 时间戳的形式存储日期时间,不提供处理人类意义上的时间单位(年月日等)。

你可以通过 Instant 类的工厂方法创建一个 Instant 实例

// 调用 instant.now()来创建一个确切的表达当前时间的 Instant 对象. 另外也有一些其它方法能创建 Instant,具体请查阅 Java 官方文档。Instant now = Instant.now();
Instant later = now.plusSeconds(3);
Instant earlier = now.minusSeconds(3);

// 第一个参数是秒,第二个是纳秒参数,纳秒的存储范围是 0 至 999,999,999
//2s 之后的在加上 100 万纳秒(1s)Instant instant = Instant.ofEpochSecond(2,1000000000);
System.out.println(instant); //1970-01-01T00:00:03Z

Instant instant1 = Instant.now();
System.out.println(instant1); //2019-05-31T16:19:28.719Z

Instant instant2 = Instant.parse("2018-11-11T10:12:35.342Z");
System.out.println(instant2); //2018-11-11T10:12:35.342Z

//java.util.Date 与 Instant 可相互转换
Instant timestamp = new Date().toInstant();
Date.from(Instant.now());

// 为了更好的显示,代码改写为
Date date = new Date();
Instant timestamp = date.toInstant();
System.out.println(date);
System.out.println(timestamp);
Instant now1 = Instant.now();
Date date1 = Date.from(now1);
System.out.println(now1);
System.out.println(date1);
// 输出结果
Sat Jun 01 00:29:42 GMT+08:00 2019
2019-05-31T16:29:42.566Z
2019-05-31T16:29:42.588Z
Sat Jun 01 00:29:42 GMT+08:00 2019

Clock

Clock 用于查找当前时刻,可以用来获取某个时区下当前的日期和时间,也可以用来代替旧的 System.currentTimeMillis()方法和 TimeZone.getDefault()方法。

// 返回系统默认时间
Clock clock = Clock.systemDefaultZone();
System.out.println(clock.instant().toString());

// 世界协调时 UTC
Clock clock = Clock.systemUTC();  
// 通过 Clock 获取当前时刻  
System.out.println("当前时刻为:" + clock.instant());  
// 获取 clock 对应的毫秒数,与 System.currentTimeMillis()输出相同  
System.out.println(clock.millis());  
System.out.println(System.currentTimeMillis());  

Duration

一个 Duration 实例是不可变的,当创建出对象后就不能改变它的值了。你只能通过 Duration 的计算方法,来创建出一个新的 Durtaion 对象。你会在之后的教程中见到的。一个 Duration 对象表示两个 Instant 间的一段时间。

创建 Duration 实例,使用 Duration 类的工厂方法来创建一个 Duration 对象

Instant first = Instant.now();
// wait some time while something happens
Instant second = Instant.now();
Duration duration = Duration.between(first, second);

// 获得 Duration 表示秒数,然后获得在此期间的分钟数、小时数、天数
Duration d = Duration.ofSeconds(6000);  
System.out.println("6000 秒相当于" + d.toMinutes() + "分");  
System.out.println("6000 秒相当于" + d.toHours() + "小时");  
System.out.println("6000 秒相当于" + d.toDays() + "天");  

访问 Duration 的时间
一个 Duration 对象里有两个域:纳秒值(小于一秒的部分),秒钟值(一共有几秒),他们的组合表达了时间长度。注意与使用 System.getCurrentTimeMillis()时不同,Duration 不包含毫秒这个属性。
你可以通过以下两个方法得到它们的值:getSeconds()和 getNano()

Period

Period 是以年月日来衡量一个时间段,用于计算两个日期间隔,所以 between() 方法只能接收 LocalDate 类型的参数。

LocalDate start = LocalDate.of(2018, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2020, Month.NOVEMBER, 11);
System.out.println("相隔月数:"+Period.between(start, end).getMonths());
System.out.println("相隔天数:"+Period.between(start, end).getDays());
// 输出结果
相隔月数:10
相隔天数:10

值得注意的是,Period 得到的是差值的绝对值(对应年月日直接计算数学上的差值),而并不表示真正的区间距离

long distanceMonth = start.until(end, ChronoUnit.MONTHS);
long  distanceDay= start.until(end, ChronoUnit.DAYS);
System.out.println("相隔月数"+distanceMonth);
System.out.println("相隔天数"+distanceDay);
// 输出结果
相隔月数:34
相隔天数:1045

ZonedDateTime 和 ZonedId

ZonedDateTime 类是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类。ZonedDateTime 类的值是不可变的,所以其计算方法会返回一个新的 ZonedDateTime 实例。

Java 使用 ZoneId 来标识不同的时区,从基准 UTC 开始的一个固定偏移。

创建一个 ZonedDateTime 实例

// 使用当前时间作为值新建对象
ZonedDateTime dateTime = ZonedDateTime.now();

// 使用指定的年月日、时分、纳秒以及时区 ID 来新建对象
// 时区是用 ZoneId 类表示的,可以使用 ZoneId.now()或 ZoneId.of(“xxx”)来实例化:// 传给 of()方法的参数是时区的 ID,如“UTC+1”指距离 UTC(格林威治时间)有一小时的时差
ZoneId zoneId = ZoneId.of("UTC+1");
ZonedDateTime dateTime2 = ZonedDateTime.of(2019, 6, 1, 14, 40, 48, 1234, zoneId);

// 也可以使用另一种方式表示 zone id,即使用地区名字
ZoneId zoneId2 = ZoneId.of("Europe/Copenhagen");

//GregorianCalendar 与 ZonedDateTime 相互转换
ZonedDateTime zonedDateTime = new GregorianCalendar().toZonedDateTime();
GregorianCalendar.from(zonedDateTime);

TemporalAdjusters

有的时候,你需要进行一些更加复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。

简单应用例子

LocalDate localDate = LocalDate.now();  
// 1. 本月第一天
LocalDate firstDayOfMonth = localDate.with(TemporalAdjusters.firstDayOfMonth());
// 2. 本月最后一天
LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());
// 3. 本年第一天
LocalDate firstDayOfYear = localDate.with(TemporalAdjusters.firstDayOfYear());
// 4. 下个月第一天
LocalDate firstDayOfNextMonth = localDate.with(TemporalAdjusters.firstDayOfNextMonth());
// 5. 本年度最后一天
LocalDate lastDayOfYear = localDate.with(TemporalAdjusters.lastDayOfYear());
System.out.println(firstDayOfMonth);
System.out.println(lastDayOfMonth);
System.out.println(firstDayOfYear);
System.out.println(firstDayOfNextMonth);
System.out.println(lastDayOfYear);

这时,你可以使用 重载版本的 with 方法,向其传递一个提供了更多定制化选择的 TemporalAdjuster 对象, 更加灵活地处理日期。TemporalAdjusters 类通过静态方法提供了大量的常用的 TemporalAdjuster 的实现供我们使用。

/**
 * 时间校正器 TemporalAdjuster
 */
@Test
public void Test() {LocalDateTime now1 = LocalDateTime.now();
    // 获取月中的第一天
    now1.with(TemporalAdjusters.firstDayOfMonth());
    // 获取下一年的第一天   
    now1.with(TemporalAdjusters.firstDayOfNextYear());
    // 获取年中第一天
    now1.with(TemporalAdjusters.lastDayOfYear());
    // 获取月中最后一天
    now1.with(TemporalAdjusters.lastDayOfMonth());
    // 获取下个星期一
    now1.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    // 自定时时间:下一个工作日,因为这里需要一个接口,所以完全可以自定义方法
    now1.with((e) -> {LocalDateTime now = (LocalDateTime)e; 
        DayOfWeek dow = now.getDayOfWeek();
        if (dow.equals(DayOfWeek.FRIDAY)) 
            return now.plusDays(3);
        else if (dow.equals(DayOfWeek.SATURDAY))
            return now.plusDays(2);
        return  now.plusDays(1);
    });
}

转换

java.util.Date 与 LocalDate、LocalTime、LocalDateTime 转换

将 Date 转换为 LocalDate,LocalTime,LocalDateTime 可以借助于 ZonedDateTime 和 Instant,实现如下:

Date date = new Date();
System.out.println("current date:" + date);

// Date -> LocalDateTime
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println("localDateTime by Instant:" + localDateTime);

// Date -> LocalDate
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
System.out.println("localDate by Instant:" + localDate);
// Date -> LocalTime
LocalTime localTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
System.out.println("localTime by Instant:" + localTime);

//2. Date -> LocalDateTime
localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
System.out.println("localDateTime by ofInstant:" + localDateTime);

由于 JDK8 实现了向下兼容,所以 Date 里在 JDK8 版本引入了 2 个方法,from 和
toInstant,所以我们可以借助这两个方法来实现 LocalDateTime 到 Date 的转换。将 LocalDateTime 转为 Date 如下:

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("localDateTime:" + localDateTime);

// LocalDateTime -> Date
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("LocalDateTime -> current date:" + date);

// LocalDate -> Date,时间默认都是 00
LocalDate localDate = LocalDate.now();
date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
System.out.println("LocalDate -> current date:" + date);

日期与字符串的转换

通过 LocalDate,LocalTime,LocalDateTime 的 parse 方法和 DateTimeFormatter 来实现:

// 字符串 -> 日期
LocalDate localDate = LocalDate.parse("2018-09-09", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDateTime localDateTime = LocalDateTime.parse("2018-09-10 12:12:12", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

// 日期 -> 字符串
String localDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String localDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 也可以通过 DateTimeFormatter 的 format 方法
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
localDateTime = dateTimeFormatter.format(LocalDateTime.now());

时间戳与 LocalDateTime 转换

具体实现如下:

// 时间戳 ->LocalDateTime
public static LocalDateTime convertToDate(long timestamp) {
   // ofEpochSecond 以秒为单位,ofEpochMilli 以毫秒为单位
   // Instant.ofEpochSecond(timestamp);
   Instant instant = Instant.ofEpochMilli(timestamp);
   return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}

//LocalDateTime-> 时间戳
public static long convertToTimestamp() {LocalDateTime localDateTime = LocalDateTime.now();
   return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}

总结

  • 所有新的日期时间 API 类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期 / 时间中提取单独部分。
  • Java 8 中新的时间与日期 API 中的所有类都是不可变且线程安全的,任何修改操作都会返回一个新的实例。
  • 新的 API 区分各种日期时间概念并且各个概念使用相似的方法定义模式,这种相似性非常有利于 API 的学习。总结一下一般的方法或者方法前缀:

    • of:静态工厂方法,用于创建实例
    • now:静态工厂方法,用当前时间创建实例
    • parse:静态工厂方法,从字符串解析得到对象实例
    • get:获取时间日期对象的部分状态。
    • is:检查某些东西的是否是 true,例如比较时间前后
    • with:返回一个部分状态改变了的时间日期对象拷贝
    • plus:返回一个时间增加了的、时间日期对象拷贝
    • minus:返回一个时间减少了的、时间日期对象拷贝
    • to:转换到另一个类型
    • at:把这个对象与另一个对象组合起来,例如 date.atTime(time)
    • format:提供格式化时间日期对象的能力

更多方法使用请参考 Java api 文档

参考文章:

http://www.importnew.com/1414…

https://docs.oracle.com/javas…

https://www.jianshu.com/p/f4a…

文章同步发表在个人博客 kiwen 的博客

退出移动版