Java日期时间处理类

Posted by Wh0ami-hy on December 21, 2023

1. Java日期发展

1.1. 第一代Date

Date 注意是java.util.Date包下的,不是java.sql包下的 精确到秒,代表特定的瞬间

// 获取当前系统时间
Date d1 = new Date()

// 通过指定毫秒数得到时间
Date d2 = new Date(54646)

SimpleDateFormat 格式化(日期–>文本)和解析日期(文本–>日期)的类

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
// Date日期转字符串
String format = sdf.format(Date日期);
// 字符串转Date日期
String s = "2000年01月01日 10:20:30 星期一"
Date parse = sdf.parse(s)

1.2. 第二代Calendar

Calendar类是一个抽象类,它为操作日历字段,如YEAR、MONTH、DAY_OF _MONTH、HOUR等提供了一些方法

Calendar类是一个抽象类,不能通过new来实例化

// 创建日历类对象
Calendar c = Calendar.getInstance()
// 获取日历对象的某个日历字段
c.get(Calendar.YEAR)
// Calendar返回月的时候,是从0开始编号
c.get(Calendar.MONTH) +1)

Calender 没有专门的格式化方法,所以需要程序员自己来组合显示

System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"+c.get(Calendar.DAY_OF_MONTH)+"日");

1.3. 第三代LocalDateTime(推荐)

前两代

  • 不能处理闰秒
  • 线程不安全

JDK8加入的 该类不存储或表示时区

  • LocalDate只包含日期,可以获取日期字段
  • LocalTime只包含时间,可以获取时间字段
  • LocalDateTime包含日期+时间,可以获取日期和时间字段
// now获取当前日期时间对象
LocalDateTime localDateTime = LocalDateTime.now();
// 获取年
System.out.println("年=" + ldt.getYear());
// 获取英文月
System.out.println("年=" + ldt.getMonth());
// 获取数字月
	System.out.println("年=" + ldt.getMonthValue());

还可以对日期时间进行加减

// 获取当前时间  
LocalDateTime localDateTime = LocalDateTime.now();
// 当前时间减2年
System.out.println(localDateTime.minusYears(2));  
// 当前时间加2年
System.out.println(localDateTime.plusYears(2));|

DateTimeFormatter格式日期类

DateTimeFormat dtf = DateTimeFormatter.ofPattern(格式)
String str = dtf.format(日期对象);

lnstant时间戳

类似于Date,提供了和Date类转换的方式

// 获取当前时间戳的对象
Instant now = Instant.now()
// 把lnstant时间戳转为Date
Date date = Date.from(now)
// 把Date转为lnstant时间戳
Instant instant = date.toInstant
graph LR
  A(JDK8) --> B(LocalDateTime:LocalDate + LocalTime,无时区属性)
  A --> C(Instant,时间戳,类似Date)
  A --> G(DateTimeFormatter,格式化日期时间,线程安全)
  A --> D(ZonedDateTime:LocalDateTime+ZoneId,表示日期和时间,有时区属性)
  A --> H(ZoneId,时区)
  A --> E(Duration,一段时间)
  A --> F(Period,一段日期)

2. 时区问题

日期时间的初始化、时区、格式化、解析和计算

初始化日期时间

处理好时间和时区问题首先就是要正确保存日期时间

以 UTC(Universal Time Coordinated)保存,保存的时间没有时区属性,是不涉及时区时间差问题的世界统一时间。我们通常说的时间戳就是用的这种方式

LocalDateTime localDateTime = LocalDateTime.now();

时区问题(重点)

全球有 24 个时区,同一个时刻不同时区(比如中国上海和美国纽约)的时间是不一样的。对于需要全球化的项目,如果初始化时间时没有提供时区,那就不是一个真正意义上的时间

要正确处理时区,在于存进去和读出来两方面:存的时候,需要使用正确的当前时区来保存,这样 UTC 时间才会正确;读的时候,也只有正确设置本地时区,才能把 UTC 时间转换为正确的当地时间。

使用 ZoneId.of 来初始化一个标准的时区,也可以使用 ZoneOffset.ofHours 通过一个 offset,来初始化一个具有指定时间差的自定义时区。

// 初始化一个时区  
// 上海时区
ZoneId timeZoneSH = ZoneId.of("Asia/Shanghai");  
// 纽约时区
ZoneId timeZoneNY = ZoneId.of("America/New_York");

获得一个带有时区属性的时间(有效时间)

// 初始化一个时间  
LocalDateTime localDateTime = LocalDateTime.now();
// 指定一个时区
ZoneId timeZoneNY = ZoneId.of("America/New_York");
// 获得带有时区的有效时间
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, timeZoneNY);

使用 DateTimeFormatter 格式化时间

yyyy-MM-dd HH:mm:ss z z 是带时区的,yyyy-MM-dd HH:mm:ss 是不带时区的

// 格式化日期时间  
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");  
String formattedDateTime = zonedDateTime.format(dateTimeFormatter);

完整代码

// 获取当前时间  
LocalDateTime localDateTime = LocalDateTime.now();  
// 初始化一个时区  
ZoneId timeZoneSH = ZoneId.of("Asia/Shanghai");  
ZoneId timeZoneNY = ZoneId.of("America/New_York");  
// 解析格式化后的日期时间,并添加时区信息  
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, timeZoneNY);  
// 格式化日期时间  
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");  
String formattedDateTime = zonedDateTime.format(dateTimeFormatter);  
// 输出结果  
System.out.println(formattedDateTime);

3. 日期时间的计算

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.plusDays(30));

计算两个日期差时可能会踩坑:Java 8 中有一个专门的类 Period 定义了日期间隔,通过 Period.between 得到了两个 LocalDate 的差,返回的是两个日期差几年零几月零几天。如果希望得知两个日期之间差几天,直接调用 Period 的 getDays() 方法得到的只是最后的“零几天”,而不是算总的间隔天数。

比如,计算 2019 年 12 月 12 日和 2019 年 10 月 1 日的日期间隔,很明显日期差是 2 个月零 11 天,但获取 getDays 方法得到的结果只是 11 天,而不是 72 天,可以使用 ChronoUnit.DAYS.between 解决这个问题:

System.out.println("//计算日期差");
LocalDate today = LocalDate.of(2019, 12, 12);
LocalDate specifyDate = LocalDate.of(2019, 10, 1);
System.out.println(Period.between(specifyDate, today).getDays());
System.out.println(Period.between(specifyDate, today));
System.out.println(ChronoUnit.DAYS.between(specifyDate, today));

本站总访问量