一、综述

1.1 java8以前的时间日期类

  1. java.util.Date: 表示日期和时间的类,已经过时,不推荐使用。
  2. java.util.Calendar: 提供了对日期和时间字段进行操作的抽象类。
  3. java.util.GregorianCalendar: Calendar类的具体实现,支持公历系统。
  4. java.text.SimpleDateFormat: 用于格式化和解析日期的类。
  5. java.sql.Date: 用于与数据库交互时表示日期的类。
  6. java.sql.Time: 用于与数据库交互时表示时间的类。
  7. java.sql.Timestamp: 用于表示日期和时间戳的类,继承自java.util.Date。

1.2 java8新引入的时间日期类

        java.time包(自Java 8引入): 提供了全新的日期和时间API,设计更加清晰和全面。

  • java.time.LocalDate: 表示日期,不包含时间。
  • java.time.LocalTime: 表示时间,不包含日期。
  • java.time.LocalDateTime: 表示日期和时间,不考虑时区。
  • java.time.ZonedDateTime: 表示带时区的日期和时间。
  • java.time.Instant: 表示时间戳,通常与协调世界时(UTC)一起使用。
  • java.time.Duration: 表示时间间隔,可以用于计算两个时间点之间的差异。
  • java.time.Period: 表示日期间隔,可以用于计算两个日期之间的差异。
  • java.time.format.DateTimeFormatter: 用于格式化和解析日期时间对象的类。

二、主要区别

2.1 线程安全性

如SimpleDateFormat、Date等类是线程不安全的。如果多个线程同时尝试使用相同的SimpleDateFormat实例进行格式化或解析,可能导致错误的结果。

而新增的时间类,如DateTimeFormatter、LocalDateTime等是线程安全的。它设计为可供多个线程同时使用,因此更适合在并发环境中使用。

2.2 不可变性

SimpleDateFormat等是可变的,如果多个线程同时使用相同的实例,可能导致不确定的结果。

DateTimeFormatter等新时间处理类是不可变的,确保线程安全性,并使得实例可以被共享而不引起问题。

2.3 时区问题

Date类中存储的是从1970年1月1日00:00:00(GMT)以来的毫秒数的长整型值,但是其中并没有包含对时区信息的直接处理方法,我们可以通过toString来查看时区,但没有快速、便捷的方法来对时区进行处理,因此其用于处理跨时区的相关时间问题时比较复杂。

而新版时间类中,提供了ZonedDateTime类来处理涉及时区的相关问题。如:

// 创建一个带有时区的日期时间对象
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);

三、常用新版旧版时间类之间的转换

3.1 Date与LocalDateTime

Date转LocalDateTime

// 创建一个java.util.Date对象
Date date = new Date();

// 使用Instant将Date转换为LocalDateTime
Instant instant = date.toInstant();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

// 输出结果
System.out.println("Date: " + date);    //Date: Mon Jan 08 20:52:49 CST 2024
System.out.println("LocalDateTime: " + localDateTime); //LocalDateTime: 2024-01-08T20:52:49.273

LocalDateTime转Date

// 创建一个LocalDateTime对象
LocalDateTime localDateTime = LocalDateTime.now();

// 使用atZone和toInstant将LocalDateTime转换为Date
ZoneId zoneId = ZoneId.systemDefault();
Date date = Date.from(localDateTime.atZone(zoneId).toInstant());

// 输出结果
System.out.println("LocalDateTime: " + localDateTime);//LocalDateTime: 2024-01-08T20:56:44.417
System.out.println("Date: " + date);//Date: Mon Jan 08 20:56:44 CST 2024

3.2 Date转LocalDate和LocalTime

Date date = new Date();

// 转换为LocalDate
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

// 转换为LocalTime
LocalTime localTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();

// 输出结果
System.out.println("Date: " + date); //Date: Mon Jan 08 21:06:28 CST 2024
System.out.println("LocalDate: " + localDate); //LocalDate: 2024-01-08
System.out.println("LocalTime: " + localTime); //LocalTime: 21:06:28.222

3.3 LocalDate与LocalTime转Date

// 创建一个LocalDate对象
LocalDate localDate = LocalDate.now();

// 创建一个LocalTime对象
LocalTime localTime = LocalTime.now();

// 合并成一个LocalDateTime对象
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);

// 转换为Date
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

// 输出结果
System.out.println("LocalDate: " + localDate); //LocalDate: 2024-01-08
System.out.println("LocalTime: " + localTime); //LocalTime: 21:08:29.830
System.out.println("Date: " + date); //Date: Mon Jan 08 21:08:29 CST 2024

四、时间的格式化与解析

4.1 SimpleDateFormat

构造函数需要一个pattern作为格式,yyyy代表年份,大写的MM代表月份,dd代表日期,HH、小写的mm、ss分别代表时、分、秒,中间用空格、/、-等连接都可以。

格式化:

// 创建SimpleDateFormat实例,指定日期时间格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// 创建一个Date对象
Date date = new Date();

// 使用format方法将Date对象格式化为字符串
String formattedDate = sdf.format(date);

// 输出结果
System.out.println("结果:" + formattedDate); //结果:2024-01-08 21:12:02

解析:

// 创建SimpleDateFormat实例,指定日期时间格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = "2024-01-08 21:12:02";
try {
    // 使用parse方法将字符串解析为Date对象
    Date parsedDate = sdf.parse(str);

    // 输出结果
    System.out.println("解析结果: " + parsedDate); //解析结果: Mon Jan 08 21:12:02 CST 2024
} catch (ParseException e) {
    e.printStackTrace();
}

此处还有一个注意点,SimpleDateFormat在解析时,会自动忽略秒数后面的字符,如:

String str = "2024-01-08 21:12:02cascsaczcsa";

一样可以被正确解析得到与先前一样的结果。而DateTimeFormatter则修复了这一问题,会直接抛异常。

4.2 DateTimeFormatter

格式化:

// 创建DateTimeFormatter实例,指定日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

// 创建一个LocalDateTime对象
LocalDateTime localDateTime = LocalDateTime.now();

// 使用format方法将LocalDateTime对象格式化为字符串
String formattedDateTime = localDateTime.format(formatter);

// 输出结果
System.out.println("格式化结果: " + formattedDateTime);//格式化结果: 2024-01-08 21:22:30

解析:

// 创建DateTimeFormatter实例,指定日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

// 待解析的日期时间字符串
String dateTimeString = "2024-01-08 21:22:30";

// 使用parse方法将字符串解析为LocalDateTime对象
LocalDateTime parsedLocalDateTime = LocalDateTime.parse(dateTimeString, formatter);

// 输出结果
System.out.println("解析结果: " + parsedLocalDateTime);//解析结果: 2024-01-08T21:22:30