时间日期处理

第三代日期类(JDK8)

LocalDateTime

LocalDateTime:日期时间 / 年月日时分秒(LocalDate:日期 / 年月日、LocalTime:时间 / 时分秒)

  • 表示本地日期和时间。

  • 默认严格按照 ISO 8601 规定的日期和时间格式进行打印。

  • ISO 8601 规定的日期和时间分隔符是T。标准格式如下:

    • 日期:yyyy-MM-dd
    • 时间:HH:mm:ss
    • 带毫秒的时间:HH:mm:ss.SSS
    • 日期和时间:yyyy-MM-dd’T’HH:mm:ss
    • 带毫秒的日期和时间:yyyy-MM-dd’T’HH:mm:ss.SSS
  • 自定义格式打印需要使用DateTimeFormatter类。

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// now(): 获取当前时间
LocalDateTime.now()

// of(): 指定的日期和时间创建 LocalDateTime 实例
LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);

// plusXxx(): 日期时间的加减(方法的参数都是long型,返回值都是LocalDateTime)
LocalDateTime plusYearsResult = localDateTime.plusYears(2L); // 2025-10-30T09:51:26.062

// 时间 转 字符串
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateTime = LocalDateTime.now().format(formatter); // 2023-10-30 09:48:01
// 字符串 转 时间
String dateTimeStr = "2023-10-30 09:48:11";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, dtf); // 2023-10-30T09:48:11

// toInstant 指定时区生成毫秒数(这里指定国际时间 UTC 0,实际上就是 System.currentTimeMillis() + 时区偏移毫秒数)
LocalDateTime.now().toInstant();
// toEpochSecond(): 获取秒数 .toEpochMilli(): 获取毫秒数
Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")); // 1698630422

格式日期类(DateTimeFormatter)

格式化类型,取代了SimpleDateFormat

示例一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//-----------------------------------------------创建对象---------------------------------------------//
// now()方法:获取本地日期时间,总是以当前默认时区返回
LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now() LocalTime.now()
System.out.println(ldt); //2022-07-05T19:22:21.955

/// of()方法:指定日期和时间创建对象:
LocalDate d2 = LocalDate.of(2017, 1, 29); // 2017-1-29
LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
LocalDateTime dt2 = LocalDateTime.of(2017, 1, 29, 15, 16, 17);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
System.out.println(dt2); // 2017-01-29T15:16:17

// 获取指定属性(年月日时分秒)
System.out.println("年:" + ldt.getYear()); // 年:2022
System.out.println("月:" + ldt.getMonthValue()); // 月:7 (getMonth():JULY)

// 使用DateTimeFormatter格式化日期时间
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒"); // 创建DateTimeFormatter对象
String format = dtf.format(ldt);
System.out.println("格式化后:" + format); //格式化后:2022年07月05日 19小时22分钟21秒

// plusDays(n):获取n天后的日期时间(其他属性同理)
LocalDateTime localDateTime = ldt.plusDays(180); // 180天后,是什么时候
System.out.println("180天后的现在 = " + dtf.format(localDateTime)); // 180天后的现在 = 2023年01月01日 19小时19分钟14秒

//minusMinutes(n):获取n分钟前的日期时间
LocalDateTime localDateTime1 = ldt.minusMinutes(3456); // 3456分钟前是什么时候
System.out.println("3456分钟前是 = " + dtf.format(localDateTime1)); // 3456分钟前是 = 2022年07月03日 09小时46分钟21秒

时区(ZoneDateTime)

LocalDateTime总是表示本地日期和时间,要表示一个带时区的日期和时间,我们就需要ZonedDateTime

可以简单地把ZonedDateTime理解成LocalDateTimeZoneIdZoneIdjava.time引入的新的时区类。

创建ZoneDateTime对象的两种方式:

1
2
3
4
5
6
7
8
9
10
11
12
//1. now()方法创建ZonedDateTime对象
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
System.out.println(zbj); // 2022-07-05T20:06:24.139+08:00[Asia/Shanghai]
System.out.println(zny); // 2022-07-05T08:06:24.141-04:00[America/New_York]

//2. 通过LocalDateTime -> ZonedDateTime (给一个LocalDateTime对象附加一个ZoneId)
LocalDateTime ldt = LocalDateTime.of(2019, 9, 15, 15, 16, 17);
ZonedDateTime zbj2 = ldt.atZone(ZoneId.systemDefault());
ZonedDateTime zny2 = ldt.atZone(ZoneId.of("America/New_York"));
System.out.println(zbj2); //2019-09-15T15:16:17+08:00[Asia/Shanghai]
System.out.println(zny2); //2019-09-15T15:16:17-04:00[America/New_York]

时间、日期差(Duration 和 Period)

Duration表示两个时刻之间的时间间隔。Period表示两个日期之间的天数:

1
2
3
4
5
6
7
LocalDateTime start = LocalDateTime.of(2020, 8, 1, 19, 48, 0);
LocalDateTime end = LocalDateTime.of(2022, 7, 5, 19, 48, 30);
Duration d = Duration.between(start, end);
System.out.println(d); // PT16872H30S

Period p = LocalDate.of(2020, 8, 1).until(LocalDate.of(2022, 7, 5));
System.out.println(p); // P1Y11M4D

代码解释:两个LocalDateTime之间的差值使用Duration表示,PT16872H30S表示16872小时30秒。而两个LocalDate之间的差值用Period表示,P1Y11M4D表示1年11个月4天。

DurationPeriod的表示方法也符合ISO 8601的格式,它以P...T...的形式表示。

时间戳(Instant)

Java提供的System.currentTimeMillis()返回的就是以毫秒(long类型)表示的当前时间戳。

这个当前时间戳在java.time中以Instant类型表示,我们用Instant.now()获取当前时间戳,效果和System.currentTimeMillis()类似

实际上,Instant内部只有两个核心字段:

1
2
private final long seconds;  // 以秒为单位的时间戳
private final int nanos; //更精确的纳秒精度

Instant是时间戳,那么,给它附加上一个时区,就可以创建出ZonedDateTime:

1
2
3
Instant ins = Instant.ofEpochSecond(1568568760);  // 以指定时间戳创建Instant:
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
System.out.println(zdt); // 2019-09-16T01:32:40+08:00[Asia/Shanghai]

可见,对于某一个时间戳,给它关联上指定的ZoneId,就得到了ZonedDateTime,继而可以获得了对应时区的LocalDateTime

LocalDateTimeZoneIdInstantZonedDateTimelong都可以互相转换:

LocalDateTime&ZoneId&ZonedDateTime

第二代日期类

Calendar类(JDK1.1引入)

1)是一个抽象类,并且构造器是private

2)可以通过getInstance()方法来获取实例

3)Calendar没有提供对应的格式化的类,需要开发者自己组合显示

1
2
3
4
5
6
7
8
9
10
11
12
13
Calendar c = Calendar.getInstance();
System.out.println(c); //java.util.GregorianCalendar[time=1657010957876,areFieldsSet=true,...]
//
System.out.println("年:" + c.get(Calendar.YEAR));
System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
System.out.println("时:" + c.get(Calendar.HOUR));
System.out.println("分:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));

//Calendar没有专门的格式化方法,需要开发者自己组合显式
System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH)
+ " " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND));

前两代的不足

Date类的大多数方法已经在引入Calendar类后被弃用。

(1)可变性:像日期和时间这样的类应该是不可变的;

(2)偏移性:Date中年份是从1900开始,而月份都从0开始;

(3)格式化:格式化类只支持Date,不支持Calendar

(4)其他:线程不安全、不能处理闰秒等(每隔2天,多出1s)

第一代日期类

Date类(JDK1.0引入)

精确到毫秒,代表特定的瞬间

SimpleDateFormat类:格式化和解析日期的类

允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化

示例

获取当前系统时间

1
2
3
4
5
6
7
8
Date date1 = new Date();  //获取当前系统时间
System.out.println("当前日期 = " + date1); //当前日期 = Tue Jul 05 15:38:28 CST 2022

//创建 SimpleDateFormat对象,可以指定格式
//格式遵循规定
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format = sdf.format(date1);
System.out.println("当前日期 = " + format); //当前日期 = 2022年07月05日 03:49:28 星期二

把一个格式化的 String 转成对应的 Date

在把String -> Date时,使用的sdf格式需要和给定的String格式一致,否则会抛出转换异常

1
2
3
4
5
String s = "2022年07月05日 03:49:28 星期二";
Date parse = sdf.parse(s);
//得到 Date 仍然按国外形式输出,需要转换
System.out.println("parse = " + parse); //parse = Tue Jul 05 03:49:28 CST 2022
System.out.println("format_parse = " + sdf.format(parse)); //format_parse = 2022年07月05日 03:49:28 星期二

通过指定毫秒数得到日期时间

1
2
Date date2 = new Date(999999999);
System.out.println(date2); //Mon Jan 12 21:46:39 CST 1970

其他

ISO 8601规定的日期和时间分隔符是T。标准格式如下:

  • 日期:yyyy-MM-dd
  • 时间:HH:mm:ss
  • 带毫秒的时间:HH:mm:ss.SSS
  • 日期和时间:yyyy-MM-dd’T’HH:mm:ss
  • 带毫秒的日期和时间:yyyy-MM-dd’T’HH:mm:ss.SSS

时间日期处理
https://blog-21n.pages.dev/2022/07/05/Java-时间日期处理/
作者
Neo
发布于
2022年7月5日
许可协议