【Java基础03】时间类

【Java基础系列】
【Java基础01】基础概述
【Java基础02】常用类
【Java基础03】时间类
【Java基础04】异常
【Java基础05】枚举类
【Java基础06】泛型
【Java基础07】注解
【Java基础08】反射
【Java基础09】代理
【Java基础10】IO流

时间类

一、Java7时间类

类名称时间格式
java.util.Date(父类)年月日时分秒
java.sql.Date(子类)年月日
java.sql.Time(子类)时分秒
java.sql.Timestamp(子类)年月日时分秒毫秒
java.util.Calendar(日历类)年月日时分秒
java.text.DateFormat(格式化类)时间格式转换类

简单示例(如下显示输出时间08:00:00,是由于CST时区问题,东八区+8小时)

@Test
void Java7TimeTest() {
    java.util.Date utilDate = new java.util.Date();
    java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
    java.sql.Time sqlTime = new java.sql.Time(utilDate.getTime());
    java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(utilDate.getTime());
    log.info("utilDate:{}", utilDate);
    log.info("sqlDate:{}", sqlDate);
    log.info("sqlTime:{}", sqlTime);
    log.info("sqlTimestamp:{}", sqlTimestamp);

    // utilDate:Thu May 19 14:39:09 CST 2022
    // sqlDate:2022-05-19
    // sqlTime:14:39:09
    // sqlTimestamp:2022-05-19 14:39:09.656

    java.util.Calendar calendar = java.util.Calendar.getInstance();
    log.info("calendar:{}", calendar.getTime());

    // calendar:Thu May 19 14:39:09 CST 2022

    log.info("new utilDate:{}", new Date(0L));
    log.info("new sqlDate:{}", new Date(0L));
    log.info("new sqlTime:{}", new Time(0L));
    log.info("new sqlTimestamp:{}", new java.sql.Timestamp(0L));
    // new utilDate:Thu Jan 01 08:00:00 CST 1970
    //  new sqlDate:Thu Jan 01 08:00:00 CST 1970
    // new sqlTime:08:00:00
    // new sqlTimestamp:1970-01-01 08:00:00.0
}

时间戳与时区的介绍

首先介绍计算机固定起始时间标准与时区:

计算机中时间原点:1970年1月1日 00:00:00(GMT:格林尼治标准时间)
时间换算单位:1秒 = 1000毫秒
UTC :统一的标准时间
GMT :格林尼治标准时间(Greenwich Mean Time)
CST :可视为中国、美国、澳大利亚或古巴的标准时间
美国:Central Standard Time (USA) UT-6:00
澳大利亚:Central Standard Time (Australia) UT+9:30
中国:China Standard Time UT+8:00
古巴:Cuba Standard Time UT-4:00
这个静态方法System.currentTimeMillis()将返回自1970年1月1日至现在的时间,以毫秒为单位返回一个long类型。

long currentTimeMillis = System.currentTimeMillis();
log.info("currentTimeMillis:{}", currentTimeMillis);
// currentTimeMillis:1652943494868

这就是全部需要做的了。返回的long值可以用来初始化java.util.Date, java.sql.Date, java.sql.Timestamp和java.util.GregorianCalendar对象。

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

java.util.Date(父类)

java.util.Date 是 java.sqlDate,Time,Timestamp 的父类,Java中的时间使用标准类库的 java.util.Date,其表示特定的时间,精确到毫秒。是用距离一个固定时间点的毫秒数(可正可负,long类型)表达一个特定的时间点。从 JDK 1.1 开始,应该使用 java.util.Calendar 类实现日期和时间字段之间转换,使用 java.text.DateFormat 类来格式化和分析日期字符串。因为 Date 的设计具有"千年虫"以及"时区"的问题,所以 Date 中的大部分方法已经不建议使用了,它们都被 java.util.Calendar 类所取代
java.util.Date 中大部分的方法都已经被标记过期了,现在常用的大致如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZG5FYQC-1677485488673)(images/时间类/image-20220519150331345.png)]

java.sql.Date(子类)

包含年月日,时分秒都被设置为 0,之所以这样设计是为了适应 SQL 中的 DATE 类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkKXquqB-1677485488674)(images/时间类/image-20220519150603924.png)]

 @Test
 void sqlDateTest() {
 // 返回的均为本地时间
 System.out.println("【java.sql.Date】: " + new java.sql.Date(System.currentTimeMillis()));
 System.out.println("【java.sql.Date】: " + new java.sql.Date(new java.util.Date().getTime()));
 System.out.println("【java.sql.Date(0)】: " + new java.sql.Date(0));
 // 时间格式转换
 System.out.println("【2020-02-10】: " + java.sql.Date.valueOf("2020-02-10").getTime());
 // 错误的时间格式
 System.out.println("【20200210】: " + java.sql.Date.valueOf("20200210"));

//【java.sql.Date】: 2022-05-19
//【java.sql.Date】: 2022-05-19
//【java.sql.Date(0)】: 1970-01-01
//【2020-02-10】: 1581264000000
//
// java.lang.IllegalArgumentException
//  at java.sql.Date.valueOf(Date.java:143)

}

java.sql.Time(子类)

包含时分秒,这个也是为了SQL中的 TIME 类型而出现的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sOkCZAw-1677485488674)(images/时间类/image-20220519151401465.png)]

@Test
    void sqlTimeTest() {
        System.out.println("【java.sql.Time】: " + new java.sql.Time(System.currentTimeMillis()));
        System.out.println("【java.sql.Time】: " + new java.sql.Time(new java.util.Date().getTime()));
        System.out.println("【java.sql.Time(0)】: " + new java.sql.Time(0));
        System.out.println("【20:20:20】: " + java.sql.Time.valueOf("20:20:20").getTime());
        System.out.println("【202020】: " + java.sql.Time.valueOf("202020"));
        
        //【java.sql.Time】: 15:15:43
        //【java.sql.Time】: 15:15:43
        //【java.sql.Time(0)】: 08:00:00
        //【20:20:20】: 44420000
        //
        //java.lang.IllegalArgumentException
        //at java.sql.Time.valueOf(Time.java:110)
    }

java.sql.Timestamp(子类)

包含年月日时分秒毫秒,时间戳,适配于SQL中的 TIMESTAMP 类型而出现的,精确到纳秒级别。
只有 TimeStamp 获取得到的时间才是完整的,Date 获取到的只有日期,Time 获取到的只有时间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T3geB0nE-1677485488674)(images/时间类/image-20220519152230046.png)]

@Test
void  sqlTimestamp() {
/**
* 返回的均为本地时间
*/
System.out.println("【java.sql.Timestamp】: " + new java.sql.Timestamp(System.currentTimeMillis()));
System.out.println("【java.sql.Timestamp】: " + new java.sql.Timestamp(new java.util.Date().getTime()));
System.out.println("【java.sql.Timestamp(0)】: " + new java.sql.Timestamp(0));
System.out.println("【java.sql.Timestamp(System.currentTimeMillis()).toLocalDateTime())】: " + new java.sql.Timestamp(System.currentTimeMillis()).toLocalDateTime());
/**
* 返回一个格林瞬时时间
*/
System.out.println("【java.sql.Timestamp(System.currentTimeMillis()).toInstant())】: " + new java.sql.Timestamp(System.currentTimeMillis()).toInstant());
/**
* 时间格式转换
*/
System.out.println("【2020-02-10 20:20:20.110】: " + java.sql.Timestamp.valueOf("2020-02-10 20:20:20.110").getTime());
System.out.println("【2020-02-10 20:20:20】: " + java.sql.Timestamp.valueOf("2020-02-10 20:20:20").getTime());
System.out.println("【202020 202020】: " + java.sql.Timestamp.valueOf("202020 202020"));

//【java.sql.Timestamp】: 2022-05-19 15:24:20.806
//【java.sql.Timestamp】: 2022-05-19 15:24:20.806
//【java.sql.Timestamp(0)】: 1970-01-01 08:00:00.0
//【java.sql.Timestamp(System.currentTimeMillis()).toLocalDateTime())】: 2022-05-19T15:24:20.806
//【java.sql.Timestamp(System.currentTimeMillis()).toInstant())】: 2022-05-19T07:24:20.808Z
//【2020-02-10 20:20:20.110】: 1581337220110
//【2020-02-10 20:20:20】: 1581337220000
//
//java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
//    at java.sql.Timestamp.valueOf(Timestamp.java:237)

}

为什么有了java.util.Date类还要有java.sql.Date这个类呢?

我们发现除了父类Date在util包下,它的三个子类都是在sql包下的。因为在和数据库打交道时(也就是在执行SQL语句时),我们要用到sql包下的时间类,其他情况下我们通常用util包下的Date来格式化日期或者得到当前时间。为什么和数据库打交道时不能用java.util.Date?因为PreparedStatement有三个设置时间的方法:setDate()、setTime()、setTimeStamp(),这三个方法的第二个参数分别是java.sql.Date、java.sql.Time和java.sql.TimeStamp,并没有提供参数是java.util.Date的方法。除此之外,ResultSet也有三个get()方法与之对应,且返回的时间类型也是sql包下的这三个类

java.text.DateFormat(格式化)

虽然Date可以获取当前的日期时间,但是默认情况下Date类输出的日期时间结构并不能够被国人所习惯,那么就需要对显示的格式进行格式化的处理,为了可以格式化日期,在java.text包中提供有SimpleDateFormat程序类,该类是DateFormat的子类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dA9uaRPF-1677485488675)(images/时间类/image-20220519152750285.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4IGH3a2t-1677485488675)(images/时间类/image-20220519152829525.png)]

@Test
void dateFormatTest() throws ParseException {
    // 1.默认实例化
    SimpleDateFormat sdf1 = new SimpleDateFormat();

    // 1.1 格式化:将Date类型数据转变为字符串
    Date date = new Date();
    // 将新建的日期对象传入默认实例化的SimpleDateFormat对象中,转变为默认格式的字符串 yyyy-MM-dd 上/下午hh:mm
    String format1 = sdf1.format(date);
    // 输出字符串为:22-5-19 下午3:46
    System.out.println(format1);

    // 1.2 解析:将字符串转换为Date类型数据
    String str = "2021-11-15 下午3:41";
    // 将与SimpleDateFormat中默认日期格式相符合的字符串转换为Date类型数据
    Date parse1 = sdf1.parse(str);
    // 输出Date类型数据为:Mon Nov 15 15:41:00 CST 2021
    System.out.println(parse1);

    // 2.带参数的实例化
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // 2.1 将Date类实例对象作为实参传入SimpleDateFormat重载构造器中,转换为自定义参数格式的字符串
    String format2 = sdf2.format(date);
    // 输出字符串为:2022-05-19 15:46:11
    System.out.println(format2);

    // 2.2 将与SimpleDateFormat类的实例对象中自定义参数格式相符和的字符串解析为Date类型数据
    Date parse2 = sdf2.parse("2021-11-15 16:11:27");
    // 输出Date类型数据为:Mon Nov 15 16:11:27 CST 2021
    System.out.println(parse2);

    /**
         * a. 时间格式: “2015-08-28”, 模式: “yyyy-MM-dd”
         */
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = dateFormat.parse("2015-08-28");
    }
    /**
         * b. 时间格式: “2015-08-28 18:28:30”, 模式: “yyyy-MM-dd HH:mm:ss”
         */
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date2 = dateFormat.parse("2015-08-28 18:28:30");
    }
    /**
         * c. 时间格式: “2015-8-28”, 模式: “yyyy-M-d”
         */
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-M-d");
        Date date3 = dateFormat.parse("2015-8-28");
    }
    /**
         * d. 时间格式: “2015-8-28 18:8:30”, 模式: “yyyy-M-d H:m:s”
         */
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-M-d H:m:s");
        Date date4 = dateFormat.parse("2015-8-28 18:8:30");
    }
    /**
         * e. 时间格式: “Aug 28, 2015 6:8:30 PM”, 模式: “MMM d, yyyy h:m:s aa”
         */
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy h:m:s aa", Locale.ENGLISH);
        Date date5 = dateFormat.parse("Aug 28, 2015 6:8:30 PM");
    }
    /**
         * f. 时间格式: “Fri Aug 28 18:08:30 CST 2015”, 模式: “EEE MMM d HH:mm:ss ‘CST’ yyyy”
         */
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss 'CST' yyyy", Locale.ENGLISH);
        Date date6 = dateFormat.parse("Fri Aug 28 18:08:30 CST 2015");
    }
}

SimpleDateFormat 正确使用

/**
* @author  luomengwei
*  方法一
*  这种方法可能会导致短期内创建大量的SimpleDateFormat实例,如解析一个excel表格里的字符串日期。
**/
public static String formatDate1(Date date) {
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf1.format(date);
}
/**
* @author  luomengwei
*  方法二
*  为了避免创建大量的SimpleDateFormat实例,往往会考虑把SimpleDateFormat实例设为静态成员变量,共享SimpleDateFormat对象。
*  这种情况下就得对SimpleDateFormat添加同步
*  缺点:方法的缺点也很明显,就是在高并发的环境下会导致解析被阻塞
**/
private static SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public static String formatDate2(Date date) {
    return sdf2.format(date);
}
/**
* @author  luomengwei
*  方法三
*  要在高并发环境下能有比较好的体验,可以使用ThreadLocal来限制SimpleDateFormat只能在线程内共享,这样就避免了多线程导致的线程安全问题。
**/
private static ThreadLocal<DateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String formatDate3(Date date) {
	return threadLocal.get().format(date);
}

java.util.Calendar(日历类)

java.util.Calendar 类是一个抽象基类,主要用于日期之间的各种计算。主要完成日期字段之间相互操作的功能,可通过setTime方法将Date类型变量转换为Calendar类型变量,通过get方法得知此刻是一年中的第几天、一月中的第几天等,或者通过set方法设置此刻时间变为一年中的指定天数等。获取Calendar实例的方法:使用 Calendar.getInstance() 方法或调用它子类 GregorianCalendar 的构造器

Java.util.Calendar 区别与 java.util.Date 的几个地方也需要注意一下 :

1、Calendar增加了毫秒的时间段,通过它可以获取时间点的毫秒值,而java.util.Date只是精确到秒
2、两个类是可以进行相互转换的可以使用Calendar类的 setTime(Date date) 和 getTime() 方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xt1MJ8Gf-1677485488675)(images/时间类/image-20220526095354186.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8byBssgx-1677485488676)(images/时间类/image-20220526095333268.png)]

@Test
void CalendarTest() {
    /**
         * 实例化的两种方式:
         *   方式一:创建其子类的实例化
         *   方式二:通过调用其静态方法getInstance实例化
         */
    Calendar calendar = new GregorianCalendar();
    Calendar calendar2 = Calendar.getInstance();

    /**
         * get()方法的形参是Calendar类中的静态整型常量
         */
    // 获得年份:2022
    System.out.println("现在是:" + calendar.get(Calendar.YEAR) + "年");
    // 获得月份:2
    System.out.println("现在是:" + (calendar.get(Calendar.MONTH)+1) + "月");
    //获得日期(本月的第几天):13
    System.out.println("现在是:" + calendar.get(Calendar.DATE) + "号");
    System.out.println("现在是:" + calendar.get(Calendar.DAY_OF_MONTH) + "号");
    // 获得这是今年的第几天:44
    System.out.println("现在是今年第" + calendar.get(Calendar.DAY_OF_YEAR) + "天");
    // 获得今天周几:0(星期天天)
    System.out.println("现在是星期:" + (calendar.get(Calendar.DAY_OF_WEEK)-1) );
    // 获得今天是这个月的第几周:2
    System.out.println("现在是第:" + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH) + "周" );
    // 12小时制的时间:9
    System.out.println("现在是:" + calendar.get(Calendar.HOUR) + "点");
    // 24小时制的时间:21
    System.out.println("现在是:" + calendar.get(Calendar.HOUR_OF_DAY) + "点");
    // 分钟数:30
    System.out.println("现在是:" + calendar.get(Calendar.MINUTE) + "分");
    // 秒数:21
    System.out.println("现在是:" + calendar.get(Calendar.SECOND) + "秒");
    // 毫秒:338
    System.out.println("现在是:" + calendar.get(Calendar.MILLISECOND) + "毫秒");

    /**
         * set()方法中的第二个参数可以把Calendar类中的静态常量值改变
         */
    // 设置当前时间为本年度的第10天
    calendar.set(Calendar.DAY_OF_YEAR,10);
    // 再次调用get()方法后返回的是更改后的值
    System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 10

    /**
         * add()方法是在set方法的基础上进行增减,如果要减,就把数字写为负数
         */
    // 给当前日期上加上本年度的3天(现在是本年度的第10天)
    calendar.add(Calendar.DAY_OF_YEAR,3);
    System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 13

    /**
         * getTime()方法实现 calendar 类 -> Date类
         */
    // 得到的Date类型变量是Calendar对象经过修改后的时间
    java.util.Date time = calendar.getTime();
    System.out.println(time); // Thu Jan 13 21:12:50 CST 2022

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    String format = simpleDateFormat.format(time);
    System.out.println(format); // 2022-01-13 09:53:14

    /**
         * setTime()方法实现 Date类 -> Calendar 类
         */
    // 通过Calendar实例对象instance调用setTime将Date类型变量转变为Calendar类型变量
    calendar.setTime(new java.util.Date());
    // 通过该Calendar实例对象调用get方法得知传入Date类型变量是一年中的第几天等信息
    System.out.println(calendar.get(Calendar.DAY_OF_MONTH)); // 13
}

java.util.TimeZone(时区)

Java中的java.util.TimeZone类用于表达时区(又称:时区偏移量),Date本身没有时区概念,当需要使用SimpleDateFormat解析与格式化时间,或者使用Calendar对时间需要计算的时候,才会涉及时区的概念。

Date类本身没有时区概念
查看源码可以知道 Date 对象中存储的是一个 long 型变量,这个变量的值为自 1997-01-01 00:00:00(GMT) 至 Date 对象记录时刻所经过的毫秒数,可以通过getTime()方法,获取这个变量值,且这个变量值和时区没有关系,全球任意地点同时执行 new Date().getTime() 获取到的值相同。
格式化Date对象成字符串

不管是调用Date对象的toString方法,还是使用SimpleDateFormat的format方法去格式化Date对象,或者使用parse解析字符串成Date对象都会涉及到时区,也就是说Date对象没有时区概念,但是格式化Date对象,或者解析字符串成Date对象时,是有时区概念的。

@Test
void TimeZoneDateToStringTest() {
    /**
         * 格式化Date对象成字符串, 涉及时区
         */
    Date date = new Date();
    // 默认是系统时区
    System.out.println(date); 
    // 修改时区
    TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
    System.out.println(date);

    // 默认是系统时区
    SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println(dateFormat1.format(date));
    // 设置时区
    dateFormat1.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
    System.out.println(dateFormat1.format(date));

    //Mon May 30 10:09:06 CST 2022
    //Mon May 30 02:09:06 GMT 2022
    //2022-05-30 02:09:06
    //2022-05-30 03:09:06
}

解析字符串成Date对象

@Test
void TimeZoneStringToDateTest() throws ParseException {
    String data = "2022-05-30 03:09:06";
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date parse = simpleDateFormat.parse(data);
    System.out.println(simpleDateFormat.format(parse) + "、" + parse.getTime());
    simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+5:00"));
    Date parse2 = simpleDateFormat.parse(data);
    System.out.println(simpleDateFormat.format(parse2) + "、" + parse2.getTime());

}

Date or Calendar 面临的问题

JDK1.0中就包含java.util.Date类,但是它的大多数方法已经在JDK1.1引入Calendar类后就弃用了。但是Calendar类也面临如下问题:

  1. 可变性:日期和时间这样的类应该是不可变的
  2. 偏移性:Date类中的年份是从1900年开始的,而月份是从0开始的
  3. 格式化:格式化只对Date有用,Calendar则不行
  4. 安全性:它们都是线程不安全的,不能处理闰秒问题
  5. 设计差:Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
  6. 时区处理麻烦:日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题
  7. 为了解决上述问题,Java8 吸收了第三方Joda - Time的精华,创建了新的 java.time

二、Java8时间类

java.time 先导篇

Java8 新日期时间API特性:

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

Java8 新日期时间API包结构:

  • java.time:父级基础包,常用的时间相关类都在这里,如LocalDate\LocalDateTime\Instant等
  • java.time.chrono:日历系统包,日历相关的接口(类似Calendar)也包括提供对其他日历系统的API
  • java.time.format:格式化和解析包,主要类是DateTimeFormatter
  • java.time.temporal:扩展功能包,提供细粒度的时间控制field、unit,如weeks、months、month-of-year等
  • java.time.zone:时区包,时区规则、本地时区等

Java8 新日期时间API类:

1、日期和时间、时钟

  • Instant:时间戳(相对于1970年1月1日的偏移量)默认是0时区,所以需要+8小时才是北京时间,与Date类不同的是其精确到了纳秒级别

  • LocalDate:只包含日期,不包含时区,比如:2016-10-20

  • LocalTime:只包含时间,不包含时区,比如:23:12:10

  • LocalDateTime:包含日期和时间,内部维护着一对LocalDate与LocalTime实例,同时也不包含时区,比如:2016-10-20 23:14:21

  • Clock:时钟,获取其他地区时钟。比如获取目前美国纽约的时间。该类用于获取指定时区的当前日期、时间。该类可取代System类的currentTimeMillis()方法,而且提供了更多方法来获取当前日期、时间。该类提供了大量静态方法来获取Clock对象。

2、期限/时间段:

  • Duration:用于计算两个“时间”间隔

  • Period:用于计算两个“日期”间隔

3、时区与偏移量:

  • ZoneId:该类代表一个时区

  • ZoneOffset:时区偏移量,比如:+8:00

  • ZonedDateTime:带时区的时间

4、附加的类型:

  • Year 存储年,如“2020”

  • YearMonth 存储年和月,如“2020-10”,可用于信用卡上的到期

  • Month:定义了一月到十二月的枚举值。存储一个月。如“十一月”

  • MonthDay:存储月和日,如“12-14”,可用于存储生日

  • DayOfWeek:存储一周中的一天,如“Tuesday”

  • OffsetTime:存储与UTC没有日期的时间和偏移量

  • OffsetDateTime:存储与UTC的日期时间和偏移量

5、几个特殊的接口:

  • Temporal:日期-时间获取值的字段

  • TemporalAdjuster:Temporal对象转换,实现自定义

  • ChronoLocalDate:日历系统接口(下面是它的四个子类)

6、Java中使用的历法是ISO 8601日历系统,它是世界民用历法(也称为公历)平年有365天,闰年是366天。此外Java8还提供了4套其他历法:

  • ThaiBuddhistDate:泰国佛教历
  • MinguoDate:中华民国历
  • JapaneseDate:日本历
  • HijrahDate:伊斯兰历

java.time.Instant

Instant 类在Java日期与时间功能中,表示了时间线上一个确切的点(时间戳),定义为距离初始时间的时间差(初始时间为GMT 1970年1月1日00:00),对应Java8之前使用的java.util.Date类,经测量一天有86400秒,从初始时间开始不断向前移动。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qCo3y9aB-1677485488676)(images/时间类/image-20220530104425499.png)]

@Test
void InstantTest() {
    /**
         * 实例方式一:通过静态方法now(),获得UTC(本初子午线)的此刻瞬时时间的实例对象
         * 输出内容(默认时间比北京时间相差8小时):2022-02-16T07:22:12.266171900Z(Z表示本初子午线)
         * 注:不建议使用Instant查看当前时间点
         */
    Instant instant1 = Instant.now();
    System.out.println(instant1); // 2022-05-30T02:46:55.842Z

    /**
         * 实例化方式二:通过给定毫秒数或秒数,获取Instant实例
         */
    System.out.println(Instant.ofEpochMilli(Clock.systemDefaultZone().millis())); // 2022-05-30T02:46:55.852Z
    System.out.println(Instant.ofEpochMilli(Clock.systemUTC().millis())); // 2022-05-30T02:46:55.852Z
    System.out.println(Instant.ofEpochMilli(System.currentTimeMillis())); // 2022-05-30T02:46:55.852Z
    System.out.println(Instant.ofEpochSecond(System.currentTimeMillis() / 1000)); // 2022-05-30T02:46:55Z

    /**
         * 实例化方式三:将字符串转换成Instant
         */
    System.out.println(Instant.parse("2022-02-16T07:22:12.266Z")); // 2022-02-16T07:22:12.266Z

    /**
         * long getEpochSecond():获取当前时间戳的秒数:
         * long toEpochMilli():获取当前时间戳的毫秒:
         * int getNano():获取当前时间点(抛开整数秒不算)的纳秒数
         * 如: 12345.12秒,抛开整数秒不算,则为0.12秒,那么instant.getNano()的结果为 0.12 * 1000_000_000 = 120_000_000
         */
    System.out.println("秒数 -> " + Instant.now().getEpochSecond()); // 秒数 -> 1653878815
    System.out.println("毫秒数 -> " + Instant.now().toEpochMilli()); // 毫秒数 -> 1653878815855
    System.out.println("纳秒数 -> " + Instant.now().getNano());      // 纳秒数 -> 855000000

    /**
         * Instant 与 时间偏移量 的相互转换, 注:从1970-01-01 00:00:00开始计算偏移
         */
    System.out.println(Instant.now()); // 2022-05-30T02:46:55.855Z
    // 对时间进行时区偏移修正,北京时间应+8,输出内容可以发现->(Z变为+8:00)
    System.out.println(Instant.now().atOffset(ZoneOffset.ofHours(8))); // 2022-05-30T10:46:55.855+08:00
    // 设置时区后,显示时间为北京时间了->(可以发现后面带上了时区)
    System.out.println(Instant.now().atZone(ZoneId.systemDefault())); // 2022-05-30T10:46:55.856+08:00[Asia/Shanghai]

    /**
         * Instant的时间加、减:
         * 由于北京时间比UTC时间晚8小时,所以我们需要得出北京的瞬时时间,需要加8小时
         */
    Instant instant3 = Instant.now().plus(8, ChronoUnit.HOURS);
    // 原(北京瞬时)instant -> 2022-05-30T10:46:55.856Z
    System.out.println("原(北京瞬时)instant -> " + instant3);
    Instant plusRes = instant3.plus(1, ChronoUnit.HOURS); // + 1 小时
    // 原(北京瞬时)instant + 1小时,结果是 -> 2022-05-30T11:46:55.856Z
    System.out.println("原(北京瞬时)instant + 1小时,结果是 -> " + plusRes);
    Instant minusRes = instant3.minus(2, ChronoUnit.HOURS); // - 2 小时
    // 原(北京瞬时)instant - 2小时,结果是 -> 2022-05-30T08:46:55.856Z
    System.out.println("原(北京瞬时)instant - 2小时,结果是 -> " + minusRes);

    /**
         * 判断两个Instant之间谁早谁晚
         */
    // 将Clock转换成Instant
    Instant instantOne = Instant.now(Clock.systemDefaultZone());
    // 对时间进行时区偏移修正,北京时间应+8
    Instant instantTwo = instantOne.plus(8, ChronoUnit.HOURS);
    boolean isAfterResult = instantOne.isAfter(instantTwo);
    // 瞬时时间点instantOne晚于instantTwo ? --- false
    System.out.println("瞬时时间点instantOne晚于instantTwo ? --- " + isAfterResult);
    // 瞬时时间点instantOne早于instantTwo ? --- true
    boolean isBeforeResult = instantOne.isBefore(instantTwo);
    System.out.println("瞬时时间点instantOne早于instantTwo ? --- " + isBeforeResult);
}

java.time.Clock

Clock 是时钟系统,用于查找当前时刻。你可以用它来获取某个时区下当前的日期或者时间。可以用 Clock 来替代旧的 System.currentTimeInMillis() 与 TimeZone.getDefault() 方法。

@Test
void ClockTest() {
    // 系统时区默认时间, 通过clock.instant()方法获取当前时刻
    Clock clock = Clock.systemDefaultZone();
    System.out.println(clock); // SystemClock[Asia/Shanghai]
    System.out.println(clock.getZone()); // Asia/Shanghai
    System.out.println("当前时刻为:" + clock.instant()); // 当前时刻为:2022-05-30T02:53:17.426Z

    // 世界协调时UTC
    Clock clockUTC = Clock.systemUTC();
    System.out.println(clockUTC); // SystemClock[Z]
    System.out.println(clockUTC.getZone()); // Z
    System.out.println("当前时刻为:" + clockUTC.instant()); // 当前时刻为:2022-05-30T02:53:17.426Z

    // 获取Clock对应的毫秒数,与System.currentTimeMillis()输出相同
    System.out.println(Clock.systemDefaultZone().millis()); // 1645172712973
    System.out.println(Clock.systemUTC().millis());         // 1645172712973
    System.out.println(System.currentTimeMillis());         // 1645172712973

    // 在clock基础上增加6000秒,返回新的Clock
    Clock clockSet = Clock.offset(clockUTC, Duration.ofSeconds(6000));
    System.out.println(clockSet); // OffsetClock[SystemClock[Z],PT1H40M]
    System.out.println(clockSet.getZone()); // Z
    System.out.println("当前时刻为:" + clockSet.instant()); // 当前时刻为:2022-05-30T02:53:17.430Z

    // 纽约时间
    Clock clockNewYork = Clock.system(ZoneId.of("America/New_York"));
    // Current DateTime with NewYork clock: 2022-05-29T22:53:17.431
    System.out.println("Current DateTime with NewYork clock: " + LocalDateTime.now(clockNewYork));
    System.out.println(clockNewYork.millis()); // 1653879197432

    // 返回具有不同时区的此时钟的副本, withZone(ZoneId zoneId)
    Clock systemDefaultZone = Clock.systemDefaultZone();
    Clock withZone = systemDefaultZone.withZone(ZoneId.of("America/New_York"));
    System.out.println(systemDefaultZone); // SystemClock[Asia/Shanghai]
    System.out.println(withZone); // SystemClock[America/New_York]

    /**
         * Clock tick(Clock baseClock, Duration tickDuration):此方法获得一个时钟,该时钟返回从指定时钟被截断到指定持续时间的最接近值的瞬间
         * Clock tickMinutes(ZoneId zone):此方法获得一个时钟,该时钟使用最佳的可用系统时钟返回整分钟的当前即时滴答
         * Clock tickSeconds(ZoneId zone)	此方法获得一个时钟,该时钟使用最佳可用系统时钟返回整秒的当前即时滴答。
         */
    Clock clock1 = Clock.systemDefaultZone();
    Clock clock2 = Clock.tick(clock1, Duration.ofDays(1));
    System.out.println("Clock1 : " + clock1.instant()); // Clock1 : 2022-05-30T02:53:17.432Z
    System.out.println("Clock2 : " + clock2.instant()); // Clock2 : 2022-05-30T00:00:00Z

    Clock clock3 = Clock.systemDefaultZone();
    Clock clock4 = Clock.tickMinutes(ZoneId.systemDefault());
    System.out.println("Clock3 : " + clock3.instant()); // Clock3 : 2022-05-30T02:53:17.432Z
    System.out.println("Clock4 : " + clock4.instant()); // Clock4 : 2022-05-30T02:53:00Z

    Clock clock5 = Clock.systemDefaultZone();
    Clock clock6 = Clock.tickSeconds(ZoneId.systemDefault());
    System.out.println("Clock5 : " + clock5.instant()); // Clock5 : 2022-05-30T02:53:17.432Z
    System.out.println("Clock6 : " + clock6.instant()); // Clock6 : 2022-05-30T02:53:17Z
}

java.time.LocalDate

LocalDate 用于表示 “本地日期” 无 “时间”,且不承载时区信息。可以对时间进行加减等操作并返回新副本,是不可变类且线程安全的。
默认格式:yyyy-MM-dd。它经常被用于展示year-month-day、day-of-year、day-of-week、week-of-year等格式的信息。

@Test
void LocalDateTest() {
/**
* 常用的两种实例化方式:
*   1.通过静态方法, 获取系统的当前时间:LocalDate.now()
*   2.通过静态方法, 获取自定义指定时间:LocalDate.of(2022, 05, 30)
*/
LocalDate today = LocalDate.now(); // 获取当前日期(年/月/日) 2020-6-14 周天
LocalDate of = LocalDate.of(2022, 05, 30);

/**
* 常用的getXxx()系列操作,获得日期:
*   int getYear() 获取当前⽇期的年份
*   Month getMonth() 获取当前⽇期的⽉份对象(返回一个 Month 枚举值)
*   int getMonthValue() 获取当前⽇期是第⼏⽉(1-12)
*   int getDayOfMonth() 表示该对象表示的⽇期是这个⽉第⼏天(1-31)
*   DayOfWeek getDayOfWeek() 表示该对象表示的⽇期是星期⼏(返回一个 DayOfWeek枚举值)
*   int getDayOfYear() 表示该对象表示的⽇期是今年第⼏天(1-366)
*/
System.out.println("今天的⽇期:" + today); // 今天的⽇期:2022-05-30
System.out.println("指定的⽇期:" + of); // 指定的⽇期:2022-05-30
System.out.println("现在是哪年:" + today.getYear()); // 现在是哪年:2022
System.out.println("现在是哪⽉(英文):" + today.getMonth()); // 现在是哪⽉(英文):MAY
System.out.println("现在是哪⽉(数字):" + today.getMonthValue()); // 现在是哪⽉(数字):5
System.out.println("现在是⼏号:" + today.getDayOfMonth()); // 现在是⼏号:30
System.out.println("现在是周⼏:" + today.getDayOfWeek()); // 现在是周⼏:MONDAY
System.out.println("现在是今年的第几天:" + today.getDayOfYear()); // 现在是今年的第几天:150

/**
* 常用的setXxx()系列操作,设置日期:
*   LocalDate withYear(int year) 修改当前对象的年份
*   LocalDate withMonth(int month) 修改当前对象的⽉份
*   LocalDate withDayOfMonth(int dayOfMonth) 修改当前对象在当⽉的⽇期
*   LocalDate withDayOfYear(int dayOfYear) 修改当前对象在当年的⽇期
*/
LocalDate withLocalDate = LocalDate.of(2022, 01, 01);
System.out.println("常用的setXxx()系列操作:" + withLocalDate); // 此刻时间:2022-01-01(用作对比参考)
System.out.println(withLocalDate.withYear(2030));// 2030-01-01
System.out.println(withLocalDate.withMonth(2)); // 2022-02-01
System.out.println(withLocalDate.withDayOfMonth(8)); // 2022-01-08
System.out.println(withLocalDate.withDayOfYear(10)); // 2022-01-10

/**
* 常用的plusXxx()系列操作,增加时间的方法:
*   LocalDate plusYears(long yearsToAdd) 增加指定年份数
*   LocalDate plusMonths(long monthsToAdd) 增加指定⽉份数
*   LocalDate plusDays(long daysToAdd) 增加指定天数
*   LocalDate plusWeeks(long weeksToAdd) 增加指定周数
*/
LocalDate plusLocalDate = LocalDate.of(2022, 01, 01);
System.out.println("常用的plusXxx()系列操作:" + plusLocalDate); // 此刻时间:2022-01-01(用作对比参考)
System.out.println(plusLocalDate.plusYears(1)); // 2023-01-01
System.out.println(plusLocalDate.plusMonths(1)); // 2022-02-01
System.out.println(plusLocalDate.plusWeeks(1)); // 2022-01-08
System.out.println(plusLocalDate.plusDays(6)); // 2022-01-07

/**
* 常用的minusXxx()系列操作,减少时间的方法:
*   LocalDate minusYears(long yearsToSubtract) 减去指定年数
*   LocalDate minusMonths(long monthsToSubtract) 减去注定⽉数
*   LocalDate minusDays(long daysToSubtract) 减去指定天数
*   LocalDate minusWeeks(long weeksToSubtract) 减去指定周数
*/
LocalDate minusLocalDate = LocalDate.of(2022, 01, 01);
System.out.println("常用的minusXxx()系列操作:" + minusLocalDate); // 此刻时间:2022-01-01(用作对比参考)
System.out.println(minusLocalDate.minusYears(5)); // 2017-01-01
System.out.println(minusLocalDate.minusMonths(60)); // 2017-01-01
System.out.println(minusLocalDate.minusWeeks(260)); // 2017-01-07
System.out.println(minusLocalDate.minusDays(1826)); // 2017-01-01

/**
* 常用日期对比方法:
*   int compareTo(ChronoLocalDate other) ⽐较当前对象和other对象在时间上的⼤⼩,返回值如果为正,则当前对象时间较晚
*   boolean isBefore(ChronoLocalDate other) ⽐较当前对象⽇期是否在other对象⽇期之前
*   boolean isAfter(ChronoLocalDate other) ⽐较当前对象⽇期是否在other对象⽇期之后
*   boolean isEqual(ChronoLocalDate other) ⽐较两个⽇期对象是否相等
*   boolean isLeapYear() 判断是否是闰年(注意是LocalDate类 和 LocalDateTime类特有的方法)
*/
LocalDate localDateOne = LocalDate.of(2022, 01, 01);
LocalDate localDateTwo = LocalDate.of(2000, 01, 01);
System.out.println("compareTo: " + localDateOne.compareTo(localDateTwo)); // compareTo: 22
System.out.println("isBefore: " + localDateOne.isBefore(localDateTwo)); // isBefore: false
System.out.println("isAfter: " + localDateOne.isAfter(localDateTwo)); // isAfter: true
System.out.println("isEqual: " + localDateOne.isEqual(localDateTwo)); // isEqual: false
System.out.println("isLeapYear: " + localDateTwo.isLeapYear()); // isLeapYear: true
}

java.time.LocalTime

LocalTime 用于表示 “本地时间” 无 “日期”。且不承载时区信息。可以对时间进行加减等操作并返回新副本,是线程安全并且不可变类。
默认格式:HH:mm:ss.fff,它经常被用于展示hour-minute-second格式的信息。

@Test
void LocalTimeTest() {
    /**
    * 常用的两种实例化方式:
    *   1.通过静态方法, 获取系统的当前时间:LocalTime.now()
    *   2.通过静态方法, 获取自定义指定时间:LocalTime.of(21, 30, 59, 11001);
    */
    LocalTime today = LocalTime.now();
    LocalTime of = LocalTime.of(21, 30, 59, 11001);
    System.out.println("指定的时间:" + of); // 指定的时间:18:24:31.761102500

    /**
    * 常用的getXxx()系列操作,获得日期:
    *   int getHour() 获取当前时间小时数
    *   int getMinute() 获取当前时间分钟数
    *   int getSecond() 获取当前时间秒值
    *   int getNano() 把获取到的当前时间的秒数换算成纳秒
    */
    System.out.println("今天的时间:" + today); // 今天的时间:18:24:31.761102500
    System.out.println("现在是几时:" + today.getHour()); // 现在是几时:18
    System.out.println("现在是几分:" + today.getMinute()); // 现在是几分:24
    System.out.println("现在是几秒:" + today.getSecond()); // 现在是几秒:31
    System.out.println("现在是⼏纳秒:" + today.getNano()); // 现在是⼏纳秒:761102500

    /**
    * 常用的setXxx()系列操作,设置日期:
    *   LocalTime withHour(int hour) 修改当前对象的小时数
    *   LocalTime withMinute(int minute) 修改当前对象的分钟数
    *   LocalTime withSecond(int second) 修改当前对象在当⽉的秒数
    *   LocalTime withNano(int nanoOfSecond) 修改当前对象在当年的纳秒数
    */
    LocalTime withLocalTime = LocalTime.of(13, 8, 20, 123456789);
    System.out.println("常用的setXxx()系列操作:" + withLocalTime); // 此刻时间:13:08:20.123456789(用作对比参考)
    System.out.println(withLocalTime.withHour(5)); // 05:08:20.123456789
    System.out.println(withLocalTime.withMinute(10)); // 13:10:20.123456789
    System.out.println(withLocalTime.withSecond(8)); // 13:08:08.123456789
    System.out.println(withLocalTime.withNano(100000001)); // 13:08:20.100000001

    /**
    * 常用的plusXxx()系列操作,增加时间的方法:
    *   LocalTime plusHours(long hoursToAdd) 增加指定小时
    *   LocalTime plusMinutes(long minutesToAdd) 增加指定分钟
    *   LocalTime plusSeconds(long secondstoAdd) 增加指定秒
    *   LocalTime plusNanos(long nanosToAdd) 增加指定纳秒
    */
    LocalTime plusLocalTime = LocalTime.of(13, 8, 20, 123456789);
    System.out.println("常用的plusXxx()系列操作:" + plusLocalTime); // 此刻时间:13:08:20.123456789(用作对比参考)
    System.out.println(plusLocalTime.plusHours(1)); // 14:08:20.123456789
    System.out.println(plusLocalTime.plusMinutes(1)); // 13:09:20.123456789
    System.out.println(plusLocalTime.plusSeconds(1)); // 13:08:21.123456789
    System.out.println(plusLocalTime.plusNanos(6)); // 13:08:20.123456795

    /**
    * 常用的minusXxx()系列操作,减少时间的方法:
    *   LocalTime minusHours(long hoursToSubtract) 减去指定年数
    *   LocalTime minusMinutes(long minutesToSubtract) 减去注定⽉数
    *   LocalTime minusSeconds(long secondsToSubtract) 减去指定天数
    *   LocalTime minusNanos(long nanosToSubtract) 减去指定周数
    */
    LocalTime minusLocalTime = LocalTime.of(13, 8, 20, 123456789);
    System.out.println("常用的minusXxx()系列操作:" + minusLocalTime); // 此刻时间:13:08:20.123456789(用作对比参考)
    System.out.println(minusLocalTime.minusHours(1)); // 12:08:20.123456789
    System.out.println(minusLocalTime.minusMinutes(60)); // 12:08:20.123456789
    System.out.println(minusLocalTime.minusSeconds(3600)); // 12:08:20.123456789
    System.out.println(minusLocalTime.minusNanos(9)); // 13:08:20.123456780

    /**
    * 常用日期对比方法:
    *   int compareTo(LocalTime other) ⽐较当前对象和other对象在时间上的⼤⼩,返回值如果为正,则当前对象时间较晚
    *   boolean isBefore(LocalTime other) ⽐较当前对象时间是否在other对象时间之前
    *   boolean isAfter(LocalTime other) ⽐较当前对象时间是否在other对象时间之后
    */
    LocalTime localTimeOne = LocalTime.of(13, 8, 20, 123456789);
    LocalTime localTimeTwo = LocalTime.of(10, 8, 20, 123456789);
    System.out.println("compareTo: " + localTimeOne.compareTo(localTimeTwo)); // compareTo: 1
    System.out.println("isBefore: " + localTimeOne.isBefore(localTimeTwo)); // isBefore: false
    System.out.println("isAfter: " + localTimeOne.isAfter(localTimeTwo)); // isAfter: true
}

java.time.LocalDateTime

LocalDateTime 用于表示 “本地日期与时间”,且不承载时区信息。可以对时间进行加减等操作并返回副本,也是线程安全并且不可变类。这个是最常用的类。
默认的日期格式,yyyy-MM-dd HH:mm:ss.fff,它经常被用于展示hour-minute-second格式的信息。

@Test
void LocalDateTimeTest() {
    /**
    * 常用的两种实例化方式:
    *   1.通过静态方法, 获取系统的当前时间:LocalTime.now()
    *   2.通过静态方法, 获取自定义指定时间:
    *     1.LocalDateTime.of(LocalDate.now(), LocalTime.now())
    *     2.LocalDateTime.of(2020,02,8,21, 30, 59, 11001)
    *   3.通过LocalDateTime转换LocalDate、LocalTime
    */
    LocalDateTime today = LocalDateTime.now();
    LocalDateTime of1 = LocalDateTime.of(LocalDate.now(), LocalTime.now());
    LocalDateTime of2 = LocalDateTime.of(2020,02,8,21, 30, 59, 11001);

    /**
    * 转换的方法:
    *   LocalDate toLocalDate():将LocalDateTime转换为相应的LocalDate对象
    *   LocalTime toLocalTime():将LocalDateTime转换为相应的LocalTime对象
    */
    LocalDate localDate = today.toLocalDate();
    LocalTime localTime = today.toLocalTime();

    /**
    * 常用的getXxx()系列操作,获得日期:
    *   int getYear() 获取当前日期的年份
    *   Month getMonth() 获取当前日期的月份对象(返回一个Month枚举值)
    *   int getMonthValue() 获取当前日期是第几月(1-12)
    *   int getDayOfMonth() 表示该对象表示的⽇期是这个⽉第⼏天(1-31)
    *   DayOfWeek getDayOfWeek() 表示该对象表示的日期是星期几(返回一个DayOfWeek枚举值)
    *   int getDayOfYear() 表示该对象表示的日期是今年第几天(1-366)
    *   int getHour() 获取当前时间小时数
    *   int getMinute() 获取当前时间分钟数
    *   int getSecond() 获取当前时间秒值
    *   int getNano() 把获取到的当前时间的秒数换算成纳秒
    *   int get(TemporalField field) 获取指定字段的日期时间,通过ChronoField枚举类
    */
    System.out.println("今天的时间:" + today); // 今天的时间:2022-02-17T19:13:38.682102600
    System.out.println("现在是哪年:" + today.getYear()); // 现在是哪年:2022
    System.out.println("现在是哪⽉(英文):" + today.getMonth()); // 现在是哪⽉(英文):FEBRUARY
    System.out.println("现在是哪⽉(数字):" + today.getMonthValue()); // 现在是哪⽉(数字):2
    System.out.println("现在是⼏号:" + today.getDayOfMonth()); // 现在是⼏号:17
    System.out.println("现在是周⼏:" + today.getDayOfWeek()); // 现在是周⼏:THURSDAY
    System.out.println("现在是该年的第几天:" + today.getDayOfYear()); // 现在是该年的第几天:48
    System.out.println("现在是几时:" + today.getHour()); // 现在是几时:19
    System.out.println("现在是几分:" + today.getMinute()); // 现在是几分:13
    System.out.println("现在是几秒:" + today.getSecond()); // 现在是几秒:38
    System.out.println("现在是⼏纳秒:" + today.getNano()); // 现在是⼏纳秒:682102600
    System.out.println("现在是⼏号:" + today.get(ChronoField.DAY_OF_MONTH)); // 现在是⼏号:19

    /**
    * 常用的setXxx()系列操作,设置日期:
    *   LocalDateTime withYear(int year) 指定对象的年份
    *   LocalDateTime withMonth(int month) 指定对象的月份
    *   LocalDateTime withDayOfMonth(int dayOfMonth) 指定对象在当月的日期
    *   LocalDateTime withDayOfYear(int dayOfYear) 指定对象在当年的日期
    *   LocalDateTime withHour(int hour) 指定对象的小时数
    *   LocalDateTime withMinute(int minute) 指定对象的分钟数
    *   LocalDateTime withSecond(int second) 指定对象在当⽉的秒数
    *   LocalDateTime withNano(int nanoOfSecond) 指定对象在当年的纳秒数
    *   LocalDateTime with(TemporalField field, long newValue) 指定对象的日期时间,通过ChronoField枚举类
    */
    LocalDateTime withLocalDateTime = LocalDateTime.of(2020,02,8,21, 30, 59, 123456789);
    System.out.println("常用的setXxx()系列操作:" + withLocalDateTime); // 常用的setXxx()系列操作:2020-02-08T21:30:59.123456789
    System.out.println(withLocalDateTime.withYear(2030));// 2030-02-08T21:30:59.123456789
    System.out.println(withLocalDateTime.withMonth(2)); // 2020-02-08T21:30:59.123456789
    System.out.println(withLocalDateTime.withDayOfMonth(8)); // 2020-02-08T21:30:59.123456789
    System.out.println(withLocalDateTime.withDayOfYear(10)); // 2020-01-10T21:30:59.123456789
    System.out.println(withLocalDateTime.withHour(5)); // 2020-02-08T05:30:59.123456789
    System.out.println(withLocalDateTime.withMinute(10)); // 2020-02-08T21:10:59.123456789
    System.out.println(withLocalDateTime.withSecond(8)); // 2020-02-08T21:30:08.123456789
    System.out.println(withLocalDateTime.withNano(100000001)); // 2020-02-08T21:30:59.100000001
    System.out.println(withLocalDateTime.with(ChronoField.DAY_OF_MONTH, 1)); // 2020-02-01T21:30:59.123456789

    /**
    * 常用的plusXxx()系列操作,增加时间的方法:
    *   LocalDateTime plusYears(long years) 增加指定年份数
    *   LocalDateTime plusMonths(long months) 增加指定⽉份数
    *   LocalDateTime plusDays(long days) 增加指定天数
    *   LocalDateTime plusWeeks(long weeks) 增加指定周数
    *   LocalDateTime plusHours(long hours) 增加指定小时
    *   LocalDateTime plusMinutes(long minutes) 增加指定分钟
    *   LocalDateTime plusSeconds(long seconds) 增加指定秒
    *   LocalDateTime plusNanos(long nanos) 增加指定纳秒
    *   plus(long amountToAdd, TemporalUnit unit) 指定增加的字段的日期时间
    */
    LocalDateTime plusLocalDateTime = LocalDateTime.of(2020,02,8,21, 30, 59, 123456789);
    System.out.println("常用的plusXxx()系列操作:" + plusLocalDateTime); // 常用的plusXxx()系列操作:2020-02-08T21:30:59.123456789
    System.out.println(plusLocalDateTime.plusYears(1)); // 2021-02-08T21:30:59.123456789
    System.out.println(plusLocalDateTime.plusMonths(1)); // 2020-03-08T21:30:59.123456789
    System.out.println(plusLocalDateTime.plusDays(6)); // 2020-02-14T21:30:59.123456789
    System.out.println(plusLocalDateTime.plusWeeks(1)); // 2020-02-15T21:30:59.123456789
    System.out.println(plusLocalDateTime.plusHours(1)); // 2020-02-08T22:30:59.123456789
    System.out.println(plusLocalDateTime.plusMinutes(1)); // 2020-02-08T21:31:59.123456789
    System.out.println(plusLocalDateTime.plusSeconds(1)); // 2020-02-08T21:31:00.123456789
    System.out.println(plusLocalDateTime.plusNanos(6)); // 2020-02-08T21:30:59.123456795
    System.out.println(plusLocalDateTime.plus(1, ChronoUnit.DAYS)); // 2020-02-09T21:30:59.123456789

    /**
    * 常用的minusXxx()系列操作,减少时间的方法:
    *   LocalDateTime minusYears(long years) 减去指定年份
    *   LocalDateTime minusMonths(long months) 减去指定月份
    *   LocalDateTime minusDays(long days) 减去指定天数
    *   LocalDateTime minusWeeks(long weeks) 减去指定周数
    *   LocalDateTime minusHours(long hours) 减去指定小时
    *   LocalDateTime minusMinutes(long minutes) 减去指定分钟
    *   LocalDateTime minusSeconds(long seconds) 减去指定秒
    *   LocalDateTime minusNanos(long nanos) 减去指定纳秒
    *   LocalDateTime minus(long amountToSubtract, TemporalUnit unit) 减少指定字段的日期时间
    */
    LocalDateTime minusLocalDateTime = LocalDateTime.of(2020,02,8,21, 30, 59, 123456789);
    System.out.println("常用的minusXxx()系列操作:" + minusLocalDateTime); // 常用的minusXxx()系列操作:2020-02-08T21:30:59.123456789
    System.out.println(minusLocalDateTime.minusYears(5)); // 2015-02-08T21:30:59.123456789
    System.out.println(minusLocalDateTime.minusMonths(60)); // 2015-02-08T21:30:59.123456789
    System.out.println(minusLocalDateTime.minusDays(1826)); // 2015-02-08T21:30:59.123456789
    System.out.println(minusLocalDateTime.minusWeeks(260)); // 2015-02-14T21:30:59.123456789
    System.out.println(minusLocalDateTime.minusHours(1)); // 2020-02-08T20:30:59.123456789
    System.out.println(minusLocalDateTime.minusMinutes(60)); // 2020-02-08T20:30:59.123456789
    System.out.println(minusLocalDateTime.minusSeconds(3600)); // 2020-02-08T20:30:59.123456789
    System.out.println(minusLocalDateTime.minusNanos(9)); // 2020-02-08T21:30:59.123456780
    System.out.println(minusLocalDateTime.minus(1, ChronoUnit.HOURS)); // 2020-02-08T20:30:59.123456789

    /**
    * 常用日期对比方法:
    *   int compareTo(localDateTimeOne other) 比较当前对象和other对象在时间上的大小,返回值如果为正,则当前对象时间较晚
    *   boolean isBefore(localDateTimeOne other) 比较当前对象时间是否在other对象时间之前
    *   boolean isAfter(localDateTimeOne other) 比较当前对象时间是否在other对象时间之后
    *   boolean isEqual(ChronoLocalDateTime other) 比较两个日期对象是否相等
    */
    LocalDateTime localDateTimeOne = LocalDateTime.of(2022,02,8,21, 30, 59, 123456789);
    LocalDateTime localDateTimeTwo = LocalDateTime.of(2020,02,8,21, 30, 59, 123456789);
    System.out.println("compareTo: " + localDateTimeOne.compareTo(localDateTimeTwo)); // compareTo: 2
    System.out.println("isBefore: " + localDateTimeOne.isBefore(localDateTimeTwo)); // isBefore: false
    System.out.println("isAfter: " + localDateTimeOne.isAfter(localDateTimeTwo)); // isAfter: true
    System.out.println("isEqual: " + localDateTimeOne.isEqual(localDateTimeTwo)); // isEqual: false

    /**
    * 从LocalDateTime实例获取时间戳
    * 从LocalDateTime获取时间戳稍微有点麻烦,需先把LocalDateTime实例转为Instant实例,再调用Instant实例的toEpochMilli方法获得对应的时间戳。
    * 下面示例从本地日期时间实例获取对应的时间戳
    */
    LocalDateTime now = LocalDateTime.now();
    Instant instant1 = now.toInstant(ZoneOffset.ofHours(8));
    System.out.println("timeFromLocal1 = " + instant1.toEpochMilli()); // timeFromLocal1 = 1645272488866
    /**
    * 上面获取代码基于北京时间,所以转为Instant实例时使用了东八区。
    * 倘若在东八区以外的其他地区运行上述代码,就无法得到正确的当地时间戳,此时要先设置当地的默认时区,再将LocalDateTime实例转为Instant实例
    */
    Instant instant2 = now.atZone(ZoneId.systemDefault()).toInstant();
    System.out.println("timeFromLocal2 = " + instant2.toEpochMilli()); // timeFromLocal2 = 1645272488866

    /**
    * 当前日期时间替换成指定的日期时间, 这里会用到一个方法adjustInto()
    * Temporal adjustInto(Temporal temporal) 将指定的时间对象调整为具有与此对象相同的日期和时间
    * 不常用(该方法不学也罢)
    */
    // 获取当前时间
    LocalDateTime localDateTime = LocalDateTime.now();
    System.out.println("转换前的时间:" + localDateTime); // 转换前的时间:2022-02-19T22:25:20.859059200
    // 使用adjustInto()方法后
    LocalDateTime localDateTimeOf = LocalDateTime.of(2020,12,12,12,00,01);
    localDateTime = (LocalDateTime)localDateTimeOf.adjustInto(localDateTime);
    System.out.println("转换后的时间:" + localDateTime); // 转换后的时间:2020-12-12T12:00:01
}

LocalDateTime API 总结

LocalDateTime、LocalDate、LocalTime 的常用方法总结:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJsClCQp-1677485488676)(images/时间类/image-20220530114236918.png)]

LocalDateTime、LocalDate、LocalTime 的静态方法 of() 参数取值范围:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVzCG6L6-1677485488676)(images/时间类/image-20220530114256668.png)]

java.time 时区先导篇

首先介绍:LocalDateTime、OffsetDateTime 和 ZoneDateTime 之间的关系,且与 ZoneOffset(偏移量)和 ZoneId(时区) 的概念。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWlhkbCP-1677485488677)(images/时间类/image-20220530114504908.png)]

void ZonePreTest() {
    System.out.println(LocalDate.now()); // 2022-05-30
    System.out.println(LocalTime.now()); // 11:48:37.363
    System.out.println(LocalDateTime.now()); // 2022-05-30T11:48:37.363
    System.out.println(ZoneOffset.ofHours(8)); // +08:00
    System.out.println(ZoneId.systemDefault()); // Asia/Shanghai
    System.out.println(OffsetDateTime.now()); // 2022-05-30T11:48:37.363+08:00
    System.out.println(ZonedDateTime.now()); // 2022-05-30T11:48:37.363+08:00[Asia/Shanghai]
    }

java.time.ZoneId

ZoneId 是 java.time 引入的新的时区类,注意和旧的 java.util.TimeZone 区别。时区从基准 UTC 开始的一个固定偏移。ZoneId 的子类 ZoneOffset,代表了这种从伦敦格林威治零度子午线开始的时间偏移,也就是时差。并提供在 Instant 和 LocalDateTime 之间进行转换的规则。其中每个时区都对应着 ID,ID的格式为 “区域/城市” 。例如 :Asia/Shanghai 等。ZoneId:该类中包含了所有的时区信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dgiWpCq8-1677485488677)(images/时间类/image-20220530145008725.png)]

java.time.ZoneOffset

java.time.ZoneOffset 类用于表示距UTC时区的固定区域偏移量。它继承了 ZoneId 类并实现 Comparable 接口。ZoneOffset 类声明三个常量:

  • MAX:这是支持的最大区域偏移量

  • MIN:这是支持的最小区域偏移量

  • UTC:这是UTC的时区偏移常数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PzMOKMJE-1677485488677)(images/时间类/image-20220530145827203.png)]

@Test
void ZoneOffsetTest() {
    System.out.println(ZoneOffset.MAX); // +18:00
    System.out.println(ZoneOffset.MIN); // -18:00
    ZoneOffset zone = ZoneOffset.UTC;
    System.out.println(zone); // Z
    Temporal temp = zone.adjustInto(ZonedDateTime.now());
    System.out.println(temp); // 2022-02-19T18:55:40.567526+08:00[Asia/Shanghai]
    // 获取5小时偏移量的ZoneOffset对象
    System.out.println(ZoneOffset.ofHours(5)); // +05:00 		 System.out.println(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"))); //1653893996
}

java.time.OffsetDateTime

ISO-8601日历系统中与UTC偏移量有关的日期时间。OffsetDateTime是一个带有偏移量的日期时间类型。存储有精确到纳秒的日期时间,以及偏移量。可以简单理解为 OffsetDateTime = LocalDateTime + ZoneOffset。

OffsetDateTime、ZonedDateTime 和 Instant它们三都能在时间线上以纳秒精度存储一个瞬间(注意:LocalDateTime是不行的),也可理解我某个时刻。OffsetDateTime 和 Instant可用于模型的字段类型,因为它们都表示瞬间值并且还不可变,所以适合网络传输或者数据库持久化。

PS:ZonedDateTime 不适合网络传输/持久化,因为即使同一个 ZoneId 时区,不同地方获取到瞬时值也有可能不一样

@Test
    void OffsetDateTimeTest()
    {
        /**
         * 最大/最小值
         * 偏移量的最大值是+18,最小值是-18,这是由ZoneOffset内部的限制决定的。
         */
        OffsetDateTime min = OffsetDateTime.MIN;
        OffsetDateTime max = OffsetDateTime.MAX;
        // OffsetDateTime最小值:-999999999-01-01T00:00+18:00
        System.out.println("OffsetDateTime最小值:" + min);
        // OffsetDateTime最大值:+999999999-12-31T23:59:59.999999999-18:00
        System.out.println("OffsetDateTime最大值:" + max);
        // +18:00:-999999999-1-1
        System.out.println(min.getOffset() + ":" + min.getYear() + "-" + min.getMonthValue() + "-" + min.getDayOfMonth());
        // -18:00:999999999-12-31
        System.out.println(max.getOffset() + ":" + max.getYear() + "-" + max.getMonthValue() + "-" + max.getDayOfMonth());

        /**
         * ZoneOffset的实例化
         */
        // 当前位置偏移量的本地时间:2022-02-19T22:31:13.539368500+08:00
        System.out.println("当前位置偏移量的本地时间:" + OffsetDateTime.now());
        // 偏移量-4(纽约)的本地时间::2022-02-19T22:31:13.539368500-04:00
        System.out.println("偏移量-4(纽约)的本地时间::" + OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.of("-4")));
        // 纽约时区的本地时间:2022-02-19T09:31:13.539368500-05:00
        System.out.println("纽约时区的本地时间:" + OffsetDateTime.now(ZoneId.of("America/New_York")));

        /**
         * 转换:LocalDateTime -> OffsetDateTime
         * 通过此例值得注意的是:LocalDateTime#atOffset()/atZone()只是增加了偏移量/时区,本地时间是并没有改变的。
         * 若想实现本地时间到其它偏移量的对应的时间只能通过其ofInstant()系列构造方法。
         */
        LocalDateTime localDateTime = LocalDateTime.of(2021, 12, 12, 18, 00, 00);
        // 当前时区(北京)时间为:2021-12-12T18:00
        System.out.println("当前时区(北京)时间为:" + localDateTime);
        // 转换为偏移量为 -4的OffsetDateTime时间:(-4地方的晚上18点)
        // -4偏移量地方的晚上18点(方式一):2021-12-12T18:00-04:00
        System.out.println("-4偏移量地方的晚上18点(方式一):" + OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(-4)));
        // -4偏移量地方的晚上18点(方式二):2021-12-12T18:00-04:00
        System.out.println("-4偏移量地方的晚上18点(方式二):" + localDateTime.atOffset(ZoneOffset.ofHours(-4)));
        // 转换为偏移量为 -4的OffsetDateTime时间:(北京时间晚上18:00 对应的-4地方的时间点)
        // 当前地区对应的-4地方的时间:2021-12-12T06:00-04:00
        System.out.println("当前地区对应的-4地方的时间:" + OffsetDateTime.ofInstant(localDateTime.toInstant(ZoneOffset.ofHours(8)), ZoneOffset.ofHours(-4)));

        /**
         * 转换:OffsetDateTime -> LocalDateTime
         */
        OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.ofHours(-4));
        // -4偏移量时间为:2022-02-19T22:39:14.442577-04:00
        System.out.println("-4偏移量时间为:" + offsetDateTime);
        // 转为LocalDateTime 注意:时间还是未变的哦
        // LocalDateTime的表示形式:2022-02-19T22:39:14.442577
        System.out.println("LocalDateTime的表示形式:" + offsetDateTime.toLocalDateTime());
    }

java.time.ZonedDateTime

ZonedDateTime 其实就是带有时区的 LocalDateTime,其实就是 LocalDateTime + ZoneId,LocalDate、LocalTime、LocalDateTime 是不带时区的。

@Test
void ZonedDateTimeTest() {
    // 获取系统的默认时区编号
    System.out.println(ZoneId.systemDefault());
    // 获取本地默认时区国家的的日期
    System.out.println("本地时区的日期时间:" + LocalDateTime.now()); // 本地时区的日期时间:2022-02-18T19:17:37.416345200

    /**
    * 实例化ZonedDateTime对象:
    * 一种是通过now()方法返回当前时间,一种是通过of()方法放回指定时间。对会带上时区ZoneId对象
    *   ZonedDateTime now()
    *   ZonedDateTime now(ZoneId zone)
    *   ZonedDateTime now(Clock clock)
    *   ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
    *   ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone)
    *   ZonedDateTime of(int year, int month, int dayOfMonth,int hour, int minute, int second, int nanoOfSecond, ZoneId zone)
    */
    ZonedDateTime zbj1 = ZonedDateTime.now(); // 默认时区
    ZonedDateTime zny1 = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
    System.out.println(zbj1); // 2022-02-18T20:41:14.164538200+08:00[Asia/Shanghai]
    System.out.println(zny1); // 2022-02-18T07:41:14.164538200-05:00[America/New_York]


    /**
    * 另一种方式是通过给一个LocalDateTime附加一个ZoneId,就可以变成ZonedDateTime:
    */
    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"));
    // 以这种方式创建的ZonedDateTime,它的日期和时间与LocalDateTime相同,但附加的时区不同,因此是两个不同的时刻:
    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]

    /**
    * 对比LocalDateTime和ZonedDateTime都设置时区的情况
    */
    // 根据时区,获得指定时区的当前时间(可以理解为还是本地时区,所以输出不显示时区)
    LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("America/Phoenix"));
    System.out.println(localDateTime); // 2022-02-18T04:17:37.431349500
    // 获取指定时区,获得指定时区的当前时间(这个是把当前时间指定到固定的时区)
    ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Europe/Monaco"));
    System.out.println(zonedDateTime); // 2022-02-18T19:17:37.416345200+01:00[Europe/Monaco]

    /**
    * 时区转换:
    * 要转换时区,通过ZonedDateTime的withZoneSameInstant()将关联时区转换到另一个时区,转换后日期和时间都会相应调整。
    * 下面的代码演示了如何将北京时间转换为纽约时间:
    * 要特别注意,时区转换的时候,由于夏令时的存在,不同的日期转换的结果很可能是不同的。这是北京时间9月15日的转换结果:
    * 下面两次转换后的纽约时间有1小时的夏令时时差( 涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时)
    */
    // 设置中国时区时间9月15日:
    ZonedDateTime zbj3 = ZonedDateTime.of(2020, 9, 15, 15, 16, 17, 00, ZoneId.of("Asia/Shanghai"));
    // 转换为纽约时间:
    ZonedDateTime zny3 = zbj3.withZoneSameInstant(ZoneId.of("America/New_York"));
    System.out.println(zbj3); // 2020-09-15T15:16:17+08:00[Asia/Shanghai]
    System.out.println(zny3); // 2020-09-15T03:16:17-04:00[America/New_York]

    // 设置中国时区时间11月15日:
    ZonedDateTime zbj4 = ZonedDateTime.of(2020, 11, 15, 15, 16, 17, 00, ZoneId.of("Asia/Shanghai"));
    // 转换为纽约时间:
    ZonedDateTime zny4 = zbj4.withZoneSameInstant(ZoneId.of("America/New_York"));
    System.out.println(zbj4); // 2020-11-15T15:16:17+08:00[Asia/Shanghai]
    System.out.println(zny4); // 2020-11-15T02:16:17-05:00[America/New_York]


    /**
    * 访问与设置ZonedDateTime对象的时间(与LocalDateTime用法基本一致, 案例直接参考LocalDateTime)
    *
    * 1.常用的getXxx()系列操作,获得日期:
    *   int getYear() 获取当前日期的年份
    *   Month getMonth() 获取当前日期的月份对象(返回一个Month枚举值)
    *   int getMonthValue() 获取当前日期是第几月(1-12)
    *   int getDayOfMonth() 表示该对象表示的⽇期是这个⽉第⼏天(1-31)
    *   DayOfWeek getDayOfWeek() 表示该对象表示的日期是星期几(返回一个DayOfWeek枚举值)
    *   int getDayOfYear() 表示该对象表示的日期是今年第几天(1-366)
    *   int getHour() 获取当前时间小时数
    *   int getMinute() 获取当前时间分钟数
    *   int getSecond() 获取当前时间秒值
    *   int getNano() 把获取到的当前时间的秒数换算成纳秒
    *
    * 2.常用的setXxx()系列操作,设置日期:
    *   ZonedDateTime withYear(int year) 指定对象的年份
    *   ZonedDateTime withMonth(int month) 指定对象的月份
    *   ZonedDateTime withDayOfMonth(int dayOfMonth) 指定对象在当月的日期
    *   ZonedDateTime withDayOfYear(int dayOfYear) 指定对象在当年的日期
    *   ZonedDateTime withHour(int hour) 指定对象的小时数
    *   ZonedDateTime withMinute(int minute) 指定对象的分钟数
    *   ZonedDateTime withSecond(int second) 指定对象在当⽉的秒数
    *   ZonedDateTime withNano(int nanoOfSecond) 指定对象在当年的纳秒数
    *
    * 3.常用的plusXxx()系列操作,增加时间的方法:
    *   ZonedDateTime plusYears(long years) 增加指定年份数
    *   ZonedDateTime plusMonths(long months) 增加指定⽉份数
    *   ZonedDateTime plusDays(long days) 增加指定天数
    *   ZonedDateTime plusWeeks(long weeks) 增加指定周数
    *   ZonedDateTime plusHours(long hours) 增加指定小时
    *   ZonedDateTime plusMinutes(long minutes) 增加指定分钟
    *   ZonedDateTime plusSeconds(long seconds) 增加指定秒
    *   ZonedDateTime plusNanos(long nanos) 增加指定纳秒
    *
    * 4.常用的minusXxx()系列操作,减少时间的方法:
    *   ZonedDateTime minusYears(long years) 减去指定年份
    *   ZonedDateTime minusMonths(long months) 减去指定月份
    *   ZonedDateTime minusDays(long days) 减去指定天数
    *   ZonedDateTime minusWeeks(long weeks) 减去指定周数
    *   ZonedDateTime minusHours(long hours) 减去指定小时
    *   ZonedDateTime minusMinutes(long minutes) 减去指定分钟
    *   ZonedDateTime minusSeconds(long seconds) 减去指定秒
    *   ZonedDateTime minusNanos(long nanos) 减去指定纳秒
    */
    // 案例直接参考LocalDateTime
}

java.time.Duration/Period

JDK8中提供了两个工具类 Duration/Period:计算日期时间差。它们最大的作用就不需要你自己复杂的计算关于两个年月日之间的相差的时间或日期啦。

  • Period :基于日期值,对应使用 LocalDate ,它们的作用范围域都是日期(年/月/日)
  • Duration:基于时间值,对应使用 Instant /LocalTime/LocalDateTime(不包含LocalDate,会报异常),它们的作用范围域都是时间(天/时/分/秒/毫秒/纳秒)注意:Duration 不包含毫秒这个属性

Duration 类常用API:

  • 静态方法 between(to, from):计算两个时间的间隔,默认是秒
  • 静态方法 ofDays()、ofHours()、ofMinutes()、ofSeconds()、ofMillis()、ofNanos():计算指定时间段
  • 静态方法 of(int amount, ChronoUnit.Xxx) 计算指定时间段
  • 静态方法 from(TemporalAmount amount):从时间量获取Duration的实例
  • toDays():将时间转换为以天为单位的
  • toHours():将时间转换为以时为单位的
  • toMinutes():将时间转换为以分钟为单位的
  • toSeconds():将时间转换为以秒为单位的
  • toMillis():将时间转换为以毫秒为单位的
  • toNanos():将时间转换为以纳秒为单位的
  • toXxxPart():跟如上toXxx方法基本一样,区别就是返回类型由long变成了int,个人认为可能是节约资源
  • withXxx():设置好前时间段的固定的时间
  • plusXxx():给当前时间段加上固定的时间
  • minusXxx():给当前时间段减少固定的时间
@Test
void DurationTest() {
    /**
         * 实例化指定单位的持续时间对象
         * 注意: 默认的打印结果为ISO国际标准化组织规定的日期格式,PT2H中的H,表示Hour小时,M代表Minute分钟,S代表Second秒数
         */
    Duration durationDays1 = Duration.ofDays(10); // 10天
    Duration durationDays2 = Duration.of(10, ChronoUnit.DAYS); // 10天
    System.out.println(durationDays1); // PT240H
    System.out.println(durationDays2); // PT240H
    Duration durationHours1 = Duration.ofHours(1); // 1小时
    Duration durationHours2 = Duration.of(1, ChronoUnit.HOURS); // 1小时
    System.out.println(durationHours1); // PT1H
    System.out.println(durationHours2); // PT1H
    Duration durationMinutes1 = Duration.ofMinutes(1); // 1分
    Duration durationMinutes2 = Duration.of(1, ChronoUnit.MINUTES); // 1分
    System.out.println(durationMinutes1); // PT1M
    System.out.println(durationMinutes2); // PT1M
    Duration durationSeconds1 = Duration.ofSeconds(1); // 1秒
    Duration durationSeconds2 = Duration.of(1, ChronoUnit.SECONDS); // 1秒
    System.out.println(durationSeconds1); // PT1S
    System.out.println(durationSeconds2); // PT1S
    Duration durationMillis1 = Duration.ofMillis(1000); // 1000毫秒
    Duration durationMillis2 = Duration.of(1000, ChronoUnit.MILLIS); // 1000毫秒
    System.out.println(durationMillis1); // PT1S
    System.out.println(durationMillis2); // PT1S
    Duration durationNanos1 = Duration.ofNanos(100000000); // 10000000纳秒
    Duration durationNanos2 = Duration.of(100000000, ChronoUnit.NANOS); // 10000000纳秒
    System.out.println(durationNanos1); // PT0.1S
    System.out.println(durationNanos2); // PT0.1S
    Duration durationFrom = Duration.from(ChronoUnit.DAYS.getDuration());
    System.out.println(durationFrom); // PT24H

    /**
         * 获取指定单位的持续时间
         *   long toDays() 这段时间的总天数
         *   long toHours() 这段时间的小时数
         *   long toMinutes() 这段时间的分钟数
         *   long toSeconds() 这段时间的秒数
         *   long toMillis() 这段时间的毫秒数
         *   long toNanos() 这段时间的纳秒数
         *   String toString() 此持续时间的字符串表示形式,使用基于ISO-8601秒*的表示形式,例如 PT8H6M12.345S
         */
    Duration durationOne = Duration.ofDays(1); // 设置1天的时间
    System.out.println("toDay天 = "+ durationOne.toDays()); // toDay时间 = 1
    System.out.println("toHours时 = "+ durationOne.toHours()); // toHours时间 = 24
    System.out.println("toMinutes分 = "+ durationOne.toMinutes()); // toMinutes时间 = 1440
    //System.out.println("toMinutes秒 = "+ durationOne.toSeconds()); // toMinutes秒 = 86400
    System.out.println("toMillis毫秒 = "+ durationOne.toMillis()); // toMillis时间 = 86400000
    System.out.println("toNanos纳秒 = "+ durationOne.toNanos()); // toNanos时间 = 86400000000000
    System.out.println("toString格式 = "+ durationOne.toString()); // toString时间 = PT24H

    /**
         * 获取2个时间点之间差值的持续时间
         *   Duration.between()方法创建Duration对象,注意这个天数是可以负数,意味着如果开始时间比结束时间更后面就会得到负数天数
         */
    LocalDateTime from = LocalDateTime.of(2017, 01, 1, 00, 0, 0);   // 2017-01-01 00:00:00
    LocalDateTime to = LocalDateTime.of(2019, 9, 12, 14, 28, 0); // 2019-09-12 14:28:00
    Duration duration1 = Duration.between(from, to);  // 表示从 from 到 to 这段时间(第⼆个参数减第⼀个参数)
    System.out.println(duration1.toDays()); // 984
    System.out.println(duration1.toHours()); // 23630
    System.out.println(duration1.toMinutes()); // 1417828
    System.out.println(duration1.getSeconds()); // 85069680
    System.out.println(duration1.toMillis()); // 85069680000
    System.out.println(duration1.toNanos()); // 85069680000000000

    /**
         * Duration时间的加减,可以参考LocalDateTime中的plusXxx、minusXxx和withXxx()系列的方法
         * 注意: Duration包含两部分:seconds秒,nanos纳秒,它们的组合表达了时间长度。所以withXxx()只有withSeconds()和withNanos()方法
         */
    System.out.println(Duration.ofDays(4).withSeconds(360).toHours()); // 加8小时(4天8小时):输出:0
    System.out.println(Duration.ofDays(4).plusHours(8).toHours()); // 加8小时(4天8小时):输出:104
    System.out.println(Duration.ofDays(4).minusHours(8).toHours()); // 加8小时(4天8小时):输出:88

    /**
         * Duration可以接收:LocalDate、LocalTime、LocalDateTime、Instant
         * Duration只能处理两个Instant、LocalTime, LocalDateTime, ZonedDateTime,
         * 参数不能混搭,混搭会报异常,如果传入的是LocalDate,将会抛出异常
         */
    Duration.between(LocalTime.now(), LocalTime.now());
    Duration.between(LocalDateTime.now(), LocalDateTime.now());
    Duration.between(Instant.now(), Instant.now());
}

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

Period 类常用API:

静态方法 between():计算两个日期之间的间隔获取Period实例
静态方法 ofYears()、ofMonths()、ofDays()、ofWeeks():计算指定时间段获取Period实例
静态方法 of(int years, int months, int days):计算指定时间段获取Period实例
静态方法 from(TemporalAmount amount):从时间量获取Period的实例
getYears():获取年份
getMonths():获取月份
getDays():获取天数

@Test
void PeriodTest() {
    /**
    * 实例化指定单位的持续日期对象
    * 注意: 默认的打印结果为ISO国际标准化组织规定的日期格式,P3Y2M1D中的,Y表示Year年,M代表Month月,D代表Day天
    */
    System.out.println(Period.between(LocalDate.now(), LocalDate.now())); // P0D
    System.out.println(Period.of(1,2,3)); // P1Y2M3D
    System.out.println(Period.ofYears(1)); // P1Y
    System.out.println(Period.ofMonths(2)); // P2M
    System.out.println(Period.ofDays(25)); // P25D
    System.out.println(Period.ofWeeks(4)); // P28D
    System.out.println(Period.from(Period.of(3, 2, 1))); // P3Y2M1D

    /**
    * 获取指定单位的持续时间
    */
    Period periodYears = Period.ofYears(1); // 设置1年的时间
    System.out.println(periodYears.getYears()); // 1
    System.out.println(periodYears.getMonths()); // 0
    System.out.println(periodYears.getDays()); // 0
    System.out.println(periodYears.get(ChronoUnit.YEARS)); // 1
    System.out.println(periodYears.get(ChronoUnit.MONTHS)); // 0
    System.out.println(periodYears.get(ChronoUnit.DAYS)); // 0
    System.out.println(periodYears.getChronology()); // 获取此Period的年表,即ISO日历系统:ISO
    System.out.println(periodYears.getUnits()); // 查看支持的枚举类型:[Years, Months, Days]

    /**
    * Period时间的加减,可以参考LocalDateTime中的plusXxx、minusXxx和withXxx()系列的方法
    */
    System.out.println(Period.of(1,2,3).withYears(8).getYears()); // 8
    System.out.println(Period.of(1,2,3).withMonths(8).getMonths()); // 8
    System.out.println(Period.of(1,2,3).withDays(8).getDays()); // 8
    System.out.println(Period.of(1,2,3).plusYears(8).getYears()); // 9
    System.out.println(Period.of(1,2,3).plusMonths(8).getMonths()); // 10
    System.out.println(Period.of(1,2,3).plusDays(8).getDays()); // 11
    System.out.println(Period.of(1,2,3).minusYears(8).getYears()); // -7
    System.out.println(Period.of(1,2,3).minusMonths(8).getMonths()); // -6
    System.out.println(Period.of(1,2,3).minusDays(8).getDays()); // -5
}

TemporalAdjuster 矫正器

LocalDateTime、Duration、Instant等,尽管已经提供了很多日期加减的方法,但还是比较局限性,如“下个星期天”,“下个结婚生日”,"下个月的第一天"等需求便难以满足。这时我们通过时间校正器效果可能会更好。

  • TemporalAdjuster:时间校正器
  • TemporalAdjusters:通过该类静态方法提供了大量的常用 TemporalAdjuster 的实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZT6PuzJa-1677485488677)(images/时间类/image-20220601104949392.png)]

@Test
void TemporalAdjusterTest() {
    LocalDateTime now = LocalDateTime.now(); // 首先获取当前时间
    System.out.println("当前时间:"+now); // 当前时间:2022-02-17T22:01:45.718728600
    /**
         * 方式一:使用TemporalAdjuster接口自定义日期方式实现
         */
    TemporalAdjuster adJuster = (temporal) -> {
        LocalDateTime dateTime = (LocalDateTime) temporal;
        DayOfWeek dayOfWeek = dateTime.getDayOfWeek(); // 先获取周几
        if (DayOfWeek.FRIDAY.equals(dayOfWeek)) {
            return dateTime.plusDays(3); // 周五加三天等于工作日
        } else if (DayOfWeek.SATURDAY.equals(dayOfWeek)) {
            return dateTime.plusDays(2); // 周六加两天
        }
        return dateTime.plusDays(1); // 其他均加一天
    };
    // 下一个工作日:2022-02-18T22:01:45.718728600
    System.out.println("下一个工作日:" + now.with(adJuster));

    /**
         * 方式二:使用TemporalAdjusters工具类提供的日期来实现
         */
    LocalDateTime with1 = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    // 下周日:2022-02-20T22:01:45.718728600
    System.out.println("下周日:" + with1);

    LocalDateTime with2 = now.with(TemporalAdjusters.firstDayOfMonth());
    // 这个月的第一天:2022-02-01T22:01:45.718728600
    System.out.println("这个月的第一天:" + with2);

    LocalDateTime with3 = now.with(TemporalAdjusters.firstDayOfNextMonth());
    // 下个月的第一天:2022-03-01T22:01:45.718728600
    System.out.println("下个月的第一天:" + with3);
}

java.time.format.DateTimeFormatter

Java8 后开始使用 java.time.format.DateTimeFormatter 类用于解析日期字符串和格式化日期输出。格式化字符串的使用方式与 Java8 之前的日期时间处理类 java.text.DateFormat 完全一致。

java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

  • 预定义的标准格式(系统提供好了枚举类)
  • 语言环境相关的格式(设置指定的语言环境Locale,如:Locale.CHINA、Locale.US)
  • 自定义的格式(自定义设置格式,例如:DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm”))

DateTimeFormatter 常用API :

DateTimeFormatter ofPattern(“yyyy-MM-dd”):静态方法,通过给定格式获取对象
String format(TemporalAccessor temporal):把一个日期对象的默认格式格式化成指定的格式的字符串
TemporalAccessor parse(CharSequence text):把一个字符串格式化成指定格式的 TemporalAccessor 对象(一般不建议用)

@Test
void DateTimeFormatterTest() {
    /**
         * DateTimeFormatter 类的两种转换方式
         *   Date -> String:String format(TemporalAccessor temporal)
         *   String -> Date:TemporalAccessor parse(CharSequence text)
         */
    LocalDateTime localDateTime = LocalDateTime.of(2020, 12, 12, 12, 12);
    // DateTimeFormatter类的:Date -> String
    System.out.println(DateTimeFormatter.ISO_DATE.format(localDateTime)); // 2020-12-12
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    System.out.println(dateTimeFormatter.format(localDateTime)); // 2020-12-12 12:12:00
    DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss E", Locale.CHINA);
    System.out.println(dateTimeFormatter1.format(localDateTime)); // 2020-12月-12 12:12:00 周六
    DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("EEEE, MMMM/dd/yyyy HH:mm", Locale.US);
    System.out.println(dateTimeFormatter2.format(localDateTime)); // Saturday, December/12/2020 12:12

    /**
         * DateTimeFormatter类的:String -> Date
         * Java DateTimeFormatter.parse()的坑:
         * 直接转换是无法转换的,运行时的具体类型是:java.time.format.Parsed类型,无法类型转换为java.io下面的时间类
         * 参考:https://blog.csdn.net/D___H/article/details/121479052
         */
    TemporalAccessor parse = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").parse("2020-12-12 12:12:12");
    System.out.println(parse); // {},ISO resolved to 2020-12-12T12:12:12
    // 直接(强)转换是无法转换的,所以只能使用对应日期类的from()方法获得对应的日期类型
    LocalDateTime from = LocalDateTime.from(parse);
    System.out.println(from); // 2020-12-12T12:12:12
    // 直接强转会报错:java.time.format.DateTimeParseException: Text '2020-12-12 12:12:12' could not be parsed at index 5
    LocalDateTime parseLocalDateTime = (LocalDateTime) dateTimeFormatter1.parse("2020-12-12 12:12:12");
}

日期时间格式化输出 & 字符串解析

java.time.format.DateTimeFormatter 能够进行 TemporalAccessor 类型(包括:LocalDate、LocalTime、LocalDateTime、ZonedDateTime、OffsetDateTime)的格式化输出。同时 LocalDate、LocalTime、LocalDateTime、ZonedDateTime、OffsetDateTime 提供了静态的 parse 方法,能够进行字符串解析。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G5unn5ZM-1677485488678)(images/时间类/image-20220601112612914.png)]

@Test
void DateTimeFormatter2Test() {
    /**
         * 日期时间类(LocalDateTime...等)自带的两种转换方式
         *   Date -> String:String format(DateTimeFormatter formatter)
         */
    LocalDateTime now = LocalDateTime.of(2020, 12, 12, 12, 12, 12);
    // Date -> String
    System.out.println("----------Date -> String----------");
    // 2020-12-12T12:12:12
    System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));
    // 2020-12月-12 12:12:12 周六
    System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss E", Locale.CHINA)));
    // Saturday, December/12/2020 12:12
    System.out.println(now.format(DateTimeFormatter.ofPattern("EEEE, MMMM/dd/yyyy HH:mm", Locale.US)));
    // 2020-12-12 12:12:12
    System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

    /**
         * 日期时间类(LocalDateTime...等)自带的两种转换方式
         * String -> Date:LocalDateTime parse(CharSequence text, DateTimeFormatter formatter)
         */
    System.out.println("----------String -> Date----------");
    // 2020-12-12T12:12:12
    System.out.println(LocalDateTime.parse("2020-12-12T12:12:12", DateTimeFormatter.ISO_DATE_TIME));
    // 2020-12-12T12:12
    System.out.println(LocalDateTime.parse("Saturday, December/12/2020 12:12", DateTimeFormatter.ofPattern("EEEE, MMMM/dd/yyyy HH:mm", Locale.US)));
    // 2020-12-12T12:12:12
    System.out.println(LocalDateTime.parse("2020-12-12 12:12:12", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}

Java8 的日期时间API总结

前面详细介绍了Java 8的日期时间API,现在进行简单总结一下。新的时间与日期 API 中很重要的一点是,它定义清楚了基本的时间与日期的概念,比方说日期、时间、瞬时时间、持续时间、时区及时间段。它们都是基于 ISO8601 日历系统,它是世界民用历法,也就是我们所说的公历。

  • java.time包下主要包含下面几个主要的类:

  • LocalDate:表示不带时间的日期,比如:2016-10-20

  • LocalTime:表示不带日期的时间,比如:23:12:10

  • LocalDateTime:日期时间,比如:2016-10-20 23:14:21

  • TemporalAdjuster : 时间调节器

  • TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等

  • Duration:持续时间,计算两个“时间”的间隔

  • Period:日期间隔,计算两个“日期”的间隔

  • Instant:Unix 时间,它代表的是时间戳,比如 2018-01-14T02:20:13.592Z

  • Clock:时钟,获取某个时区下的瞬时时间

  • ZoneId:时区id,例如 Asia/Shanghai等

  • ZonedDateTime:带时区的日期时间

  • DateTimeFormatter:时间格式化

新的 API 区分各种日期时间概念并且各个概念使用相似的方法定义模式,这种相似性非常有利于 API 的学习。总结一下一般的方法规律:

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

三、Java 的时间工具类

LocalDateTime和时间戳互转

/**
     * 获取到毫秒级时间戳
     * @param localDateTime 具体时间
     * @return long 毫秒级时间戳
     */
public static long toEpochMilli(LocalDateTime localDateTime){
    return localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
}

/**
     * 毫秒级时间戳转 LocalDateTime
     * @param epochMilli 毫秒级时间戳
     * @return LocalDateTime
     */
public static LocalDateTime ofEpochMilli(long epochMilli){
    return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.of("+8"));
}

/**
     * 获取到秒级时间戳
     * @param localDateTime 具体时间
     * @return long 秒级时间戳
     */
public static long toEpochSecond(LocalDateTime localDateTime){
    return localDateTime.toEpochSecond(ZoneOffset.of("+8"));
}

/**
     * 毫秒级时间戳转 LocalDateTime
     * @param epochSecond 秒级时间戳
     * @return LocalDateTime
     */
public static LocalDateTime ofEpochSecond(long epochSecond){
    return LocalDateTime.ofEpochSecond(epochSecond, 0 , ZoneOffset.of("+8"));
}

LocalDateTime和Date互转

/**
     * Date时间类转LocalDateTime
     * @param date Date时间类
     * @return LocalDateTime
     */
public static LocalDateTime date2LocalDateTime(Date date) {
    return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
}

/**
     * LocalDateTime时间类转 Date时间类
     * @param localDateTime LocalDateTime时间类
     * @return Date时间类
     */
public static Date localDateTime2Date(LocalDateTime localDateTime) {
    return  Date.from(localDateTime.atZone(ZoneOffset.of("+8")).toInstant());
}

LocalDateTime和字符串互转

/**
     * LocalDateTime转时间格式字符串
     * @param localDateTime 时间
     * @return string
     */
public static String formatToString(LocalDateTime localDateTime){
    String format  = "yyyy:MM:dd HH:mm:ss";
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
    return localDateTime.format(dateTimeFormatter);
}

/**
     *  时间字符串 转LocalDateTime
     * @param localDateTime  时间字符串
     * @return LocalDateTime
     */
public static LocalDateTime stringToFormat(String localDateTime){
    String format  = "yyyy:MM:dd HH:mm:ss";
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(format);
    return LocalDateTime.parse(localDateTime, dateTimeFormatter);
}

Java 8 时间工具类

public class LocalDateUtils {

    /**
     * 显示年月日时分秒,例如 2015-08-11 09:51:53.
     */
    public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 仅显示年月日,例如 2015-08-11.
     */
    public static final String DATE_PATTERN = "yyyy-MM-dd";

    /**
     * 仅显示时分秒,例如 09:51:53.
     */
    public static final String TIME_PATTERN = "HH:mm:ss";

    /**
     * 显示年月日时分秒(无符号),例如 20150811095153.
     */
    public static final String UNSIGNED_DATETIME_PATTERN = "yyyyMMddHHmmss";

    /**
     * 仅显示年月日(无符号),例如 20150811.
     */
    public static final String UNSIGNED_DATE_PATTERN = "yyyyMMdd";

    /**
     * 春天;
     */
    public static final Integer SPRING = 1;

    /**
     * 夏天;
     */
    public static final Integer SUMMER = 2;

    /**
     * 秋天;
     */
    public static final Integer AUTUMN = 3;

    /**
     * 冬天;
     */
    public static final Integer WINTER = 4;

    /**
     * 星期日;
     */
    public static final String SUNDAY = "星期日";

    /**
     * 星期一;
     */
    public static final String MONDAY = "星期一";

    /**
     * 星期二;
     */
    public static final String TUESDAY = "星期二";

    /**
     * 星期三;
     */
    public static final String WEDNESDAY = "星期三";

    /**
     * 星期四;
     */
    public static final String THURSDAY = "星期四";

    /**
     * 星期五;
     */
    public static final String FRIDAY = "星期五";

    /**
     * 星期六;
     */
    public static final String SATURDAY = "星期六";

    /**
     * 年
     */
    private static final String YEAR = "year";

    /**
     * 月
     */
    private static final String MONTH = "month";

    /**
     * 周
     */
    private static final String WEEK = "week";

    /**
     * 日
     */
    private static final String DAY = "day";

    /**
     * 时
     */
    private static final String HOUR = "hour";

    /**
     * 分
     */
    private static final String MINUTE = "minute";

    /**
     * 秒
     */
    private static final String SECOND = "second";

    /**
     * 获取当前日期和时间字符串.
     *
     * @return String 日期时间字符串,例如 2015-08-11 09:51:53
     */
    public static String getLocalDateTimeStr() {
        return format(LocalDateTime.now(), DATETIME_PATTERN);
    }

    /**
     * 获取当前日期字符串.
     *
     * @return String 日期字符串,例如2015-08-11
     */
    public static String getLocalDateStr() {
        return format(LocalDate.now(), DATE_PATTERN);
    }

    /**
     * 获取当前时间字符串.
     *
     * @return String 时间字符串,例如 09:51:53
     */
    public static String getLocalTimeStr() {
        return format(LocalTime.now(), TIME_PATTERN);
    }

    /**
     * 获取当前星期字符串.
     *
     * @return String 当前星期字符串,例如 星期二
     */
    public static String getDayOfWeekStr() {
        return format(LocalDate.now(), "E");
    }

    /**
     * 获取指定日期是星期几
     *
     * @param localDate 日期
     * @return String 星期几
     */
    public static String getDayOfWeekStr(LocalDate localDate) {
        String[] weekOfDays = {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
        int dayOfWeek = localDate.getDayOfWeek().getValue() - 1;
        return weekOfDays[dayOfWeek];
    }

    /**
     * 获取日期时间字符串
     *
     * @param temporal 需要转化的日期时间
     * @param pattern  时间格式
     * @return String 日期时间字符串,例如 2015-08-11 09:51:53
     */
    public static String format(TemporalAccessor temporal, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        return dateTimeFormatter.format(temporal);
    }

    /**
     * 日期时间字符串转换为日期时间(java.time.LocalDateTime)
     *
     * @param localDateTimeStr 日期时间字符串
     * @param pattern          日期时间格式 例如DATETIME_PATTERN
     * @return LocalDateTime 日期时间
     */
    public static LocalDateTime parseLocalDateTime(String localDateTimeStr, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(localDateTimeStr, dateTimeFormatter);
    }

    /**
     * 日期字符串转换为日期(java.time.LocalDate)
     *
     * @param localDateStr 日期字符串
     * @param pattern      日期格式 例如DATE_PATTERN
     * @return LocalDate 日期
     */
    public static LocalDate parseLocalDate(String localDateStr, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(localDateStr, dateTimeFormatter);
    }

    /**
     * 获取指定日期时间加上指定数量日期时间单位之后的日期时间.
     *
     * @param localDateTime 日期时间
     * @param num           数量
     * @param chronoUnit    日期时间单位
     * @return LocalDateTime 新的日期时间
     */
    public static LocalDateTime plus(LocalDateTime localDateTime, int num, ChronoUnit chronoUnit) {
        return localDateTime.plus(num, chronoUnit);
    }

    /**
     * 获取指定日期时间减去指定数量日期时间单位之后的日期时间.
     *
     * @param localDateTime 日期时间
     * @param num           数量
     * @param chronoUnit    日期时间单位
     * @return LocalDateTime 新的日期时间
     */
    public static LocalDateTime minus(LocalDateTime localDateTime, int num, ChronoUnit chronoUnit) {
        return localDateTime.minus(num, chronoUnit);
    }

    /**
     * 根据ChronoUnit计算两个日期时间之间相隔日期时间
     *
     * @param start      开始日期时间
     * @param end        结束日期时间
     * @param chronoUnit 日期时间单位
     * @return long 相隔日期时间
     */
    public static long getChronoUnitBetween(LocalDateTime start, LocalDateTime end, ChronoUnit chronoUnit) {
        return Math.abs(start.until(end, chronoUnit));
    }

    /**
     * 根据ChronoUnit计算两个日期之间相隔年数或月数或天数
     *
     * @param start      开始日期
     * @param end        结束日期
     * @param chronoUnit 日期时间单位,(ChronoUnit.YEARS,ChronoUnit.MONTHS,ChronoUnit.WEEKS,ChronoUnit.DAYS)
     * @return long 相隔年数或月数或天数
     */
    public static long getChronoUnitBetween(LocalDate start, LocalDate end, ChronoUnit chronoUnit) {
        return Math.abs(start.until(end, chronoUnit));
    }

    /**
     * 获取本年第一天的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfYearStr() {
        return getFirstDayOfYearStr(LocalDateTime.now());
    }

    /**
     * 获取本年最后一天的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfYearStr() {
        return getLastDayOfYearStr(LocalDateTime.now());
    }

    /**
     * 获取指定日期当年第一天的日期字符串
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfYearStr(LocalDateTime localDateTime) {
        return getFirstDayOfYearStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期当年最后一天的日期字符串
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfYearStr(LocalDateTime localDateTime) {
        return getLastDayOfYearStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期当年第一天的日期字符串,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfYearStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withDayOfYear(1).withHour(0).withMinute(0).withSecond(0), pattern);
    }

    /**
     * 获取指定日期当年最后一天的日期字符串,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfYearStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(TemporalAdjusters.lastDayOfYear()).withHour(23).withMinute(59).withSecond(59), pattern);
    }

    /**
     * 获取本月第一天的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfMonthStr() {
        return getFirstDayOfMonthStr(LocalDateTime.now());
    }

    /**
     * 获取本月最后一天的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfMonthStr() {
        return getLastDayOfMonthStr(LocalDateTime.now());
    }

    /**
     * 获取指定日期当月第一天的日期字符串
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getFirstDayOfMonthStr(LocalDateTime localDateTime) {
        return getFirstDayOfMonthStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期当月最后一天的日期字符串
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfMonthStr(LocalDateTime localDateTime) {
        return getLastDayOfMonthStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期当月第一天的日期字符串,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfMonthStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0), pattern);
    }

    /**
     * 获取指定日期当月最后一天的日期字符串,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfMonthStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59), pattern);
    }

    /**
     * 获取本周第一天的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfWeekStr() {
        return getFirstDayOfWeekStr(LocalDateTime.now());
    }

    /**
     * 获取本周最后一天的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfWeekStr() {
        return getLastDayOfWeekStr(LocalDateTime.now());
    }

    /**
     * 获取指定日期当周第一天的日期字符串,这里第一天为周一
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfWeekStr(LocalDateTime localDateTime) {
        return getFirstDayOfWeekStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期当周最后一天的日期字符串,这里最后一天为周日
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfWeekStr(LocalDateTime localDateTime) {
        return getLastDayOfWeekStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期当周第一天的日期字符串,这里第一天为周一,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getFirstDayOfWeekStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(DayOfWeek.MONDAY).withHour(0).withMinute(0).withSecond(0), pattern);
    }

    /**
     * 获取指定日期当周最后一天的日期字符串,这里最后一天为周日,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getLastDayOfWeekStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(DayOfWeek.SUNDAY).withHour(23).withMinute(59).withSecond(59), pattern);
    }

    /**
     * 获取今天开始时间的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getStartTimeOfDayStr() {
        return getStartTimeOfDayStr(LocalDateTime.now());
    }

    /**
     * 获取今天结束时间的日期字符串
     *
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getEndTimeOfDayStr() {
        return getEndTimeOfDayStr(LocalDateTime.now());
    }

    /**
     * 获取指定日期开始时间的日期字符串
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 00:00:00
     */
    public static String getStartTimeOfDayStr(LocalDateTime localDateTime) {
        return getStartTimeOfDayStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期结束时间的日期字符串
     *
     * @param localDateTime 指定日期时间
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getEndTimeOfDayStr(LocalDateTime localDateTime) {
        return getEndTimeOfDayStr(localDateTime, DATETIME_PATTERN);
    }

    /**
     * 获取指定日期开始时间的日期字符串,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd HH:mm:ss
     */
    public static String getStartTimeOfDayStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withHour(0).withMinute(0).withSecond(0), pattern);
    }

    /**
     * 获取指定日期结束时间的日期字符串,带日期格式化参数
     *
     * @param localDateTime 指定日期时间
     * @param pattern       日期时间格式
     * @return String 格式:yyyy-MM-dd 23:59:59
     */
    public static String getEndTimeOfDayStr(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withHour(23).withMinute(59).withSecond(59), pattern);
    }

    /**
     * 切割日期。按照周期切割成小段日期段。例如: <br>
     *
     * @param startDate 开始日期(yyyy-MM-dd)
     * @param endDate   结束日期(yyyy-MM-dd)
     * @param period    周期(天,周,月,年)
     * @return 切割之后的日期集合
     * <li>startDate="2019-02-28",endDate="2019-03-05",period="day"</li>
     * <li>结果为:[2019-02-28, 2019-03-01, 2019-03-02, 2019-03-03, 2019-03-04, 2019-03-05]</li><br>
     * <li>startDate="2019-02-28",endDate="2019-03-25",period="week"</li>
     * <li>结果为:[2019-02-28,2019-03-06, 2019-03-07,2019-03-13, 2019-03-14,2019-03-20,
     * 2019-03-21,2019-03-25]</li><br>
     * <li>startDate="2019-02-28",endDate="2019-05-25",period="month"</li>
     * <li>结果为:[2019-02-28,2019-02-28, 2019-03-01,2019-03-31, 2019-04-01,2019-04-30,
     * 2019-05-01,2019-05-25]</li><br>
     * <li>startDate="2019-02-28",endDate="2020-05-25",period="year"</li>
     * <li>结果为:[2019-02-28,2019-12-31, 2020-01-01,2020-05-25]</li><br>
     */
    public static List<String> listDateStrs(String startDate, String endDate, String period) {
        List<String> result = new ArrayList<>();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_PATTERN);
        LocalDate end = LocalDate.parse(endDate, dateTimeFormatter);
        LocalDate start = LocalDate.parse(startDate, dateTimeFormatter);
        LocalDate tmp = start;
        switch (period) {
            case DAY:
                while (start.isBefore(end) || start.isEqual(end)) {
                    result.add(start.toString());
                    start = start.plusDays(1);
                }
                break;
            case WEEK:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    if (tmp.plusDays(6).isAfter(end)) {
                        result.add(tmp.toString() + "," + end);
                    } else {
                        result.add(tmp.toString() + "," + tmp.plusDays(6));
                    }
                    tmp = tmp.plusDays(7);
                }
                break;
            case MONTH:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    LocalDate lastDayOfMonth = tmp.with(TemporalAdjusters.lastDayOfMonth());
                    if (lastDayOfMonth.isAfter(end)) {
                        result.add(tmp.toString() + "," + end);
                    } else {
                        result.add(tmp.toString() + "," + lastDayOfMonth);
                    }
                    tmp = lastDayOfMonth.plusDays(1);
                }
                break;
            case YEAR:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    LocalDate lastDayOfYear = tmp.with(TemporalAdjusters.lastDayOfYear());
                    if (lastDayOfYear.isAfter(end)) {
                        result.add(tmp.toString() + "," + end);
                    } else {
                        result.add(tmp.toString() + "," + lastDayOfYear);
                    }
                    tmp = lastDayOfYear.plusDays(1);
                }
                break;
            default:
                break;
        }
        return result;
    }

    public static void main(String[] args) {
        System.out.println(getLocalDateTimeStr());
        System.out.println(getLocalDateStr());
        System.out.println(getLocalTimeStr());
        System.out.println(getDayOfWeekStr());
        System.out.println(getDayOfWeekStr(LocalDate.now()));

        System.out.println("========");
        System.out.println(format(LocalDate.now(), UNSIGNED_DATE_PATTERN));

        System.out.println("========");
        System.out.println(parseLocalDateTime("2020-12-13 11:14:12", DATETIME_PATTERN));
        System.out.println(parseLocalDate("2020-12-13", DATE_PATTERN));

        System.out.println("========");
        System.out.println(plus(LocalDateTime.now(), 3, ChronoUnit.HOURS));
        System.out.println(minus(LocalDateTime.now(), 4, ChronoUnit.DAYS));

        System.out.println("========");
        System.out.println(getChronoUnitBetween(LocalDateTime.now(), parseLocalDateTime("2020-12-12 12:03:12", DATETIME_PATTERN), ChronoUnit.MINUTES));
        System.out.println(getChronoUnitBetween(LocalDate.now(), parseLocalDate("2021-12-12", DATE_PATTERN), ChronoUnit.WEEKS));

        System.out.println("========");
        System.out.println(getFirstDayOfYearStr());
        System.out.println(getFirstDayOfYearStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getFirstDayOfYearStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println(getLastDayOfYearStr());
        System.out.println(getLastDayOfYearStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getLastDayOfYearStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println("========");
        System.out.println(getFirstDayOfMonthStr());
        System.out.println(getFirstDayOfMonthStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getFirstDayOfMonthStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println(getLastDayOfMonthStr());
        System.out.println(getLastDayOfMonthStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getLastDayOfMonthStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println("========");
        System.out.println(getFirstDayOfWeekStr());
        System.out.println(getFirstDayOfWeekStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getFirstDayOfWeekStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println(getLastDayOfWeekStr());
        System.out.println(getLastDayOfWeekStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getLastDayOfWeekStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println("========");
        System.out.println(getStartTimeOfDayStr());
        System.out.println(getStartTimeOfDayStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getStartTimeOfDayStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println(getEndTimeOfDayStr());
        System.out.println(getEndTimeOfDayStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN)));
        System.out.println(getEndTimeOfDayStr(parseLocalDateTime("2021-12-12 12:03:12", DATETIME_PATTERN), UNSIGNED_DATETIME_PATTERN));

        System.out.println("========");
        List<String> dateStrs = listDateStrs("2019-01-30", "2020-12-13", YEAR);
        for (String dateStr : dateStrs) {
            System.out.println(dateStr);
        }

        System.out.println("========");
        List<String> dateStrs1 = listDateStrs("2019-01-30", "2020-12-13", MONTH);
        for (String dateStr : dateStrs1) {
            System.out.println(dateStr);
        }

        System.out.println("========");
        List<String> dateStrs2 = listDateStrs("2020-12-01", "2020-12-13", DAY);
        for (String dateStr : dateStrs2) {
            System.out.println(dateStr);
        }
    }

}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值