likes
comments
collection
share

Java8日期处理【上】(超详细、JDK8)

作者站长头像
站长
· 阅读数 23

一:序

Java8日期处理【上】(超详细、JDK8)

关于旧日期如何转换成Java8日期时间对象:\color{#f00}{关于旧日期如何转换成Java8日期时间对象:}关于旧日期如何转换成Java8日期时间对象:

public static void main(String[] args) {
    // 创建一个老日期对象
    Date date = new Date();
    // 将老日期通过Java8的toInstant()方法转为Instant对象。
    Instant instant = date.toInstant();
    // 成功转换成Java8新日期
    LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
    System.out.println(date); // Fri Jul 26 16:34:22 CST 2024
    System.out.println(ldt);  // 2024-07-26T16:34:22.832

    // 从新日期对象中转换成老日期对象
    Instant ins = ldt.toInstant(ZoneOffset.of("+08:00"));
    Date from = Date.from(ins);
    System.out.println(from); // Fri Jul 26 16:34:22 CST 2024
    // 总结:还是新日期看起来舒服得劲;具体的Instant使用请看下文的java8日期介绍
}

为何强烈推荐使用Java8日期:\color{#f00}{为何强烈推荐使用Java8日期:}为何强烈推荐使用Java8日期:

  Java 8 引入了全新的日期和时间 API,位于java.time包中。这个 API 提供了一组全面的类来处理日期、时间、时区、持续时间等,解决了旧版java.util.Datejava.util.Calendar存在的许多问题,比如旧版的线程安全性差、API设计不佳、易用性差等,导致日期和时间的处理变得困难和容易出错。为了解决这些问题,JDK 1.8 引入了全新的日期时间API。所以使用新的日期时间 API 旨在提供更好的日期时间处理解决方案,弥补了之前 API 的不足之处。

关于日常容易混淆的单位转换:\color{#f00}{关于日常容易混淆的单位转换:}关于日常容易混淆的单位转换:

1s【秒】 = 1000ms【毫秒】
1ms【毫秒】 = 1000μs【微秒】
1μs【微秒】 = 1000ns【纳秒】
1ns【纳秒】 = 1000ps【皮秒】
所以转换如下:1秒 = 1000(毫秒) = 1000,000(微秒) = 1000,000,000(纳秒) = 1000,000,000,000(皮秒)

Java1.8日期时间的整体组合:\color{#f00}{Java 1.8 日期时间的整体组合:}Java1.8日期时间的整体组合: Java8日期处理【上】(超详细、JDK8)   上面只列举了大概java.time包下的类,其实还有如:OffsetTime(时间偏差)、Year(年)、YearMonth(年月)、MonthDay(月日);关于这些大家看完文章就会悟出来,下面将不再阐述这四个类。

二:常见枚举和静态方法速查

(一):ChronoField枚举类

  ChronoField类是java.time包中的一个枚举类,它定义了各种日期和时间字段,用于获取和操作日期时间对象的不同部分。常见的字段包括年份、月份、日、小时、分钟、秒等。

点击查看详情:关于TemporalField里的ChronoField枚举值说明
'我只是列举大概哪些类能使用哪个枚举;'
'具体的需要使用每个类中的 boolean isSupported(TemporalField field) 方法校验是否可用。'
'================== 一般在LocalDate、LocalDateTime类中使用 =================='
ChronoField.ALIGNED_WEEK_OF_YEAR:(AlignedWeekOfYear
    一年中的对齐周数,即一年中第几个对齐周。说白了到今天是是一年中的多少个周。
    当前年的天数÷7=周数;若有余数不足一周则算一周。
    计算公式:((一年中的天 - 1) / 7) + 1
ChronoField.ALIGNED_WEEK_OF_MONTH:(AlignedWeekOfMonth
    一个月中的对齐周数,即一个月中第几个对齐周。将每个月的一号排在日历中的每个月最前面计算。
    得出:1~7号是一周、8~14号是二周、15~21是三周、22~28是四周、28~31是五周。
    计算公式:((一个月中的天 - 1) / 7) + 1
ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR:(AlignedDayOfWeekInYear
    一年中的对齐星期几,将11号的日历打头,后面每天换一列,直到排列到指定天数;排在列第一个的便是星期一
    得出:1~7代表周一到周日、8~14代表周一到周日、...、36~43代表周一到周日、...
    计算公式:((一年中的天 - 1) % 7) + 1
ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH:(AlignedDayOfWeekInMonth
    一个月中的对齐星期几,将每个月的一号排在日历中的每个月最前面计算。
    得出:1~7周一到周日、8~14周一到周日、15~21周一到周日、22~28周一到周日、29~31周一到周三
    计算公式:((一个月中天 - 1) % 7) + 1
ChronoField.YEAR:(Year
    返回当前年份。
ChronoField.MONTH_OF_YEAR:(MonthOfYear
    一年中的第几个月,取值范围为112
ChronoField.DAY_OF_MONTH:(DayOfMonth
    一个月中的第几天
ChronoField.DAY_OF_YEAR:(DayOfYear
    一年中的第几天
ChronoField.DAY_OF_WEEK:(DayOfWeek
    一周中的第几天,范围1~71为周一、7为周日)
ChronoField.ERA:(Era
    表示时代,0代表公元前、1代表公元后(公元)
    计算公式:年份 >= 1 ? 1 : 0
ChronoField.YEAR_OF_ERA:(YearOfEra
    返回时代的年份,不像YEAR0或者负数;比如:2024返回20240返回1、-1返回21返回1
ChronoField.EPOCH_DAY:(EpochDay
    表示从标准纪元(1970-01-01)以来的天数。
ChronoField.PROLEPTIC_MONTH:(ProlepticMonth
    表示从公元前1年开始的月数,可以是负数,比如0表示公元前11月。
YEAR_OF_ERA一起使用,可以表示比公历更早的日期。
'================== 一般在LocalTime、LocalDateTime类中使用 =================='
ChronoField.NANO_OF_SECOND:(NanoOfSecond
    表示秒内的纳秒部分。取值范围:0~999999999纳秒
ChronoField.MICRO_OF_SECOND:(MicroOfSecond
    表示秒内的微秒部分。取值范围:0~999999微妙
ChronoField.MILLI_OF_SECOND:(MilliOfSecond
    表示秒内的毫秒部分。取值范围:0~999毫秒
ChronoField.SECOND_OF_MINUTE:(SecondOfMinute
    表示分钟内的秒数。取值范围:0~59
ChronoField.MINUTE_OF_HOUR:(MinuteOfHour
    表示小时内的分钟数。取值范围:0~59
ChronoField.HOUR_OF_AMPM:(HourOfAmPm
    表示上午/下午的小时数(12小时制,12点就是0)。取值范围:0~11时;
ChronoField.CLOCK_HOUR_OF_AMPM:(ClockHourOfAmPm
    表示上午/下午的小时数(12小时制),使用钟表格式。取值范围:1~12时。
ChronoField.HOUR_OF_DAY:(HourOfDay
    表示一天内的小时数(24小时制)。取值范围:0~23时。
ChronoField.CLOCK_HOUR_OF_DAY:(ClockHourOfDay
    表示一天内的小时数,使用钟表格式。取值范围:1~24时。
ChronoField.AMPM_OF_DAY:(AmPmOfDay
    表示上午或下午。取值范围:0AM)或 1PM)。
ChronoField.NANO_OF_DAY:(NanoOfDay
    表示一天内的纳秒偏移量。取值范围:0 ~ 24*60*60*1000000000-1纳秒
ChronoField.MICRO_OF_DAY:(MicroOfDay
    表示一天内的微秒偏移量。取值范围:0 ~ 24*60*60*1000000-1微秒
ChronoField.MILLI_OF_DAY:(MilliOfDay
    表示一天内的毫秒偏移量。取值范围:0~86399999毫秒
ChronoField.SECOND_OF_DAY:(SecondOfDay
    表示一天内的秒偏移量。取值范围:0~86399
ChronoField.MINUTE_OF_DAY:(MinuteOfDay
    表示一天内的分钟偏移量。取值范围:0~1439分钟(即 24 × 60 - 1
'========================= 一般在ZoneOffset类中使用 ========================='
ChronoField.OFFSET_SECONDS:(OffsetSeconds
    用于表示时区偏移量的字段,秒数偏移量;取值范围:64800 ~ -64800秒。
'========================= 一般在Instant类中使用 ========================='
ChronoField.INSTANT_SECONDS:(InstantSeconds
    表示时间线上的秒数。在Instant中,时间被表示为从纪元(1970-01-01T00:00:00Z)开始的秒数。

(二):ChronoUnit枚举类

  ChronoUnit是Java日期时间API中的一个枚举类型,它定义了日期时间的不同单位。

点开查看详情:关于TemporalUnit里的ChronoUnit枚举值说明
ChronoUnit.NANOS:纳秒(Nanos
ChronoUnit.MICROS:微妙(Micros
ChronoUnit.MILLIS:毫秒(Millis
ChronoUnit.SECONDS:秒(Seconds
ChronoUnit.MINUTES:分钟(Minutes
ChronoUnit.HOURS:小时(Hours
ChronoUnit.HALF_DAYS:半天(HalfDays
ChronoUnit.DAYS:天(Days
ChronoUnit.WEEKS:周(Weeks
ChronoUnit.MONTHS:月(Months
ChronoUnit.YEARS:年(Years
ChronoUnit.DECADES:十年(Decades)以十年为一个单位
ChronoUnit.CENTURIES:世纪(Centuries)以百年为一个单位
ChronoUnit.MILLENNIA:千年(Millennia)以千年为一个单位
ChronoUnit.ERAS:时代(Eras)以一个时代为单位
ChronoUnit.FOREVER:永远(Forever
    关于FOREVER是一个特殊的ChronoUnit常量,表示时间上的无限远。
    它通常不用于计算时间间隔,而是用于表示持久性的概念,例如在日期时间API中表示长期持续的时间段或时间戳。

(三):TemporalAdjuster函数式接口

  TemporalAdjuster是函数式接口,用于便捷调整日期时间对象,如调整LocalDate、LocalDateTime等的值。它允许我们执行特定的日期调整操作。我们可以通过静态类的TemporalAdjusters对象快速创建需要的参数对象;如可以快速获取如下时间:当月开始、当月结束、下月开始、本年开始、本年结束、下年开始、下周时间、上周时间等便捷功能。

点开查看详情:关于TemporalAdjuster的函数式接口使用
static TemporalAdjuster  firstDayOfMonth():返回当月的第一天调节器
static TemporalAdjuster  lastDayOfMonth():返回当月的最后一天调节器
static TemporalAdjuster  firstDayOfNextMonth():返回下一个月的第一天调节器
static TemporalAdjuster  firstDayOfYear():返回当年的第一天调节器
static TemporalAdjuster  lastDayOfYear():返回当年的最后一天调节器
static TemporalAdjuster  firstDayOfNextYear():返回下一年的第一天调节器
static TemporalAdjuster  firstInMonth(DayOfWeek dayOfWeek)
    返回当前月初的第一个匹配的星期几的新日期调节器
static TemporalAdjuster  lastInMonth(DayOfWeek dayOfWeek)
    返回当前月末往前推算的第一个匹配的星期几的新日期调节器
static TemporalAdjuster  dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek)
    用于找到当前月中的第ordinal个星期dayOfWeek,并返回调节器。
static TemporalAdjuster  next(DayOfWeek dayOfWeek)
    用于将日期调整为下周的指定星期几,并返回新日期调节器。
    假设当前:2024-06-28(周五);调用xx.next(DayOfWeek.FRIDAY)则会返回:2024-07-05(周五)
static TemporalAdjuster  nextOrSame(DayOfWeek dayOfWeek)
    用于将日期调整为下周或本周相同星期的指定星期几,并返回新日期调节器。
    注意:如果调节的星期正好是当天,则会返回当前日期调节器
    比如现在周五,我调节dayOfWeek是周五,那么我就把当前的周五日期返回即可。
    若不是当前的星期则就会正常的往下推算,返回下周的指定星期。
    假设当前:2024-06-28(周五);调用xx.nextOrSame(DayOfWeek.FRIDAY)则会返回:2024-06-28(周五)
static TemporalAdjuster  previous(DayOfWeek dayOfWeek)
    用于将日期调整为指定星期的上一个出现的日期。(它的功能和 next 方法相反)
static TemporalAdjuster  previousOrSame(DayOfWeek dayOfWeek)
    将日期调整为指定星期的上一个或当前日期的第一次出现。(它的功能和 nextOrSame 方法相反)
static TemporalAdjuster  ofDateAdjuster(UnaryOperatorLocalDate> dateBasedAdjuster)
    它使用提供的UnaryOperatorLocalDate>对象来进行日期调整。可以在这里定义你自己的日期调整逻辑。
    如下示例将日期加2天,并返回对应的调节器:
        // 对date1使用调节器
        LocalDate date1 = LocalDate.of(2024, 6, 28);
        LocalDate with = date1.with(TemporalAdjusters.ofDateAdjuster(e -> e.plusDays(2)));
        // 注意,Lambda表达式的 e参数就是当前的date1日期
        // 它允许我们在这个表达式内做任意操作,扩展性贼强。
        // 所以最终打印date1日期+2天:2024-06-30

三:LocalDate日期对象

  Java 8 提供了一套全新的日期和时间API。在java.time包下有个LocalDate类,它是一个不可变的(线程安全)日期时间对象,表示日期,通常被视为年月日。也可以访问其它日期字段,如:日期和星期。但是该类不存储或表示时间或时区哟。所以它不能代表线上时间的即时信息,也没有附加信息,如偏移或时区。这就导致了它只能存一些固定日期,如生日等信息。   需要了解的是LocalDate创建出的日期是ISO-8601日历系统;有着闰年和平年的规则,适用于日常生活的公历日历系统。对于现在的大多数应用,ISO-8601规则是完全合适的。

(一):方法列表

点开查看详情:LocalDate日期对象方法列表说明
'Ⅰ:关于日期的创建和生成'
    static LocalDate now()
    static LocalDate now(Clock clock)
    static LocalDate now(ZoneId zone)
        上面这3个方法用来创建具体的日期,不同的是不带参数的会创建当地默认时区的日期;
        注:关于带参数创建会在指定的now章节详解。
    static LocalDate of(int year, int month, int dayOfMonth)
    static LocalDate of(int year, Month month, int dayOfMonth)
        分别从年、月、日三个参数来构造一个LocalDate对象。
        注:Month是个枚举,它内部包含12个月的枚举值,就是防止我们日期数值填错。
    static LocalDate ofEpochDay(long epochDay)
        表示自1970-01-01(标准的Java日期起始日)起的天数偏移量。它返回对应于指定天数的LocalDate对象。
        比如传入32,那么就会生成:1970-02-01 的时间。
    static LocalDate ofYearDay(int year, int dayOfYear)
        构建指定年份的天数,并通过天数自动换算成当年的月和日。
        如传入:2024和32 则会创建日期 2024-02-01;
        注:第二个参数的传值范围是参考第一个参数的值,比如2024(闰年)最大可传1~366,平年则最大1~365。
 
'Ⅱ:关于日期的分量获取'
    int get(TemporalField field)
    long getLong(TemporalField field)
        获取指定字段的数值;方法使用都一样,但返回值不一样。因为有的返回值大需要用到Long。
        如通过传入 ChronoField.YEAR 可以获取年份的整数值或者长整数值。
    int getYear()int getMonthValue()int getDayOfMonth()
        获取年、月(1~12)、日的整数方法。
    int getDayOfYear()
        返回一年中的第多少天,返回的最大值也就是 1 ~ 365(平年)/366(闰年)。
    DayOfWeek getDayOfWeek()
        获取日期对应的星期几,返回一个枚举类型DayOfWeek表示星期几。
    Month getMonth()
        获取月份字段,返回一个Month枚举类型。
    Era getEra()
        获取日期的时代(例如公元前或公元后);返回一个Era类型的枚举;BCE=公元前、CE=公元后
    IsoChronology getChronology()
        获取这个日期的时间顺序,即返回ISO日历系统。
 
'Ⅲ:日期检查'
    boolean isBefore(ChronoLocalDate other)
        检查当前日期是否在指定日期之前。true代表是,反之false
    boolean isAfter(ChronoLocalDate other)
        检查当前日期是否在指定日期之后。true代表是,反之false
    boolean equals(Object obj)
        检查当前日期是否等于另一个日期,它只能比较同类型的历法日期对象
    boolean isEqual(ChronoLocalDate other)
        检查当前日期是否等于指定日期,它可以比较不同类型历法的日期,只要最终日期一样即可。
    boolean isLeapYear()
        根据ISO日历系统规则,检查年份是否是闰年。
    boolean isSupported(TemporalField field)
        检查指定的枚举字段是否被支持,比如ChronoField.YEAR可以被支持。
    boolean isSupported(TemporalUnit unit)
        检查指定的时间单位枚举字段是否被支持,比如ChronoUnit.DAYS可以被支持。
 
'Ⅲ:日期的修改方法(一般使用with开头)'
    LocalDate withYear(int year):修改年份。
    LocalDate withMonth(int month):修改月份。
    LocalDate withDayOfMonth(int dayOfMonth):修改日期。
    LocalDate withDayOfYear(int dayOfYear):修改一年中的某一天。
    LocalDate with(TemporalField field, long newValue)
        此方法通过ChronoField枚举可用来修改年月日等信息。
    LocalDate with(TemporalAdjuster adjuster)
        通过TemporalAdjuster函数式接口调整日期,具体可以查看第二章的第三节说明。
        其实LocalDate类也实现了TemporalAdjuster函数式接口。
 
'Ⅳ:日期的增加方法(一般使用plus开头)'
    LocalDate plusYears(long yearsToAdd):增加年份
    LocalDate plusMonths(long monthsToAdd):增加月份
    LocalDate plusWeeks(long weeksToAdd):增加周数,输入1代表加7
    LocalDate plusDays(long daysToAdd):增加天数
    LocalDate plus(long amountToAdd, TemporalUnit unit)
        此方法通过ChronoUnit枚举可用来增加年月日等信息。
    LocalDate plus(TemporalAmount amountToAdd)
        通过TemporalAmount里的Period类来增加一段日期。
 
'Ⅴ:日期的减少方法(一般使用minus开头)'
    LocalDate minusYears(long yearsToSubtract):减少年份
    LocalDate minusMonths(long monthsToSubtract):减少月份
    LocalDate minusWeeks(long weeksToSubtract):减少周数,输入1代表加7
    LocalDate minusDays(long daysToSubtract):减少天数
    LocalDate minus(long amountToSubtract, TemporalUnit unit)
        此方法通过ChronoUnit枚举可用减少年月日等信息。
    LocalDate minus(TemporalAmount amountToSubtract)
        通过TemporalAmount里的Period类来减少一段时期。
 
'Ⅵ:日期的相差方法(一般使用until)'
    Period until(ChronoLocalDate endDateExclusive)
        计算两个日期之间的间隔,并返回一个Period对象;
        表示从当前日期(调用方法的日期)到endDateExclusive日期之间的年、月、日的间隔。
        endDateExclusive参数是一个ChronoLocalDate类型,表示计算间隔的结束日期(不包括这一天)。
    long until(Temporal endExclusive, TemporalUnit unit)
        根据指定的ChronoUnit枚举单位计算直到另一个日期的间隔。
 
'Ⅶ:关于日期的比较'
    int compareTo(ChronoLocalDate other)
        将此日期与另一个日期进行比较。最终会返回:负数、0、正数;
        说明:0代表日期相同、正数代表当前日期比传入的日期大、负数代表当前日期比传入的日期小。
 
'Ⅷ:获取当前日期的各种数值的取值范围'
    ValueRange range(TemporalField field)
        获取当前日期的指定ChronoField枚举字段的有效值的范围。
 
'Ⅸ:转换方法'
    long toEpochDay()
        将此日期转换为大纪元日。就是距197011日差多少天(1970年前则为负数)
    int lengthOfMonth()
        返回调用它的日期对象所表示的月份的长度,即当前日期该月包含的天数。
    int lengthOfYear()
        返回调用它的日期对象所表示的年份的长度,即当前日期该年包含的天数。
 
'Ⅹ:日期字符串转换于日期格式化方法(具体参考日期时间格式化章节)'
    String format(DateTimeFormatter formatter)
        用于将日期对象格式化为一个字符串,使用指定的DateTimeFormatter格式化程序。
    static LocalDate parse(CharSequence text)
        用于从一个文本字符串中解析并获取一个LocalDate实例。
        只能传如:"2024-04-03"字符串
    static LocalDate parse(CharSequence text, DateTimeFormatter formatter)
        使用特定的DateTimeFormatter格式化程序将LocalDate从文本字符串获取LocalDate的实例。
 
'ⅩⅠ:日期组成指定对象'
    LocalDateTime atStartOfDay()
        将此日期和时间00:00结合组成LocalDateTime对象。
    ZonedDateTime atStartOfDay(ZoneId zone)
        将此日期和时间00:00以及传入的时区结合组成ZonedDateTime对象。
    LocalDateTime atTime(LocalTime time)
        在当前日期的基础上传入一个LocalTime对象参数结合,并创建一个新的LocalDateTime对象返回。
    OffsetDateTime atTime(OffsetTime time)
        在当前日期的基础上传入一个OffsetTime对象参数结合,并创建一个新的OffsetDateTime对象返回。
    LocalDateTime atTime(int hour, int minute)
    LocalDateTime atTime(int hour, int minute, int second)
    LocalDateTime atTime(int hour, int minute, int second, int nanoOfSecond)
        在当前日期的基础上设置指定的小时、分钟、秒钟、纳秒参数,并创建一个新的LocalDateTime对象返回。
 
'ⅩⅡ:其它方法'
    static LocalDate from(TemporalAccessor temporal)
        从时间对象获取一个 LocalDate的实例。具体查看from()方法详讲。
    Temporal adjustInto(Temporal temporal)
        调整指定的时间对象与此对象的日期相同。
    〈R〉 R query(TemporalQuery〈R〉 query)
            使用指定的查询查询此日期。具体查看query()方法详讲。

(二):常用方法讲解

1:日期创建和分量获取\color{#CD9B1D}{1:日期创建和分量获取}1:日期创建和分量获取   说明:在公历历法中,没有一个称为公元0年的年份。相反该历法直接跳过了0年这个概念,将年份划分为公元前(BCE)公元后(CE) 两部分。在使用或者获取年份的时候为0时则代表公元前1年,若在使用或者获取年份的时候为-1年时则代表公元前2年(比如-4代表公元前5年),若在使用或者获取年份的时候为1年则代表公元后1年,但通常不说公元后1年,而大部分时刻都叫它公元1年或者1年。

点看查看详情代码:日期创建和分量获取
public static void main(String[] args) {
    // ========================= 几种基本的日期创建方式 =========================
    LocalDate localDate1 = LocalDate.now();
    LocalDate localDate2 = LocalDate.of(2024, 6, 20);
    LocalDate localDate3 = LocalDate.of(2024, Month.JUNE, 23);
    LocalDate localDate4 = LocalDate.ofYearDay(2024, 366);
    LocalDate localDate5 = LocalDate.ofEpochDay(32);
    System.out.println(localDate1); // 2024-06-27
    System.out.println(localDate2); // 2024-06-20
    System.out.println(localDate3); // 2024-06-23
    System.out.println(localDate4); // 2024-12-31
    System.out.println(localDate5); // 1970-02-02
    // ========================= 几种获取分量的方法 =========================
    LocalDate ld1 = LocalDate.of(2024, 6, 20);
    // 基本地获取分量
    String strDate = ld1.getYear()+"年"+ld1.getMonthValue()+"月"+ld1.getDayOfMonth()+"日";
    System.out.println(strDate);    // 打印:2024年6月20日
    System.out.println("一年中过去了多少天:" + ld1.getDayOfYear()); // 一年中过去了多少天:172
    DayOfWeek dayOfWeek = ld1.getDayOfWeek();    // 因为是枚举,可以通过getValue()获取数值
    Month month = ld1.getMonth();                // 因为是枚举,可以通过getValue()获取数值
    System.out.println("当前月份:"+month+" 今天:"+dayOfWeek); // 当前月份:JUNE 今天:THURSDAY
    // 获取日期的时代
    LocalDate ld2 = LocalDate.of(0, 6, 20);
    LocalDate ld3 = LocalDate.of(1, 6, 20);
    LocalDate ld4 = LocalDate.of(-1, 6, 20);
    System.out.println(ld2 + " 打印公元前:" + ld2.getEra()); // 0000-06-20 打印公元前:BCE
    System.out.println(ld3 + " 打印公元后:" + ld3.getEra()); // 0001-06-20 打印公元后:CE
    System.out.println(ld4 + " 打印公元前:" + ld4.getEra()); // -0001-06-20 打印公元前:BCE
}

2:日期的检查判断系列功能\color{#CD9B1D}{2:日期的检查判断系列功能}2:日期的检查判断系列功能

点看查看详情代码:日期的检查判断系列功能
public static void main(String[] args) {
    LocalDate date1 = LocalDate.of(2024, 6, 20);
    LocalDate date2 = LocalDate.of(2024, 6, 23);
    LocalDate date3 = LocalDate.of(2024, 4, 29);
    LocalDate date4 = LocalDate.of(2024, 4, 29);
    // 创建伊斯兰历法规范的日历,当前日历等于我们公历的:2024-04-29
    HijrahDate hijrahDate = HijrahDate.of(1445, 10, 20);
    System.out.println(date3.equals(hijrahDate));
    // 示例代码
    System.out.println("date1日期在date2日期之前:" + date1.isBefore(date2));
    // 打印:date1日期在date2日期之前:true
    System.out.println("date1日期在date2日期之后:" + date1.isAfter(date2));
    // 打印:date1日期在date2日期之前:true
    System.out.println("date3和date4日期是否相等:" + date3.equals(date4));
    // 打印:date3和date4日期是否相等:true
    System.out.println("date3和date1日期是否相等:" + date3.equals(date1));
    // 打印:date3和date1日期是否相等:false
    System.out.println("date3和hijrahDate日期是否相等:" + date3.equals(hijrahDate));
    // 打印:date3和hijrahDate日期是否相等:false (虽然日期一样,但不是同日历类型)
    System.out.println("date3和hijrahDate日期是否相等:" + date3.isEqual(hijrahDate));
    // 打印:date3和hijrahDate日期是否相等:true
    System.out.println("2024是闰年:" + date1.isLeapYear());
    System.out.println("2023不是闰年:" + LocalDate.of(2023, 1, 1).isLeapYear());
    // 校验一些枚举是否可用
    System.out.println("ChronoField.YEAR是否可用:" + date1.isSupported(ChronoField.YEAR));
    System.out.println("ChronoUnit.DAYS是否可用:" + date1.isSupported(ChronoUnit.DAYS));
    // =================== 关于LocalDate中可用的ChronoField枚举类如下 ===================
    // ALIGNED_WEEK_OF_YEAR、ALIGNED_WEEK_OF_MONTH、
    // ALIGNED_DAY_OF_WEEK_IN_YEAR、ALIGNED_DAY_OF_WEEK_IN_MONTH、
    // YEAR、MONTH_OF_YEAR、DAY_OF_MONTH、DAY_OF_YEAR、DAY_OF_WEEK、ERA、YEAR_OF_ERA
    // =================== 关于LocalDate中可用的ChronoField枚举类如下 ===================
    // DAYS、WEEKS、MONTHS、YEARS、DECADES、CENTURIES、MILLENNIA、ERAS
}

3:日期的修改、增加、减少、相差方法\color{#CD9B1D}{3:日期的修改、增加、减少、相差方法}3:日期的修改、增加、减少、相差方法

点看查看详情代码:日期的修改、增加、减少、相差方法
public static void main(String[] args) {
    LocalDate date1 = LocalDate.of(2024, 2, 29);
    LocalDate date2 = LocalDate.of(2023, 6, 23);
    LocalDate date3 = LocalDate.of(2024, 6, 23);
    LocalDate date4 = LocalDate.of(2020, 6, 23);
    // ============================= 日期修改方法 =============================
    System.out.println(date1.withYear(2023));     // 2023-02-28(会自动条件闰年平年的2月天数)
    System.out.println(date1.withMonth(3));       // 2024-03-29
    System.out.println(date1.withDayOfMonth(28)); // 2024-02-28(2月超过29则天报错)
    System.out.println(date1.withDayOfYear(32));  // 2024-02-01
    // System.out.println(date1.with(ChronoField.YEAR,2023)); //同withYear()
    // System.out.println(date1.with(ChronoField.DAY_OF_MONTH,28)); //同withDayOfMonth()
    // 通过TemporalAdjuster函数式接口便捷修改日期,具体参考第二章第三节的TemporalAdjuster说明
    System.out.println(date1.with(TemporalAdjusters.lastDayOfYear()));
    // ============================= 日期增加方法 =============================
    System.out.println(date2.plusYears(2));     // 2025-06-23
    System.out.println(date2.plusMonths(2));    // 2023-08-23
    System.out.println(date2.plusWeeks(1));     // 2023-06-30
    System.out.println(date2.plusDays(2));      // 2023-06-25
    // System.out.println(date2.plus(2, ChronoUnit.YEARS)); //同plusYears()
    // System.out.println(date2.plus(1, ChronoUnit.WEEKS)); //同plusWeeks()
    // 可以通过Period来添加一段日期,具体参考下面篇章的Period类
    // 创建一个时间段,时间段为1年3个月5天;(或全部负数代表减去)
    Period period = Period.of(1, 3, 5);
    System.out.println(date2.plus(period)); // 2024-09-28
    // ============================= 日期减少方法 =============================
    System.out.println(date3.minusYears(2));     // 2022-06-23
    System.out.println(date3.minusMonths(2));    // 2024-04-23
    System.out.println(date3.minusWeeks(1));     // 2024-06-16
    System.out.println(date3.minusDays(2));      // 2024-06-21
    // System.out.println(date3.minus(2, ChronoUnit.YEARS)); //同minusYears()
    // System.out.println(date3.minus(1, ChronoUnit.WEEKS)); //同minusWeeks()
    // 可以通过Period来减少一段日期,具体参考下面篇章的Period类
    System.out.println(date3.minus(period)); // 2023-03-18
    // ============================= 日期相差方法 =============================
    // 计算date1据date2还差多少天;(返回的是Period时间段对象)
    // P代表年月日时间段;M=月、D=天
    // 所以相差3个月25天
    System.out.println(date1.until(date3));  // P3M25D
    // 计算date3据date1还差多少天(返回的是Period时间段对象)
    // 由于date3日期在后面,所以要计算相差,需要往前倒着计算时差
    System.out.println(date3.until(date1));  // P-3M-23D
    // 计算date1据date2还差多少天(返回的是Period时间段对象)
    // 相差3年8个月6天
    System.out.println(date4.until(date1));  // P3Y8M6D
    // 以指定单位来计算时差
    long until1 = date2.until(date1, ChronoUnit.DAYS);
    System.out.println("相差天:" + until1); // 相差天:251
    System.out.println("相差月:" + date2.until(date1, ChronoUnit.MONTHS)); // 相差月:8
    System.out.println("相差年:" + date2.until(date1, ChronoUnit.YEARS));  // 相差年:0
    // 说明:关于时间段的知识请参考下面章节的Period
}

4:日期的比较、范围获取、其它方法\color{#CD9B1D}{4:日期的比较、范围获取、其它方法}4:日期的比较、范围获取、其它方法

点看查看详情代码:日期的比较、范围获取、其它方法
public static void main(String[] args) {
    LocalDate date1 = LocalDate.of(2024, 2, 29);
    LocalDate date2 = LocalDate.of(2024, 2, 29);
    LocalDate date3 = LocalDate.of(2020, 6, 3);
    LocalDate date4 = LocalDate.of(2020, 6, 23);
    // ============================= 日期比较方法 =============================
    System.out.println("相同日期:" + date1.compareTo(date2));  // 相同日期:0
    System.out.println("date1比date3大:" + date1.compareTo(date3)); // date1比date3大:4
    System.out.println("date3比date4大:" + date3.compareTo(date4)); // date3比date4大:-20
    // 日期比较说明:我们不用看具体的返回的数值是多少,看完代码便知道是按照年月日比较,年大则返回年的天数,
    // 若年份一样则比较月份,月份大的则返回月份的月数差;
    // 总结:0代笔日期相同、正数代表当前日期比传入的日期大、负数代表当前日期比传入的日期小
    // ============================= 日期范围获取 =============================
    // 主要获取当前年份的最大可设置的数值
    System.out.println("年份最大范围:" + date1.range(ChronoField.YEAR));
    //      年份最大范围:-999999999 - 999999999
    System.out.println("月份最大范围:" + date1.range(ChronoField.MONTH_OF_YEAR));
    //      月份最大范围:1 - 12
    System.out.println("当前年天数范围:" + date1.range(ChronoField.DAY_OF_YEAR));
    //      当前年天数范围:1 - 366
    ValueRange range = date1.range(ChronoField.DAY_OF_MONTH);
    System.out.println("当前月天数最小值:" + range.getMaximum()); // 当前月天数最小值:29
    System.out.println("当前月天数最大值:" + range.getMinimum()); // 当前月天数最大值:1
    // ============================= 其它方法说明 =============================
    System.out.println("当前月最大天数:" + date1.lengthOfMonth()); // 当前月最大天数:29
    System.out.println("当前年最大天数:" + date1.lengthOfYear());  // 当前年最大天数:366
    System.out.println("获取这个日期的时间顺序:" + date1.getChronology());
    //      获取这个日期的时间顺序:ISO
    System.out.println("距1970年1月1日相差天数:" + date1.toEpochDay());
    //      距1970年1月1日相差天数:19782
}

5:日期的组合和生成对应对象\color{#CD9B1D}{5:日期的组合和生成对应对象}5:日期的组合和生成对应对象

点开查看详情代码:日期的组合和生成对应对象
public static void main(String[] args) {
    // 关于下面不熟悉的对象后面章节有说明
    // 创建当前日期:2024-07-21
    LocalDate ld = LocalDate.of(2024, 7, 21);
    // 将日期和00:00时间组合生成LocalDateTime对象
    System.out.println(ld.atStartOfDay()); // 2024-07-21T00:00
    // 将日期与00:00时间、时区组合生成ZonedDateTime对象
    ZonedDateTime zdt = ld.atStartOfDay(ZoneId.of("GMT+09:00"));
    System.out.println(zdt); // 2024-07-21T00:00+09:00[GMT+09:00]
    // 将日期和时间(时、分、秒、纳秒、LocalTime)组合生成新的LocalDateTime对象;
    System.out.println(ld.atTime(LocalTime.now()));   // 2024-07-21T17:08:10.895
    System.out.println(ld.atTime(10, 20));            // 2024-07-21T10:20
    System.out.println(ld.atTime(10, 20, 30));        // 2024-07-21T10:20:30
    System.out.println(ld.atTime(10, 20, 30, 999));   // 2024-07-21T10:20:30.000000999
    // 将日期和时间偏差(OffsetTime)组合生成新的OffsetDateTime对象
    OffsetTime offsetTime = OffsetTime.of(LocalTime.of(10, 20, 30), ZoneOffset.of("+09:00"));
    System.out.println(offsetTime); // 10:20:30+09:00
    OffsetDateTime offsetDateTime = ld.atTime(offsetTime);
    System.out.println(offsetDateTime); // 2024-07-21T10:20:30+09:00
}

四:LocalTime时间对象

  Java 8 中,LocalTime是用来 表示时间 类的,它和LocalDate表示日期其实差不多,只不过一个表示日期的年月日,另一个表示时间的时分秒和纳秒;而且它们都属于不可变且线程安全的日期时间类;需要注意的它不存储或表示日期或时区,也它不能代表时间线上的即时信息,没有附加信息,如偏移或时区。只适合处理不带时区的时间信息。   那么LocalTime这个类可以用来处理不带时区的时间信息,它适合用于日常应用中不涉及日期的时间处理场景,例如会议时间、计划任务时间等。通过LocalTime也可以方便地进行时间的创建、格式化、解析、比较和修改等操作。

(一):方法列表

点开查看详情:LocalTime时间对象方法列表说明
'1:一些基本的静态属性:'
    static LocalTime MAX:表示最大支持的时间,即23:59:59.999999999。这是一天中最后一刻的时间表示。
    static LocalTime MIN:表示最小支持的时间,即00:00:00。这是一天中最早的时间表示。
    static LocalTime MIDNIGHT:表示午夜开始的时间,即00:00:00。这是一天的开始时刻。
    static LocalTime NOON:表示中午的时间,即12:00:00。这是一天的中间时刻。
 
'2:关于时间的创建和生成'
    static LocalTime now()
    static LocalTime now(Clock clock)
    static LocalTime now(ZoneId zone)
        上面这3个方法用来创建具体的时间,不同的是不带参数的会创建当地默认时区的时间;
        注:关于带参数创建会在指定的now章节详解。
    static LocalTime of(int hour, int minute)
    static LocalTime of(int hour, int minute, int second)
    static LocalTime of(int hour, int minute, int second, int nanoOfSecond)
        从小时、分钟、秒钟、纳秒这几个参数来创建时钟;但是它无法进位哟;
        其中hour最大取值23;minute、second最大取值59;nanoOfSecond最大取值0~999999999
    static LocalTime ofNanoOfDay(long nanoOfDay)
    static LocalTime ofSecondOfDay(long secondOfDay)
        通过纳秒值(nanoOfDay)或秒钟值(secondOfDay)来构建时间;时间可进位;
        比如设置120秒,日期则打印:0时2分;
        注意:它时代表当天已经过去了多少秒或者纳秒;但进位的时间不能超过一天;如秒最多设置86399;
        因为86400为一天24小时;在小时中无法体现;最大只能23:59:59
 
'3:关于时间的分量获取'
    int get(TemporalField field)
    long getLong(TemporalField field)
        获取指定字段的数值;方法使用都一样,但返回值不一样。因为有的返回值大需要用到Long。
        如通过传入 ChronoField.YEAR 可以获取年份的整数值或者长整数值。
    int getHour()、int getMinute()、int getSecond()、int getNano()
        获取小时、分钟、秒钟、纳秒
 
'4:时间检查'
    boolean isBefore(LocalTime other)
        检查当前时间是否在指定时间之前。true代表是,反之false。
    boolean isAfter(LocalTime other)
        检查当前时间是否在指定时间之后。true代表是,反之false。
    boolean equals(Object obj)
        检查当前时间是否等于另一个时间。
    boolean isSupported(TemporalField field)
        检查指定的枚举字段是否被支持,比如ChronoField.MILLI_OF_DAY可以被支持。
    boolean isSupported(TemporalUnit unit)
        检查指定的时间单位枚举字段是否被支持,比如ChronoUnit.NANOS可以被支持。
 
'5:时间的修改方法(一般使用with开头)'
    LocalTime withHour(int hour):修改小时
    LocalTime withMinute(int minute):修改分钟
    LocalTime withSecond(int second):修改秒钟
    LocalTime withNano(int nanoOfSecond):修改纳秒
    LocalTime with(TemporalField field, long newValue)
        此方法通过ChronoField枚举类可用来修改时分秒纳秒等信息。
    LocalTime with(TemporalAdjuster adjuster)
        通过TemporalAdjuster函数式接口调整日期,当前的LocalTime就实现此函数式接口。
        LocalTime的四个常量都可使用。如:时间.with(LocalTime.NOON);设置当天的正午值。
 
'6:时间的增加方法(一般使用plus开头)'
    LocalTime plusHours(long hoursToAdd):增加小时
    LocalTime plusMinutes(long minutesToAdd):增加分钟
    LocalTime plusSeconds(long secondstoAdd):增加秒钟
    LocalTime plusNanos(long nanosToAdd):增加纳秒
    LocalTime plus(long amountToAdd, TemporalUnit unit)
        此方法通过ChronoUnit枚举类可用来增加时分秒纳秒等信息。
    LocalTime plus(TemporalAmount amountToAdd)
        通过TemporalAmount里的Duration类来增加一段时间。
 
'7:时间的减少方法(一般使用minus开头)'
    LocalTime minusHours(long hoursToSubtract):减少小时
    LocalTime minusMinutes(long minutesToSubtract):减少分钟
    LocalTime minusSeconds(long secondsToSubtract):减少秒钟
    LocalTime minusNanos(long nanosToSubtract):减少纳秒
    LocalTime minus(long amountToSubtract, TemporalUnit unit)
        此方法通过ChronoUnit枚举可用来减少时分秒纳秒等信息。
    LocalTime minus(TemporalAmount amountToSubtract)
        通过TemporalAmount里的Duration类来减少一段时间。
 
'8:时间的相差方法(一般使用until)'
    long until(Temporal endExclusive, TemporalUnit unit)
        根据指定的ChronoUnit枚举单位计算直到另一个时间的间隔。
 
'9:关于日期的比较'
    int compareTo(LocalTime other)
        将此时间与另一个时间进行比较。最终会返回:负数、0、正数;
        说明:0代表时间相同、正数代表当前时间比传入的时间大、负数代表当前时间比传入的时间小。
 
'10:获取当前时间的各种数值的取值范围'
    ValueRange range(TemporalField field)
        获取当前时间的指定ChronoField枚举字段的有效值的范围。
 
'11:日期字符串转换于日期格式化方法(具体参考日期时间格式化章节)'
    String format(DateTimeFormatter formatter)
        用于将时间对象格式化为一个字符串,使用指定的DateTimeFormatter格式化程序。
    static LocalTime parse(CharSequence text)
        用于从一个文本字符串中解析并获取一个LocalTime实例。
        只能传如:"12:25:30" 字符串
    static LocalTime parse(CharSequence text, DateTimeFormatter formatter)
        使用特定的DateTimeFormatter格式化程序将LocalDate从文本字符串获取LocalTime的实例。
 
'12:其它方法'
    long toNanoOfDay()
        将时间转换为一天中的纳秒值
    int toSecondOfDay()
        将时间转换为一天中的秒钟值
    LocalTime truncatedTo(TemporalUnit unit)
        用于将当前时间对象按照指定的粒度单位进行截断操作,并返回一个新的LocalTime对象。
        比如:10:30:25 按照分钟粒度截断则为 10:30
 
'13:时间组成指定对象'
LocalDateTime atDate(LocalDate date)
    将此时间和传入的日期对象结合组成LocalDateTime对象。
OffsetTime atOffset(ZoneOffset offset)
    在当前时间的基础上传入一个ZoneOffset(时区偏移)参数结合,并创建一个新的OffsetTime对象返回。
 
'14:其它方法'
    static LocalTime from(TemporalAccessor temporal)
        从时间对象获取一个 LocalTime的实例。具体查看from()方法详讲。
    Temporal adjustInto(Temporal temporal)
        调整指定的时间对象与此对象具有相同的时间。
    〈R〉 R query(TemporalQuery〈R〉 query)
        此时使用指定的查询进行查询。具体查看query()方法详讲。

(二):常用方法讲解

1:时间创建和分量获取\color{#CD9B1D}{1:时间创建和分量获取}1:时间创建和分量获取

点看查看详情代码:时间创建和分量获取
public static void main(String[] args) {
    // ========================= 几种基本的时间创建方式 =========================
    // 生成一个默认时区的时间;时:分:秒.毫秒
    LocalTime time1 = LocalTime.now();
    // 生成一个只要小时和分钟的时间
    LocalTime time2 = LocalTime.of(23, 59);
    // 生成一个有小时、分钟、秒钟的时间
    LocalTime time3 = LocalTime.of(23, 59, 59);
    // 最全的;生成的时间有小时、分钟、秒钟、纳秒
    LocalTime time4 = LocalTime.of(23, 59, 59, 999999999);
    // 通过纳秒生成时间(当天已经过去了320纳秒)
    LocalTime time5 = LocalTime.ofNanoOfDay(320);
    // 通过纳秒生成时间;但这个纳秒达到进位标准,所以为1秒;
    LocalTime time6 = LocalTime.ofNanoOfDay(1000000000L);
    // 生成一个120秒的时间;进位则为2分钟(当天已经过去了120秒)
    LocalTime time7 = LocalTime.ofSecondOfDay(120);
    System.out.println(time1); // 20:56:30.587
    System.out.println(time2); // 20:30
    System.out.println(time3); // 23:59:59
    System.out.println(time4); // 23:59:59.999999999
    System.out.println(time5); // 00:00:00.000000320
    System.out.println(time6); // 00:00:01
    System.out.println(time7); // 00:02
    // ========================= 几种获取分量的方法 =========================
    // 创建一个基本日期
    LocalTime time8 = LocalTime.of(12, 30, 25, 90300);
    // 获取时分秒和纳秒
    System.out.println("小时:" + time8.getHour());    // 小时:12
    System.out.println("分钟:" + time8.getMinute());  // 分钟:30
    System.out.println("秒钟:" + time8.getSecond());  // 秒钟:25
    System.out.println("纳秒:" + time8.getNano());    // 纳秒:90300
    System.out.println(time8.get(ChronoField.NANO_OF_SECOND));  // 相当getNano()
    System.out.println(time8.get(ChronoField.AMPM_OF_DAY));     // 打印 1;代表下午
}

2:时间的检查判断系列功能\color{#CD9B1D}{2:时间的检查判断系列功能}2:时间的检查判断系列功能

点看查看详情代码:时间的检查判断系列功能
public static void main(String[] args) {
    LocalTime time1 = LocalTime.of(23, 59, 59);
    LocalTime time2 = LocalTime.of(20, 30, 50);
    LocalTime time3 = LocalTime.of(20, 30, 50);
    // 判断日期
    System.out.println("time1时间在time2时间之后:" + time1.isAfter(time2));
    //      time1时间在time2时间之后:true
    System.out.println("time2时间在time1时间之后:" + time2.isAfter(time1));
    //      time2时间在time1时间之后:false
    System.out.println("time2时间在time1时间之前:" + time2.isBefore(time1));
    //      time2时间在time1时间之前:true
    System.out.println("time2和time3时间相同:" + time2.equals(time3));
    //      time2和time3时间相同:true
    // 校验是否可用如下枚举
    System.out.println(time1.isSupported(ChronoUnit.NANOS));            // 打印:true
    System.out.println(time1.isSupported(ChronoField.MILLI_OF_DAY));    // 打印:true
    // =================== 关于LocalTime中可用的ChronoField枚举如下 ===================
    // NANO_OF_SECOND、NANO_OF_DAY、MICRO_OF_SECOND、MICRO_OF_DAY、MILLI_OF_SECOND、
    // MILLI_OF_DAY、SECOND_OF_MINUTE、SECOND_OF_DAY、MINUTE_OF_HOUR、MINUTE_OF_DAY、
    // HOUR_OF_AMPM、CLOCK_HOUR_OF_AMPM、HOUR_OF_DAY、CLOCK_HOUR_OF_DAY、AMPM_OF_DAY
    // =================== 关于LocalTime中可用的ChronoUnit枚举如下 ===================
    // NANOS、MICROS、MILLIS、SECONDS、MINUTES、HOURS、HALF_DAYS
}

3:时间的修改、增加、减少、相差方法\color{#CD9B1D}{3:时间的修改、增加、减少、相差方法}3:时间的修改、增加、减少、相差方法

点看查看详情代码:时间的修改、增加、减少、相差方法
public static void main(String[] args) {
    LocalTime time1 = LocalTime.of(23, 59, 59);
    LocalTime time2 = LocalTime.of(20, 30, 50);
    LocalTime time3 = LocalTime.of(10, 30, 30);
    // ============================ 时间的修改方法 ============================
    System.out.println(time1.withHour(22));     // 22:59:59
    System.out.println(time1.withMinute(22));   // 23:22:59
    System.out.println(time1.withSecond(15));   // 23:59:15
    System.out.println(time1.withNano(999999999));  // 23:59:59.999999999
    // System.out.println(time1.with(ChronoField.MINUTE_OF_HOUR, 22)); //同withMinute(22)
    // System.out.println(time1.with(ChronoField.SECOND_OF_MINUTE, 15)); //同withSecond(15)
    // 通过LocalTime静态常量快速设置时间;
    System.out.println(time1.with(LocalTime.MAX));      // 23:59:59.999999999
    System.out.println(time1.with(LocalTime.MIN));      // 00:00
    System.out.println(time1.with(LocalTime.MIDNIGHT)); // 00:00
    System.out.println(time1.with(LocalTime.NOON));     // 12:00
    // ============================ 时间的增加方法 ============================
    System.out.println(time2.plusHours(4)); // 00:30:50(时间超出,也无法进位,只能是0)
    System.out.println(time2.plusMinutes(1));   // 20:31:50
    System.out.println(time2.plusSeconds(1));   // 20:30:51
    System.out.println(time2.plusNanos(3000));   // 20:30:50.000003
    // System.out.println(time2.plus(1, ChronoUnit.MINUTES)); // 同 plusMinutes(1)
    // 可以通过Duration来添加一段时间,具体参考下面篇章的Duration类
    Duration duration = Duration.ofMinutes(30);  // 创建一个时间段为30分钟
    System.out.println(time2.plus(duration));    // 21:00:50
    // ============================ 时间的减少方法 ============================
    System.out.println(time3.minusHours(1));        // 09:30:30
    System.out.println(time3.minusMinutes(1));    // 10:29:30
    System.out.println(time3.minusSeconds(2));   // 10:30:28
    System.out.println(time3.minusNanos(50));
    //      打印:10:30:29.999999950;由于纳秒为0则需要借1秒钟来充当纳秒减50
    // System.out.println(time3.minus(1, ChronoUnit.MINUTES)); // 同 minusMinutes(1)
    // 可以通过Duration来减少一段时间,具体参考下面篇章的Duration类
    System.out.println(time3.minus(duration)); // 10:00:30
    // ============================ 时间的间隔方法 ============================
    System.out.println(time2.until(time3, ChronoUnit.HOURS));  // 间隔-10小时
    System.out.println(time3.until(time2, ChronoUnit.MINUTES));// 间隔600分钟
    // 因为 10:30 ~ 20:30 中间相差 600 分钟
}

4:时间的比较、范围获取、其它方法\color{#CD9B1D}{4:时间的比较、范围获取、其它方法}4:时间的比较、范围获取、其它方法

点看查看详情代码:时间的比较、范围获取、其它方法
public static void main(String[] args) {
    LocalTime time1 = LocalTime.of(23, 59, 59);
    LocalTime time2 = LocalTime.of(20, 30, 50);
    LocalTime time3 = LocalTime.of(10, 30, 30);
    LocalTime time4 = LocalTime.of(10, 30, 30);
    // ============================ 时间的比较方法 ============================
    System.out.println("time1比time2时间大:"+time1.compareTo(time2)); //time1比time2时间大:1
    System.out.println("time2比time1时间小:"+time2.compareTo(time1)); //time1比time2时间大:-1
    System.out.println("time3和time4时间一样:"+time3.compareTo(time4)); //time3和time4时间一样:0
    // ============================ 时间的范围获取方法 ============================
    System.out.println(time1.range(ChronoField.HOUR_OF_DAY));   // 小时范围:0 - 23
    System.out.println(time1.range(ChronoField.MINUTE_OF_HOUR));// 分钟范围:0 - 59
    // ============================ 时间的其它方法 ============================
    System.out.println(time4.toNanoOfDay());    // 37830000000000(正好等于10:30:30)
    System.out.println(time4.toSecondOfDay());  // 37830(正好等于10:30:30)
    System.out.println("将时间截断到分钟:" + time4.truncatedTo(ChronoUnit.MINUTES));
    // 将时间截断到分钟:10:30
    System.out.println("将时间截断到小时:" + time4.truncatedTo(ChronoUnit.HOURS));
    // 将时间截断到小时:10:00
}

5:时间的组合和生成对应对象\color{#CD9B1D}{5:时间的组合和生成对应对象}5:时间的组合和生成对应对象

点开查看详情代码:时间的组合和生成对应对象
public static void main(String[] args) {
    // 关于下面不熟悉的对象后面章节有说明
    // 创建当前日期:2024-07-21
    LocalDate ld = LocalDate.of(2024, 7, 21);
    // 创建当前时间:12:20:30
    LocalTime lt = LocalTime.of(12, 20, 30);
    // 将当前时间与日期相结合生成LocalDateTime对象
    System.out.println(lt.atDate(ld));  // 2024-07-21T12:20:30
    // 将当前时间与时区偏差结合生成时间偏差对象
    System.out.println(lt.atOffset(ZoneOffset.of("+09:00"))); // 12:20:30+09:00
}

五:LocalDateTime日期时间对象

  LocalDateTime 是 Java 8 引入的 表示日期时间类,位于java.time包下。它结合了日期(年、月、日)和时间(时、分、秒、纳秒)信息,并且是不可变的线程安全的日期时间对象类;它还没有时区信息,所以适用于不依赖于特定时区的时间处理,例如表示某个具体的日历时间点。   这个日期时间类提供了简单而强大的日期时间操作功能,适用于大多数本地日期时间需求,特别是当不需要考虑时区的时候使用。通过合理使用它的方法,可以方便地处理和操作日期时间信息。

(一):方法列表

  由于前面说了 LocalDateLocalTime 这两个类的铺垫;所以 LocalDateTime 类看起来不会太难懂;下面我针对一些不好理解的方法进行说明;其它方式只用来基本查阅。

'1:一些基本的静态属性:(关于日期时间可设置的最大值和最小值)'
    static LocalDateTime MAX
        最大可设置日期:'+ 999999999-12-31T23:59:59.999999999'
    static LocalDateTime MIN
        最小可设置日期:'-999999999-01-01T00:00:00'
'2:关于日期时间的创建和生成'
    static LocalDateTime now()
    static LocalDateTime now(Clock clock)
    static LocalDateTime now(ZoneId zone)
        上面这3个方法用来创建具体的日期时间,不同的是不带参数的会创建当地默认时区的日期时间;
        注:关于带参数创建会在指定的now章节详解。
    static LocalDateTime of(LocalDate date, LocalTime time)
        通过日期类和时间类来组装成一个日期时间对象
    static LocalDateTime of(int year,int month,int dayOfMonth,int hour,int minute)
    static LocalDateTime of(int year,int month,int dayOfMonth,int hour,int minute,int second)
    static LocalDateTime of(int year,int month,int dayOfMonth,int hour,int minute,int second,
                            int nanoOfSecond)
    static LocalDateTime of(int year,Month month,int dayOfMonth,int hour,int minute)
    static LocalDateTime of(int year,Month month,int dayOfMonth,int hour,int minute,int second)
    static LocalDateTime of(int year,Month month,int dayOfMonth,int hour,int minute,int second,
                            int nanoOfSecond)
        通过不同的年、月、日、时、分、秒、纳秒来创建日期时间对象
    static LocalDateTime ofEpochSecond(long epochSecond, int nanoOfSecond, ZoneOffset offset)
1970-01-01 00:00:00(零时区)的时间开始的指定秒数来获得一个日期时间类。时区偏移后面篇章说明。
    static LocalDateTime ofInstant(Instant instant, ZoneId zone)
        从Instant和区域ID获取一个LocalDateTime的实例。这两个类后面篇章说明。
'3:关于日期时间的分量获取'
    getYear()、getMonthValue()、getDayOfMonth()、getHour()、
    getMinute()、getSecond()、getNano()、getDayOfYear()
        获取int类型的 年、月(1~12)、日、时、分、秒、纳秒、一年中的第几天。
    int get(TemporalField field)
        获取指定字段的数值;方法使用都一样,但返回值不一样。因为有的返回值大需要用到Long。
        如通过传入 ChronoField.YEAR 可以获取年份的整数值或者长整数值。
    Month getMonth()
        获取月份字段,返回一个Month枚举类型。
    DayOfWeek getDayOfWeek()
        获取日期对应的星期几,返回一个枚举类型DayOfWeek表示星期几。
'4:日期检查'
    boolean isAfter(ChronoLocalDateTime<?> other)
        检查当前日期时间是否在指定日期时间之前。true代表是,反之false
    boolean isBefore(ChronoLocalDateTime<?> other)
        检查当前日期时间是否在指定日期时间之后。true代表是,反之false
    boolean isEqual(ChronoLocalDateTime<?> other)
        检查当前日期时间是否等于指定日期时间,它可以比较不同类型历法的日期时间,只要最终日期时间一样即可。
    boolean equals(Object obj)
        检查当前日期时间是否等于另一个日期,它只能比较同类型的历法日期时间对象
    boolean isSupported(TemporalUnit unit)
    boolean isSupported(TemporalField field)
        检查指定的ChronoUnit枚举字段和ChronoField枚举字段是否被支持。
'5:日期时间的修改、增加、减少、相差'
    '注:修改一般以with开头、增加一般以plus开头、减少一般以minus开头、相差一般以until开头'
    '注:关于修改、增加、减少、相差则去参考LocalDate和LocalTime,他们的方法这里都有'
    '注:关于日期的相差只有 long until(Temporal endExclusive, TemporalUnit unit)方法'
'6:关于日期时间的比较'
    int compareTo(ChronoLocalDateTime<?> other)
        将此时间与另一个时间进行比较。最终会返回:负数、0、正数;
        说明:0代表时间相同、正数代表当前时间比传入的时间大、负数代表当前时间比传入的时间小。
'7:获取当前时间时间的各种数值的取值范围'
    ValueRange range(TemporalField field)
        获取当前时间的指定ChronoField枚举字段的有效值的范围。
'8:日期时间拆分'
    LocalDate toLocalDate()、LocalTime toLocalTime()
        将日期拆分成 LocalDate 或者 LocalTime
'9:日期时间截断'
    LocalDateTime truncatedTo(TemporalUnit unit)
        用于将当前日期时间对象按照指定的粒度单位进行截断操作,并返回一个新的LocalDateTime对象。
        比如:2024-07-04T17:33:13.914 按照小时粒度截断则为 2024-07-04T17:00
'10:日期时间字符串转换于日期时间格式化方法(具体参考日期时间格式化章节)'
    String format(DateTimeFormatter formatter)
    static LocalDateTime parse(CharSequence text)
    static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter)
'11:日期时间组成指定对象'
    ZonedDateTime atZone(ZoneId zone)
    OffsetDateTime atOffset(ZoneOffset offset)
        将此日期时间和传入的时区ID(ZoneId)或时区偏移(ZoneOffset)组合成指定的对象。
        具体的这两个对象下文会专门介绍。
'12:其它方法'
    static LocalDateTime from(TemporalAccessor temporal)
        从时间对象获取一个LocalDateTime的实例。具体查看from()方法详讲。
    Temporal adjustInto(Temporal temporal)
        调整指定的时间对象与此对象具有相同的日期和时间。
    <R> R query(TemporalQuery<R> query)
        使用指定的查询查询此日期时间。具体查看query()方法详讲。

(二):常用方法使用

  大部分方法在LocalDate和LocalTime中已经讲过了,在LocalDateTime中基本上都可以使用。

public static void main(String[] args) {
    // 创建一个以年、月、日、时、分、秒、纳秒来构建一个日期
    LocalDateTime dt1 = LocalDateTime.of(2024, 7, 5, 12, 30, 25, 6900);
    // 以1970-01-01 00:00:00(零时区)日期往后加指定的秒和纳秒来构建日期时间
    LocalDateTime dt2 = LocalDateTime.ofEpochSecond(1720153825, 20, ZoneOffset.of("+08:00"));
    System.out.println("日期1:" + dt1);   // 日期1:2024-07-05T12:30:25.000006900
    System.out.println("日期2:" + dt2);   // 日期2:2024-07-05T12:30:25.000000020
    System.out.println(dt2.getYear()+"-"+dt2.getMonthValue()+"-"+dt2.getDayOfMonth());//2024-7-5
    // 说明:关于如下ChronoField的枚举字段必须使用getLong()方法获取:
    System.out.println(dt1.getLong(ChronoField.NANO_OF_DAY));       // 45025000006900
    System.out.println(dt1.getLong(ChronoField.MICRO_OF_DAY));      // 45025000006
    System.out.println(dt2.getLong(ChronoField.EPOCH_DAY));         // 19909
    System.out.println(dt2.getLong(ChronoField.PROLEPTIC_MONTH));   // 24294
    // 日期时间加减
    System.out.println(dt1.plusDays(5));    // 2024-07-10T12:30:25.000006900
    System.out.println(dt1.minusDays(5));   // 2024-06-30T12:30:25.000006900
}

六:Period时间段(日期)

  Java 8 中提出了 Period 时间段的类,它位于java.time包中,用于 表示日期间隔。主要用于计算和处理两个日期之间的差异,例如几年、几个月、几天等这样的时间段。注意的是Period对象是不可变的,一旦创建就不能被修改。这意味着每次修改操作都会返回一个新的Period实例,从而确保线程安全性。   总结:Period类适合用于计算和表达人类习惯的日期间隔,例如计算两个日期之间的差距,或者在日期上进行简单的加减操作。它通常用于处理不需要精确到时分秒的日期计算需求。

(一):方法列表

点开查看详情:Period时间段方法列表
'1:一些基本的静态属性:'
    static Period ZERO
        通过Period.ZERO静态变量可以构建一个为0的时间段。
'2:关于时间段Period的创建'
    static Period ofYears(int years)
    static Period ofMonths(int months)
    static Period ofDays(int days)
    static Period ofWeeks(int weeks)
    static Period of(int years, int months, int days)
        通过传入年数、月数、天数、周数或直接of传入年月日来构建指定的时间段;
        注:传入的数值无法实现进位,如传入31天,系统无法将31天转换成一个月,
            因为有的月还有30天呢?所以干脆都不转换。
'3:关于时间段分量获取'
    IsoChronology getChronology()
        获得这个时期的年表,返回的都是ISO日历系统。
    getYears()、getMonths()、getDays()
        获取时间段上int类型的计数器:年数、月数、天数
    List<TemporalUnit> getUnits()
        获得类定义的所有计数器,也就是返回这些:YEARS、MONTHS、DAYS
    long get(TemporalUnit unit)
        获取时间段上面的指定数值,通过ChronoUnit枚举类来获取,可选枚举:YEARS、MONTHS、DAYS
'4:修改时间段上指定计数器值(以with开头的基本上都是修改)'
    Period withYears(int years)
    Period withMonths(int months)
    Period withDays(int days)
        修改时间段上的年、月、日的值,并返回全新的Period对象。
'5:增加时间段上指定计数器值(以plus开头的基本上都是增加)'
    Period plusYears(long yearsToAdd)
    Period plusMonths(long monthsToAdd)
    Period plusDays(long daysToAdd)
        增加时间段上的年、月、日的值,并返回全新的Period对象。
    Period plus(TemporalAmount amountToAdd)
        在当前的时间段上再加上指定的时间段;
        如当前时间段:P2Y3M10D;传入的时间段:P2Y3M60D;最终构建时间段为:P4Y6M70D
'6:减少时间段上指定计数器值(以minus开头的基本上都是减少)'
    Period minusYears(long yearsToSubtract)
    Period minusMonths(long monthsToSubtract)
    Period minusDays(long daysToSubtract)
        减少时间段上的年、月、日的值,并返回全新的Period对象。
    Period minus(TemporalAmount amountToSubtract)
        在当前的时间段上再减去指定的时间段;
'7:时间段比较,是否相同'
    boolean equals(Object obj)
        判断两个Period对象是否相等。
'8:计算两个日期的差值'
    static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
        用于计算两个LocalDate对象之间的时间段(间隔)。
        方法返回一个Period对象,表示从startDateInclusive到endDateExclusive之间的年、月、日差异。
'9:其它方法'
    boolean isNegative()
        判断Period对象是否包含负数;时间段年月日其中一个计数器为负数则都为true
    boolean isZero()
        判断Period对象是否为0,三个计数器都为0才是true
    Period normalized()
        规范时间段,对年和月进行标准化处理。如:P2Y25M60D,规范后被转为:P4Y1M60D。
        注:只能规范月和年,无法规范天,因为每个月天不固定。
    Period negated()
        时间段反转,将计数器中的年、月、日都取反。如:P2Y-5M-36D,规范后被转为:P-2Y5M36D。
        反转时间段就是由正变负,由负变正。
    Temporal addTo(Temporal temporal)
    Temporal subtractFrom(Temporal temporal)
        以某个日期为基础 向后推一段时间 或 向前推一段时间,并将计算好的日期添加到指定的Temporal对象中。
        Temporal是一个广义的接口,代表了可以读取或操作日期时间的对象,比如LocalDate、LocalDateTime等。
    long toTotalMonths()
        获得该时间段总共包含多少个月(注:该方法计算不一定准确)
        注:此方法会获取年和月,但天数无法获取转换成月,因为每个月的天数不一样,无法转换。
    Period multipliedBy(int scalar)
        用于将当前的时间段乘以指定的倍速。时间段的每个计数器(年、月、日)都乘以指定的倍速。
'10:补充'
    static Period from(TemporalAmount amount)
        从时间量获取一个Period的实例。具体查看from()方法详讲。
    static Period parse(CharSequence text)
        从一个文本字符串获取一个Period ,只能传入格式:PnYnMnD。

(二):常用方法讲解

点开查看详情:Period时间段方法基本说明
public static void main(String[] args) {
    // =========================== 构建时间段Period对象 ===========================
    Period period1 = Period.ofYears(10);
    Period period2 = Period.ofMonths(24);
    Period period3 = Period.ofDays(367);
    Period period4 = Period.ofWeeks(2);     // 设置2周,会默认转成14天
    Period period5 = Period.of(2, 3, 10);
    Period period6 = Period.of(-2, -3, -10);
    System.out.println("年构造Period:" + period1);     // 年构造Period:P10Y
    System.out.println("月构造Period:" + period2);     // 月构造Period:P24M
    System.out.println("天构造Period:" + period3);     // 天构造Period:P367D
    System.out.println("周构造Period:" + period4);     // 周构造Period:P14D
    System.out.println("年月日构造Period:" + period5);  // 年月日构造Period:P2Y3M10D
    System.out.println(period6);                       // 负数的话则代表时间段之前:P-2Y-3M-10D
    // 以构造方式创建出的Period对象,除了星期会被自动转换成天以外,
    // 年月日之间不会自动完成转换操作,即使设置367天,也就是367天,不会转成1年这样
    // 说明:为什么打印(默认调用toString())的是如:P2Y3M10D
    // P代表Period、Y代表年份、M代表月份、D代表天数
    // ============================== 时间段分量获取 ==============================
    Period p7 = Period.of(5, 25, 10);
    // System.out.println("P"+p7.getYears()+"Y"+p7.getMonths()+"M"+p7.getDays()+"D");
    // p7.get(ChronoUnit.DAYS) 就等于 p7.getYears()
    // =========================== 时间段修改、增加、减少 ===========================
    Period p8 = Period.of(5, 25, 10);
    Period p9 = Period.of(5, 25, 20);
    System.out.println("修改后打印:" + p8.withDays(600));  // 修改后打印:P5Y25M600D
    System.out.println("增加后打印:" + p8.plusDays(20));   // 增加后打印:P5Y25M30D
    System.out.println("减少后打印:" + p8.minusDays(30));  // 减少后打印:P5Y25M-20D
    System.out.println("将两个时间段加一起:" + p8.plus(p9)); // 将两个时间段加一起:P10Y50M30D
    // =============================== 时间段比较和间隔 =============================
    System.out.println("p8与p9是否相同:" + p8.equals(p9)); // p8与p9是否相同:false
    // 以计算的方式来获得时间差,2个日期的差值
    LocalDate ld1 = LocalDate.of(2023, 11, 10);
    LocalDate ld2 = LocalDate.of(2024, 12, 12);
    System.out.println(Period.between(ld1, ld2)); // P1Y1M2D
    System.out.println(Period.between(ld2, ld1)); // P-1Y-1M-2D
    // ============================ 时间段的其它方法示例 ============================
    Period p10 = Period.of(4, -25, 90);
    System.out.println(p10.normalized());   // P1Y11M90D
    // 说明:-25个月其实等于-2年1个月;4年 减 -2年1个月 等于 1年11个月,其中多个一个-1月从年借位计算。
    System.out.println("反转:" + p10.negated());  // 反转:P-4Y25M-90D
    // 以某个日期往后推算指定时间段
    LocalDate ld = LocalDate.of(2024, 6, 10);
    Period period = Period.of(0, 2, 30);
    LocalDate localDate = (LocalDate) period.addTo(ld);  // 2024-09-09
    System.out.println(localDate);
}

(三):关于Period时间段总结

  1. 以构造of方法创建出的Period对象,年月日之间不会自动完成转换,即不会有“进位”和“退位”效果。
  2. 以构造方式创建出的Period对象,调用其toTotalMonths()不一定能够准确的计算出该对象所代表的时间段中包含的月数,因为计算过程中并没有考虑“日计数器”上的值。
  3. normalized()方法只能在年和月之间完成换算,并不能把天换算成月,即使天数已经超过了31。

  说明:造成以上三种情况的原因可以统一归结为:日与月之间没有确定的换算标准。

七:Duration时间段(时间)

  与Period时间段类不同的是,Duration类用于 表示时间间隔,通常用于处理时间的差异,例如秒、纳秒等单位。主要用于精确的时间计算,可以跨越日期的边界,适合于需要精确到时分秒的时间间隔。需要注意的是Duration对象也是不可变的,类似于Period,因此在进行任何修改操作时会返回一个新的Duration实例,从而确保线程安全性。   总结:当我们需要精确计算时间间隔的场景时,例如测量两个时间点之间的精确时间差、在时间戳上进行精确地加减操作等。

(一):方法列表

点开查看详情:Duration时间段方法列表
'1:一些基本的静态属性:'
    static Duration ZERO
        通过Duration.ZERO静态变量可以构建一个为0的时间段。
'2:关于时间段Duration的创建'
    static Duration ofDays(long days)
    static Duration ofHours(long hours)
    static Duration ofMinutes(long minutes)
    static Duration ofSeconds(long seconds)
    static Duration ofMillis(long millis)
    static Duration ofNanos(long nanos)
    static Duration ofSeconds(long seconds, long nanoAdjustment)
        通过天、小时、分钟、秒、毫秒、纳秒、秒和纳秒组合来创建时间段。
    static Duration of(long amount, TemporalUnit unit)
        通过ChronoUnit枚举类属性构建指定单位的时间段。
'3:关于时间段分量获取'
    int getNano()
        返回时间段中的纳秒值;只有设置了才能获取。若纳秒进位了,则进位的部分无法获取。
    long getSeconds()
        将时间段的时分秒转换为秒钟;并返回转换后的总秒数;不考虑纳秒。
    List<TemporalUnit> getUnits()
        获得类定义的所有计数器,也就是返回这些:Seconds, Nanos
    long get(TemporalUnit unit)
        获取时间段上面的指定数值,通过ChronoUnit枚举类来获取,可选枚举:Seconds, Nanos
'4:修改时间段上指定计数器值(以with开头的基本上都是修改)'
    Duration withSeconds(long seconds)
    Duration withNanos(int nanoOfSecond)
        修改时间段上的秒和纳秒的值,并返回全新的Duration对象。
'5:增加时间段上指定计数器值(以plus开头的基本上都是增加)'
    Duration plusDays(long daysToAdd)
    Duration plusHours(long hoursToAdd)
    Duration plusMinutes(long minutesToAdd)
    Duration plusSeconds(long secondsToAdd)
    Duration plusMillis(long millisToAdd)
    Duration plusNanos(long nanosToAdd)
        向持续的时间对象添加指定的天数、小时、分钟、秒钟、毫秒、纳秒;
        并返回一个新的持续时间对象,表示原始持续时间加上指定的时间。
    Duration plus(Duration duration)
        在当前的时间段上再加上指定的时间段;
    Duration plus(long amountToAdd, TemporalUnit unit)
        向持续的时间对象添加指定单位的数值;具体单位通过ChronoUnit枚举获取
'6:减少时间段上指定计数器值(以minus开头的基本上都是减少)'
    Duration minusDays(long daysToSubtract)
    Duration minusHours(long hoursToSubtract)
    Duration minusMinutes(long minutesToSubtract)
    Duration minusSeconds(long secondsToSubtract)
    Duration minusMillis(long millisToSubtract)
    Duration minusNanos(long nanosToSubtract)
        向持续的时间对象减少指定的天数、小时、分钟、秒钟、毫秒、纳秒;
        并返回一个新的持续时间对象,表示原始持续时间减少指定的时间。
    Duration minus(Duration duration)
    Duration minus(long amountToSubtract, TemporalUnit unit)
        和增加时间一个使用方式。
'7:将时间段转换为指定单位值'
    toDays()、toHours()、toMinutes()、toMillis()、toNanos()
        将时间段转换为指定的天数、小时、分钟、毫秒、纳秒;转换的指定单位为int类型整数。
        需要注意的是,假设将时间段转换为天,那么不足一天的,将会被舍弃。
'8:时间段比较和校验是否相同'
    boolean equals(Object otherDuration)
        校验当前的时间段是否和传入的时间段一样,true代表时间段时间一样。
    int compareTo(Duration otherDuration)
        校验时间段大小;会返回:负数、0、正数;
        说明:0代表时间段相同、正数代表当前时间段比传入的时间段大、负数代表当前时间段比传入的时间段小。
'9:计算两个日期的差值'
    static Duration between(Temporal startInclusive, Temporal endExclusive)
        用于计算两个LocalTime对象之间的时间段(间隔)。
        方法返回一个Duration对象,表示从startInclusive到endExclusive之间的时分秒差异。
'10:其它方法'
    boolean isNegative()
        判断Duration对象是否包含负数;包含负数时返回true
    boolean isZero()
        判断Duration对象是否为0,为0则返回true,如Duration.ZERO创建的时间段就为0
    Temporal addTo(Temporal temporal)
    Temporal subtractFrom(Temporal temporal)
        以某个时间为基础 向后推一段时间 或 向前推一段时间,并将计算好的时间添加到指定的Temporal对象中。
        Temporal是一个广义的接口,代表了可以读取或操作日期时间的对象,比如LocalTime、LocalDateTime等。
    Duration abs()
        返回当前时间段的绝对值的。如果持时间段为负数,它会返回一个同样长但是正的时间段。
    Duration negated()
        返回当前持续时间的负值的副本。
        如果当前持续时间是正数,返回一个相同数值但是带负号的持续时间;
        如果是负数,返回一个同样数值但是带正号的持续时间
    Duration dividedBy(long divisor)
    Duration multipliedBy(long multiplicand)
        用于将当前的时间段除以或乘以指定的倍速。时间段的每个计数器(秒、纳秒)都乘以或除以指定的倍速。
'11:其它方法'
    static Duration from(TemporalAmount amount)
        从时间量获取一个Duration的实例。具体查看from()方法详讲。
    static Duration parse(CharSequence text)
        从一个文本字符串获得一个Duration ,只能传入格式如:PnDTnHnMn.nS 。

(二):常用方法讲解

点开查看详情:Duration时间段方法基本说明
public static void main(String[] args) {
    // =========================== 构建时间段Duration对象 ===========================
    Duration d1 = Duration.ofDays(2);            // 以天创建
    Duration d2 = Duration.ofHours(25);          // 以小时创建
    Duration d3 = Duration.ofMinutes(61);        // 以分钟创建
    Duration d4 = Duration.ofSeconds(65);        // 以秒创建
    Duration d5 = Duration.ofMillis(7100);       // 以毫秒创建
    Duration d6 = Duration.ofNanos(1000000001L); // 以纳秒创建
    Duration d7 = Duration.ofSeconds(2, 333); // 以秒和纳秒一起创建
    Duration d8 = Duration.of(61, ChronoUnit.MINUTES);      // 以指定的单位创建
    System.out.println(d1); // PT48H
    System.out.println(d2); // PT25H
    System.out.println(d3); // PT1H1M
    System.out.println(d4); // PT1M5S
    System.out.println(d5); // PT7.1S
    System.out.println(d6); // PT1.000000001S
    System.out.println(d7); // PT2.000000333S
    System.out.println(d8); // PT1H1M
    // 总结:关于Duration时间段创建的时间是可以进位的,因为它们之间的换算模式是不变的。
    // =========================== 时间段Duration分量获取 ===========================
    Duration d9 = Duration.ofSeconds(69145);        // PT19H12M25S
    Duration d10 = Duration.ofNanos(1000000025L);   // PT1.000000025S
    System.out.println(d9.getSeconds());// 69145
    System.out.println(d10.getNano());  // 25
    // 总结:关于默认toString()方法输出PT19H12M25S代表如下:
    //      PT代表以时分秒来表示的时间段Duration;H=小时、M=分钟、S=秒
    // =========================== 时间段修改、增加、减少 ===========================
    Duration d11 = Duration.ofSeconds(69145);        // PT19H12M25S
    System.out.println("修改:" + d11.withSeconds(60)); // 将秒设置为60打印:修改:PT1M
    System.out.println("增加:" + d11.plusDays(1));   // 增加1天打印:增加:PT43H12M25S
    System.out.println("减少:" + d11.minusSeconds(5)); // 减少5秒打印:减少:PT19H12M20S
    // ============================= 时间段转换对应单位和比较 =============================
    Duration d12 = Duration.ofDays(2);      // PT48H
    Duration d13 = Duration.ofSeconds(70);  // PT1M10S
    System.out.println(d12.toHours());  // 48
    System.out.println(d13.toMinutes());// 1  (还有10秒就被舍弃了)
    System.out.println(d13.compareTo(d12)); // -1 代表d13比d12小
    // ============================= 计算2个时间之间的相差值 =============================
    LocalTime time1 = LocalTime.of(10, 30, 20);
    LocalTime time2 = LocalTime.of(5, 15, 10);
    Duration duration1 = Duration.between(time1, time2);
    Duration duration2 = Duration.between(time2, time1);
    System.out.println(duration1);  // PT-5H-15M-10S  ;time1距离time2是往前的,负数
    System.out.println(duration2);  // PT5H15M10S     ;time2距离time1是往后的,正数
    // ================================== 其它方法说明 ==================================
    // 在time1的时间上加5秒
    Duration d15 = Duration.ofSeconds(5);          // PT5S
    LocalTime time = (LocalTime)d15.addTo(time1);
    System.out.println(time);   // 10:30:25
    // 时间段取反
    Duration d16 = Duration.ofSeconds(125,-999999999);
    System.out.println(d16.negated());  // PT-2M-4.000000001S
    System.out.println(d16);            // PT2M4.000000001S
    // 时间段绝对值
    Duration d17 = Duration.ofSeconds(-60);
    System.out.println("时间段绝对值:"+d17.abs());    // 时间段绝对值:PT1M
}

(三):关于Duration时间段总结

  Duration创建的时间段都是会进行自动的进位或退位的,因为时分秒是有着换算方式。

八:ZoneId时区

  在 Java 8 中引入了一个全新的 ZoneId时区抽象类 ,被作为新的日期时间API(java.time包)的一部分。ZoneId取代了旧的 java.util.TimeZone 类,并提供了一种统一的方式来处理和表示不同的时区,适用于处理日期时间信息中的时区转换和处理操作。   我们可以使用 ZoneId.of()方法 根据时区标识符字符串创建 ZoneId 对象。时区标识符通常采用 "大洲/城市" 的形式,例如 "Asia/Shanghai"(亚洲/上海) 或 "Europe/Paris"(欧洲/巴黎)。

(一):ZoneRegion、ZoneOffset

  ZoneId时区类它其实是一个抽象类,被设计出来是为了表示两个概念,分别表示地区和时差;正好ZoneId有两个子类,分别是ZoneRegion(表示时区)和ZoneOffset(表示时差), 需要注意的是它这两个实现类都是不可变的和线程安全的。   ZoneRegion:用于表示具体的地理区域时区,例如 "Europe/Paris"、"America/New_York" 等。这些时区标识符通常由一个大洲或国家/地区和城市名组成,可以准确地反映出特定地区的时区规则和历史变化。   ZoneOffset:表示的是与UTC或格林威治标准时间(GMT)的固定偏移量。它以形如 "+08:00"、"-05:00" 的格式表示,分别代表相对于UTC的小时和分钟偏移量。ZoneOffset主要用于表示不考虑夏令时变化的固定偏移量时区,如 "UTC+1" 或 "GMT-5"。

区别和用途:\color{#f00}{区别和用途:}区别和用途:   ZoneRegion:适合用于表示具体的地理区域时区,它包含了更多的时区规则信息,例如何时进行夏令时调整、偏移量的变化等。它是处理全球化应用中时区转换和时间计算的首选。   ZoneOffset:则用于表示相对于UTC的固定偏移量,它通常不包含夏令时规则。它适合于那些不需要考虑夏令时变化,只需简单地表示时间偏移的场景。

(二):什么是GMT和UTC

  GMT(Greenwich Mean Time)UTC(Coordinated Universal Time) 都是标准时间的名称,用于表示全球标准时间的参考基准。它们之间有一些历史上的差异,但在现代使用中,它们通常被视为相同的概念,尽管有些微小的差异。   GMT(格林威治标准时间): 它是以英国伦敦的格林威治天文台为基准的时间标准。它最初是由天文观测员用来描述地球自转周期和天体观测的时间基准。以基于太阳穿越格林威治子午线的时间来定义。这种方法在20世纪70年代后逐渐被原子钟和UTC取代,但GMT仍然被广泛使用作为时间的标准。GMT通常与英国伦敦所在的时区一致 (UTC+0),但在夏季会有夏令时的调整 (GMT+1)。   UTC(协调世界时): 它是由国际原子时(TAI)通过原子钟测量获得的时间标准。它旨在提供一个更准确和稳定的时间基准,取代了天文学上的GMT。UTC与地球自转没有直接关系,而是通过原子钟的国际协调来保持稳定和同步。它是一个统一的时间标准,不会因为夏令时而变化,与GMT在大多数情况下是一致的,但有微小的差异。 区别和用途:\color{#f00}{区别和用途:}区别和用途:   ▢:历史背景:GMT是早期基于天文观测的时间标准,而UTC是现代基于原子钟的国际标准。   ▢:精度:UTC比GMT更准确和稳定,因为它直接基于原子钟的测量。   ▢:使用:在航空航天、国际通讯等领域,通常使用UTC作为时间标准,因为它更精确和普遍接受。 注:Java8新日期时间系统中,GMT与UTC并无数值上的差别。\color{#f00}{注:Java8新日期时间系统中,GMT与UTC并无数值上的差别。}注:Java8新日期时间系统中,GMTUTC并无数值上的差别。

(三):方法列表

  我们可以使用ZoneId来创建出ZoneRegion和ZoneOffset类型的时区,但 无法直接使用 ZoneRegion 类,因为它这个类访问修饰符为default,只能在当前包下使用,我们无法进行使用,但ZoneOffset却可以正常使用。

'================ 关于抽象ZoneId类说明;但最终由ZoneRegion、ZoneOffset实现 ================'
'1:静态常量'
    static Map<String,String> SHORT_IDS
        区域短名称,它内部存放了28个常用的区域映射。比如上海我们只需要设置CTT即可。
        如:CTT=(Asia/Shanghai、亚洲上海)、JST=(Asia/Tokyo、亚洲东京)、
        如:ART=(Africa/Cairo、非洲开罗)、CST=(America/Chicago、美国芝加哥)
'2:创建时区'
    static ZoneId of(String zoneId)
        从一个时区ID字符串中获取一个ZoneId实例。
    static ZoneId of(String zoneId, Map<String,String> aliasMap)
        获取ZoneId的实例,使用其ID别名映射来补充标准区域ID。可用使用自带的SHORT_IDS静态Map常量。
        如:ZoneId.of("CTT",ZoneId.SHORT_IDS)
    static ZoneId ofOffset(String prefix, ZoneOffset offset)
        方法可以根据偏移量创建一个ZoneId实例。偏移类型可以通过prefix传入,但偏移类型只能传GMT、UTC、UT。
    static ZoneId systemDefault()
        获取当前系统的默认时区信息,我们正常获取的都是:Asia/Shanghai
'3:其它方法'
    static Set<String> getAvailableZoneIds()
        获取603个区域的ID,返回的这些区域ID都可以当作区域的参数
        如:Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi.....
    abstract String getId()
        获取唯一的时区ID。这个方法和toString()一样。
    abstract ZoneRules getRules()
        获取此ID的时区相关规则和信息,例如偏移量、夏令时调整规则等。
    boolean equals(Object obj)
        检查此时区ID是否等于其他时区ID。
        需要注意的是:使用区域ID创建的时区和偏移创建的时区比较会返回false
        其核心比较逻辑:取两个时区的getId()返回值,并通过字符串的equals方法比较是否一样。
    ZoneId normalized()
        规范时区ID,尽可能ZoneOffset。但一般我看不出啥效果,因为我们写的已经是规范的时区了。
    String getDisplayName(TextStyle style, Locale locale)
        获取区域的文字表示形式,例如“英国时间”或“+0200”。具体看下方代码示例。
        参数说明:
            style:指定文字样式,可以是FULL、SHORT或NARROW中的一个,决定了返回的文字表示的详细程度。
            locale:指定用于本地化显示的区域设置,确定了语言和地区特定的文字显示方式。
        示例:
            zone3.getDisplayName(TextStyle.FULL, Locale.CHINA); 打印:中国时间
    static ZoneId from(TemporalAccessor temporal)
        从TemporalAccessor对象中提取时区信息并返回对应的ZoneId实例;TemporalAccessor是一个接口,
        它提供了访问日期时间字段值的通用方式,例如LocalDateTime、ZonedDateTime等都实现了这个接口。
        注意:如ZonedDateTime是可用提取时区的,但是如LocalDateTime是不包含时区信息的,强行提取会报错。
'========================= 关于ZoneId抽象类的ZoneOffset实现类说明;========================='
'1:静态常量'
    static ZoneOffset MAX
        最大支持偏移量的常数。返回:+18:00
    static ZoneOffset MIN
        最大支持偏移量的常数。返回:-18:00
    static ZoneOffset UTC
        UTC的时区偏移量。返回:Z;这个Z也被称为0时区。
'2:创建时区'
    static ZoneOffset of(String offsetId)
        使用时区偏移量ID字符串创建ZoneOffset实例。
    static ZoneOffset ofHours(int hours)
    static ZoneOffset ofHoursMinutes(int hours, int minutes)
    static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds)
    static ZoneOffset ofTotalSeconds(int totalSeconds)
        根据小时(hours)、分钟(minutes)、秒数(seconds)、总秒数(totalSeconds)来构建ZoneOffset对象。
'3:常用获取方法'
    int get(TemporalField field)
    long getLong(TemporalField field)
        从指定的偏移量获取指定字段的值。两个方法一样,只不过返回的参数有int或者long两种。
        可选择的TemporalField值有:ChronoField.OFFSET_SECONDS
    int getTotalSeconds()
        获取区域偏移量,并转换为秒返回。和 get(ChronoField.OFFSET_SECONDS)返回的一样。
    String getId()
        获取唯一的时区ID。这个方法和toString()一样。
    ZoneRules getRules()
        获取此ID的时区相关规则和信息,例如偏移量、夏令时调整规则等。
4:比较相同或比大小
    boolean equals(Object obj)
        检查这个偏移量是否等于另一个偏移量。
    int	compareTo(ZoneOffset other)
        将此偏移量与其他偏移量按降序进行比较。
        注:返回0代表相同,负数代表左比右大,正数代表右比左大
'5:其它方法'
    boolean isSupported(TemporalField field)
        检查指定的字段是否受支持。其实这里只支持:。
    ValueRange range(TemporalField field)
        获取指定字段的有效值的范围。只有一种使用方式:xx.range(ChronoField.OFFSET_SECONDS)
    static ZoneOffset from(TemporalAccessor temporal)
        和上面的ZoneId介绍的方法使用方式一致。
    <R> R query(TemporalQuery<R> query)
        使用指定的查询查询此偏移量。具体查看query()方法详讲。
    Temporal adjustInto(Temporal temporal)
        调整指定的时间对象与此对象的偏移量相同。

(四):关于ZoneId基本使用

public static void main(String[] args) {
    // ============================= 两种类型参数创建时区的区别 =============================
    // 使用 区域 创建时区信息
    ZoneId zone1 = ZoneId.of("Asia/Shanghai");
    // 使用 偏移 创建时区信息
    ZoneId zone2 = ZoneId.of("+08:00");
    System.out.println("时区打印:" + zone1); // 时区打印:Asia/Shanghai
    System.out.println("时区打印:" + zone2); // 时区打印:+08:00
    System.out.println("zone1实现类:" + zone1.getClass()); // class java.time.ZoneRegion
    System.out.println("zone2实现类:" + zone2.getClass()); // class java.time.ZoneOffset
    // ================================ 创建时区的几种方式 ================================
    // 下面这四个的区域或者偏移都是表示一个地方
    ZoneId zone3 = ZoneId.of("Asia/Shanghai");            // Asia/Shanghai
    ZoneId zone4 = ZoneId.of("+08:00");                   // +08:00
    ZoneId zone5 = ZoneId.of("CTT", ZoneId.SHORT_IDS);    // Asia/Shanghai
    ZoneId zone6 = ZoneId.ofOffset("GMT", ZoneOffset.of("+08:00")); // GMT+08:00
    // =================================== 其它使用方式 ===================================
    // 获取时区的详细信息;我们可用借助ZoneRules来做其它操作
    ZoneRules rules = zone3.getRules();
    System.out.println(rules); // ZoneRules[currentStandardOffset=+08:00]
    // Asia/Shanghai 和 Asia/Shanghai 比较相同
    System.out.println(zone3.equals(zone5));    // true
    // +08:00 和 GMT+08:00 比较不相同
    System.out.println(zone4.equals(zone6));    // false
    // 获取区域的文字表示形式
    ZoneId zone7 = ZoneId.of("Asia/Shanghai");
    ZoneId zone8 = ZoneId.of("Asia/Tokyo");
    // 以中文显示
    System.out.println(zone7.getDisplayName(TextStyle.FULL, Locale.CHINA));// 中国时间
    System.out.println(zone8.getDisplayName(TextStyle.FULL, Locale.CHINA));// 日本时间
    // 以日文显示(发现小日子过的不错的日本一些文字和中文繁体一样)
    System.out.println(zone7.getDisplayName(TextStyle.FULL, Locale.JAPAN));// 中国時間
    System.out.println(zone8.getDisplayName(TextStyle.FULL, Locale.JAPAN));// 日本時間
    // 从带有时区的类中获取时区
    ZonedDateTime zonedDateTime = ZonedDateTime.now();
    // LocalDateTime localDateTime = LocalDateTime.now(); 这个类不包含时区哟
    ZoneId from = ZoneId.from(zonedDateTime);
    System.out.println(from);   // Asia/Shanghai
}

(五):关于ZoneOffset基本使用

public static void main(String[] args) {
    // ================================ 创建时区的几种方式 ================================
    // 关于几种时区偏移量ID字符串的写法
    // +代表比格林时间早,-代表比格林时间晚;
    ZoneOffset z1 = ZoneOffset.of("+08");  // +08:00
    // 这种的 "+08" 可以直接写成 "+8"
    ZoneOffset z2 = ZoneOffset.of("+08:30");// +08:30
    // 这种的 "+08:30" 可直接写成+0830;一定要保证四位数,比如+830是不行的。
    ZoneOffset z3 = ZoneOffset.of("-08:30:45"); // -08:30:45
    // 这种的 "+08:30:15" 可直接写成+083045;一定要保证六位数。
    // 关于使用指定时分秒创建的时区偏移的时分秒必须符号(+,-)必须一致。
    ZoneOffset z4 = ZoneOffset.ofHoursMinutesSeconds(8, 30, 45);    // +08:30:45
    ZoneOffset z5 = ZoneOffset.ofHoursMinutesSeconds(-8, -30, -45); // -08:30:45
    // =================================== 时区的比较 ===================================
    ZoneOffset zoneOffset1 = ZoneOffset.of("+08:00");
    ZoneOffset zoneOffset2 = ZoneOffset.of("+01:00");
    ZoneOffset zoneOffset3 = ZoneOffset.of("+01:00");
    System.out.println(zoneOffset3.equals(zoneOffset2));    // true
    System.out.println(zoneOffset1.equals(zoneOffset2));    // false
    System.out.println(zoneOffset2.compareTo(zoneOffset3)); // 0
    System.out.println(zoneOffset2.compareTo(zoneOffset1)); // 25200
    System.out.println(zoneOffset1.compareTo(zoneOffset2)); // -25200
    // 关于ZoneOffset的比较方法说明:返回0代表相同,负数代表左比右大,正数代表右比左大
    // =================================== 其它的方法 ===================================
    // 从一个带时区的日期时间中提取时区偏移
    ZonedDateTime zonedDateTime = ZonedDateTime.now();
    ZoneOffset offset = ZoneOffset.from(zonedDateTime);
    System.out.println(offset); // +08:00
}

九:Instant具体时刻

  Java 8 中引入了一个Instant类,用于 表示时间线上的一个具体时刻 ,它是以Unix时间戳的形式存储,精确到纳秒(在旧版的Date类中则精确到毫秒)。其实使用纳秒去表示一个时间,其内部是由一个long字段(记录秒)和一个int字段(记录纳秒)组成,第一个部分保存的是Unix时间戳(就是1970-01-01T00:00:00Z)开始到现在的秒数,第二部分保存的是纳秒数(永远不会超过999,999,999,若加1就进位变为1秒)。我们需要知道在Instant类中只表示一个特定的时刻,不受时区影响。而且它也是一个不可变的类哟,一旦创建后则无法继续修改,这样保证了线程的安全性。   比如创建具体时刻:Instant.ofEpochSecond(2,1); 打印:1970-01-01T00:00:02.000000001Z;我们需要知道的是Instant类在保存“1970-01-01T00:00:02”则是转换为秒存放在一个long类型的seconds字段中,而存放后面的纳秒“000000001”则是放在int类型的nanos字段里,为此这个时刻便被存储了起来。 注:一定要知道Instant是没有时区概念的。\color{#f00}{注:一定要知道Instant是没有时区概念的。}注:一定要知道Instant是没有时区概念的。

(一):方法列表

点开查看详情:Instant时刻方法列表
'1:关于时刻的常量'
    static Instant EPOCH
        这个常量代表了时间的起始点,即19701100:00:00UTC。
    static Instant MAX
        定义了Instant类所能表示的最大时间点。返回:1000000000-12-31T23:59:59.999999999Z。
    static Instant MIN
        定义了Instant类所能表示的最小时间点。返回:-1000000000-01-01T00:00:00Z。
'2:关于时刻的创建(创建出的没有时区概念)'
    static Instant now()
    static Instant now(Clock clock)
        获取系统下的当前时间点。并返Instant时刻对象;带Clock则是从指定的时钟对象中获取当前时间点。
    static Instant ofEpochMilli(long epochMilli)
    static Instant ofEpochSecond(long epochSecond)
    static Instant ofEpochSecond(long epochSecond, long nanoAdjustment)
        根据自19701100:00:00UTC以来的 毫秒、秒、秒和纳秒 来创建一个Instant实例。
        参数可以是正数(表示从纪元(Epoch)之后的时间)或负数(表示从纪元(Epoch)之前的时间)。
'3:关于时刻的获取'
    int getNano()
    long getEpochSecond()
        从时刻中获取纳秒、秒这两个分量值。
    int get(TemporalField field)
    long getLong(TemporalField field)
        从当前时刻中获取指定字段的整数值。通过ChronoField枚举获取;
        比如:ChronoField.NANO_OF_SECOND等同getNano()方法
        注意:ChronoField.INSTANT_SECONDS必须使用getLong()方法
'4:关于时刻的判断'
    boolean isBefore(Instant otherInstant)
    boolean isAfter(Instant otherInstant)
        检查当前时刻是否在指定时刻之前或之后。true代表是,反之false
    boolean isSupported(TemporalUnit unit)
    boolean isSupported(TemporalField field)
        检查指定的ChronoUnit枚举字段和ChronoField枚举字段是否被支持。
'5:关于时刻的比较'
    boolean equals(Object otherInstant)
        检查当前时刻是否等于另一个时刻。
    int compareTo(Instant otherInstant)
        将此时刻与另一个时刻进行比较。最终会返回:负数、0、正数;
        说明:0代表时刻相同、正数代表当前时刻比传入的时刻大、负数代表当前时刻比传入的时刻小。
'6:关于时刻的修改(with)方法'
    Instant with(TemporalAdjuster adjuster)
        通过TemporalAdjuster来修改,具体可以参考TemporalAdjusters类。
    Instant with(TemporalField field, long newValue)
        通过ChronoField枚举修改指定分量的值。
'7:关于时刻的增加(plus)方法'
    Instant plusSeconds(long secondsToAdd):增加秒
    Instant plusMillis(long millisToAdd):增加毫秒
    Instant plusNanos(long nanosToAdd):增加纳秒
    Instant plus(long amountToAdd, TemporalUnit unit):通过ChronoUnit枚举类来增加时刻信息。
    Instant plus(TemporalAmount amountToAdd)
        对当前的时刻增加一个时间段,时间段可以为Period(日期间隔)或Duration(持续时间)。
'8:关于时刻的减少(minus)方法'
    Instant minusSeconds(long secondsToSubtract):减少秒
    Instant minusMillis(long millisToSubtract):减少毫秒
    Instant minusNanos(long nanosToSubtract):减少纳秒
    Instant minus(long amountToSubtract, TemporalUnit unit):通过ChronoUnit枚举类来减少时刻信息。
    Instant minus(TemporalAmount amountToSubtract)
        对当前的时刻减少一个时间段,时间段可以为Period(日期间隔)或Duration(持续时间)。
'9:关于时刻的时差计算'
    long until(Temporal endExclusive, TemporalUnit unit)
        根据指定的ChronoUnit枚举单位计算直到另一个时刻的间隔。
'10:关于时刻生成指定带时区的方法'
    ZonedDateTime atZone(ZoneId zone)
        将当前的Instant对象与指定的时区(ZoneId)相结合,创建一个ZonedDateTime对象。
    OffsetDateTime atOffset(ZoneOffset offset)
        将当前的Instant对象与指定的偏移量(ZoneOffset)组合,创建一个OffsetDateTime对象。
        众所周知,时刻是一个没有时区和偏移的时间;那么加上时区或偏移量就可以创建一个日期对象了。
'11:其它方法'
    long toEpochMilli()
        将当前的Instant对象转换为自19701100:00:00 GMT(格林尼治标准时间)开始的毫秒数。
    static Instant parse(CharSequence text)
        将表示日期时间的文本解析为Instant对象。
        格式通常为ISO-8601标准格式的字符串,例如 2024-07-17T10:15:30Z。
    ValueRange range(TemporalField field)
       获取指定字段的有效值的范围。如纳秒范围:xx.range(ChronoField.NANO_OF_SECOND)
    Instant truncatedTo(TemporalUnit unit)
        将Instant对象按照指定的时间单位进行截断,返回一个新的Instant对象。截取之后的时间将被丢弃。
    static Instant from(TemporalAccessor temporal)
        用于从任意实现了TemporalAccessor接口的时间对象获取一个Instant实例。
        但测试只有OffsetDateTime、ZonedDateTime可以转换Instant对象。
        注意:转换成Instant对象的参数必须要携带时区和偏移信息,否则无法转换。
    Temporal adjustInto(Temporal temporal)
        调整指定的时间对象具有这个瞬间。
    〈R〉 R query(TemporalQuery〈R〉 query)
        使用指定的查询查询此即时。具体查看query()方法详讲。

(二):常用方法讲解

点开查看详情:Instant时刻方法详解
public static void main(String[] args) throws IOException {
    // =========================== 构建Instant对象 ===========================
    // 注意:Instant创建出的时刻是没有时区概念的;全部都是0时区("Z")
    Instant ins1 = Instant.now();
    Instant ins2 = Instant.ofEpochSecond(120); // 通过秒创建
    Instant ins3 = Instant.ofEpochMilli(2000); // 通过毫秒创建
    Instant ins4 = Instant.ofEpochSecond(120, 999999999);
    System.out.println("ins1:" + ins1); // ins1:2024-07-16T14:20:01.093Z
    System.out.println("ins2:" + ins2); // ins2:1970-01-01T00:02:00Z
    System.out.println("ins3:" + ins3); // ins3:1970-01-01T00:00:02Z
    System.out.println("ins4:" + ins4); // ins4:1970-01-01T00:02:00.999999999Z
    // 现在是22:20分;但从上面可以看出now()创建的时刻是无时区的,所以为14:20;加上八个时区正好北京时间。
    // ========================== 构建Instant对象分量 ==========================
    System.out.println("获取秒:" + ins4.getEpochSecond()); // 获取秒:120
    System.out.println("获取纳秒:" + ins4.getNano()); // 获取纳秒:999999999
    System.out.println("纳秒:" + ins4.get(ChronoField.NANO_OF_SECOND)); // 纳秒:999999999
    System.out.println("微秒:" + ins4.get(ChronoField.MICRO_OF_SECOND)); // 微秒:999999
    System.out.println("毫秒:" + ins4.get(ChronoField.MILLI_OF_SECOND)); // 毫秒:999
    System.out.println("秒:" + ins4.getLong(ChronoField.INSTANT_SECONDS)); // 秒:120
    // ChronoField.INSTANT_SECONDS的获取必须使用getLong方法哟。
    // =========================== 校验时刻和比较对象 ===========================
    Instant ins5 = Instant.ofEpochSecond(60, 999999999);
    Instant ins6 = Instant.ofEpochSecond(120, 999999999);
    Instant ins7 = Instant.ofEpochSecond(120, 999999999);
    System.out.println(ins5.isBefore(ins6)); // ins5时刻在ins6之前 : true
    System.out.println(ins5.isAfter(ins6));  // ins5时刻在ins6之后 : false
    System.out.println(ins5.isSupported(ChronoField.MICRO_OF_SECOND)); // true
    // 关于Instant中可用的ChronoUnit枚举类如下:
    //      MICROS、MILLIS、SECONDS、MINUTES、HOURS、HALF_DAYS、DAYS
    // 关于Instant中可用的ChronoField枚举类如下:
    //      INSTANT_SECONDS、NANO_OF_SECOND、MICRO_OF_SECOND、MILLI_OF_SECOND
    System.out.println("ins6等于ins7:" + ins6.equals(ins7));    // ins6等于ins7:true
    System.out.println("ins6等于ins7:" + ins6.compareTo(ins7)); // ins6等于ins7:0
    System.out.println("ins5小于ins6:" + ins5.compareTo(ins6)); // ins5小于ins6:-1
    // 0代表相同、负数代表当前时刻比传入的时刻小、正数代表当前时刻比传入的时刻大
    // ========================= 关于时刻修改、增加、减少方法 =========================
    Instant ins9 = Instant.ofEpochSecond(60, 1000);
    System.out.println("将时刻修改成20秒:" + ins9.with(ChronoField.INSTANT_SECONDS, 20));
    //      打印:将时刻修改成20秒:1970-01-01T00:00:20.000001Z
    System.out.println("将时刻增加10秒:" + ins9.plusSeconds(10));
    //      打印:将时刻增加10秒:1970-01-01T00:01:10.000001Z(原来00:01:00;现在00:01:10)
    System.out.println("将时刻减少10秒:" + ins9.minusSeconds(10));
    //      打印:将时刻减少10秒:1970-01-01T00:00:50.000001Z(原来00:01:00;现在00:00:50)
    // 我们也可以增加或减少一个时段(Period、Duration)
    Period period = Period.of(0, 0, 10); // 10天
    Duration duration = Duration.ofSeconds(20); // 20秒
    Instant ins9plus = ins9.plus(period);   // ins9增加一段时间
    Instant ins9minus = ins9.minus(duration); // ins9减少一段时间
    System.out.println("增加:" + period + " 等于:" + ins9plus);
    System.out.println("减少:" + duration + " 等于:" + ins9minus);
    //      打印:增加:P10D 等于:1970-01-11T00:01:00.000001Z
    //      打印:减少:PT20S 等于:1970-01-01T00:00:40.000001Z
    // ============================== 关于时刻时差方法 ==============================
    Instant ins10 = Instant.ofEpochSecond(60, 1000);
    Instant ins11 = Instant.ofEpochSecond(120, 1000);
    System.out.println(ins10.until(ins11, ChronoUnit.SECONDS)); // 相差60秒
    System.out.println(ins11.until(ins10, ChronoUnit.SECONDS)); // 相差-60秒
    // ========================== 关于时刻转换成时区时间方法 ==========================
    Instant ins12 = Instant.EPOCH;  // 1970-01-01T00:00:00Z
    ZoneId zoneId = ZoneId.of("Asia/Shanghai");
    ZoneOffset zoneOffset = ZoneOffset.of("+08:00");
    ZonedDateTime zonedDateTime = ins12.atZone(zoneId);
    OffsetDateTime offsetDateTime = ins12.atOffset(zoneOffset);
    System.out.println(zonedDateTime);
    //      打印:1970-01-01T08:00+08:00[Asia/Shanghai] ;上海是+08区,所以时间要+8小时
    System.out.println(offsetDateTime);
    //      打印:1970-01-01T08:00+08:00 ;日期偏移8个时区,所以日期为8点
    // ============================ 关于时刻的其它方法说明 ============================
    Instant ins13 = Instant.now();
    long epochMilli = ins13.toEpochMilli(); // 将时刻转换为无时区的毫秒数
    Date date = new Date(epochMilli);       // 通过毫秒数可以创建Date对象(Date会自己加时区)
    System.out.println(date);               // Wed Jul 17 16:58:36 CST 2024
    // 比如下面:
    // Instant ins14 = Instant.ofEpochSecond(120); // 1970-01-01T00:02:00Z
    // Date date = new Date(ins14.toEpochMilli()); // Thu Jan 01 08:02:00 CST 1970
    System.out.println(ins13.range(ChronoField.NANO_OF_SECOND));
    //      打印:纳秒范围:0 - 999999999
    Instant parse = Instant.parse("2024-07-17T09:11:44.577Z");
    System.out.println("时刻字符串转时刻:" + parse);
    //      打印:时刻字符串转时刻:2024-07-17T09:11:44.577Z
    Instant instant = ins4.truncatedTo(ChronoUnit.SECONDS); // 按照秒截取;截取完纳秒被丢弃了
    System.out.println(instant);    // 1970-01-01T00:02:00Z
    // 下面3个打印都是一样的,比如:2024-07-17T09:30:38.751Z
    System.out.println(Instant.from(ZonedDateTime.now()));
    System.out.println(Instant.from(OffsetDateTime.now()));
    System.out.println(Instant.from(LocalDateTime.now().atZone(ZoneId.systemDefault())));
}

十:Clock时钟类

  在 Java 8 里的 java.time 包中引入了一个Clock时钟抽象类,用来记录当前时间时,这个类并没有记录具体的年月日和时分秒信息,而是记录了当前时间距离时间原点(1970-01-01T00:02:00)的毫秒数,同时这个时钟类还自动与系统默认时区相关联;所以有了时间毫秒和时区信息的话就能把Clock对象转换成各种日期时间类的对象。Clock抽象类的主要作用就是为日期和时间提供一个时钟,可以用于替代系统默认的时钟来进行日期和时间的操作。其次Clock里的所有实现是线程安全的类。

'Java中的Clock类是一个抽象类,具体内部实现类如下:'
    SystemClock【默认实现】:使用系统默认的时区来获取当前的日期和时间信息。
    OffsetClock:表示了一个相对于UTC的固定偏移量。
    TickClock:根据指定的时钟和时钟周期产生一个周期性的时间序列。
    FixedClock:表示一个固定的时间点,通常用于测试或模拟特定时间的场景。

(一):方法列表

点击查看详情:Clock时钟方法列表
'1:关于Clock静态创建方法'
    static Clock systemDefaultZone()
        使用系统默认时区来获取当前的时刻。
    static Clock system(ZoneId zone)
        使用提供的zone时区来获取当前的时刻。
    static Clock systemUTC()
        使用UTC时区来获取当前的时刻。就是 “Z” 0时区;
        这对于需要在全球范围内进行时间处理并确保一致性的应用程序很有用。
    static Clock offset(Clock baseClock, Duration offsetDuration)
        从指定的baseClock返回时钟,并在返回时钟的基础上添加指定的时间段偏移offsetDuration。
    static Clock tickMinutes(ZoneId zone)
    static Clock tickSeconds(ZoneId zone)
        根据指定时区来创建Clock对象,但创建的对象只能精确到分或者秒。
    static Clock tick(Clock baseClock, Duration tickDuration)
        从指定的baseClock开始,并按照指定的tickDuration周期截断时间。tickDuration为Duration时段;
        只有当baseClock的瞬时时间点与该周期的边界对齐时,才会返回一个新的瞬时时间点。
        例如:tickDuration是Duration.ofHours(2),则新的Clock实例每隔2小时返回一个时间点。
        说明:比如以ofHours(2)来算;2小时为一个时间点。
00:25则Clock为00:00;如01:15则Clock为00:00;如02:12则Clock为02:00
        如果以ofDays(3)来算;则3天为一个时间点。
    static Clock fixed(Instant fixedInstant, ZoneId zone)
        返回一个始终固定时刻的时钟。因为Instant是一个无时区的时间戳类,ZoneId是一个标准的时区类。
        从上面两个参数可以组装成一个始终固定的时间,不会因为系统的时区或者带时区的时间戳影响。
'2:其它方法'
    boolean equals(Object obj)
        检查当前时钟对象是否等于另一个对象。
    abstract ZoneId getZone()
        获取当前Clock对象中的时区信息
    abstract Instant instant()
        返回一个Instant对象,表示当前时钟的时间点。
    long millis()
        获取时钟的当前毫秒时间。
    abstract Clock withZone(ZoneId zone)
        修改Clock对象,使用了指定的新时区。

(二):常用方法讲解

点击查看详情:Clock时钟的基本方法详解
public static void main(String[] args) throws IOException {
    // ============================ 关于Clock静态创建方法 ============================
    // 创建2个时区信息
    ZoneId zone1 = ZoneId.of("GMT+08:00");      // 时区的偏移
    ZoneId zone2 = ZoneId.of("Asia/Shanghai");  // 时区的地点
    System.out.println("========== 通过Clock构建SystemClock对象 ==========");
    Clock clock1 = Clock.system(zone1);
    Clock clock2 = Clock.system(zone2);
    Clock clock3 = Clock.systemDefaultZone();   // 构建默认时区时钟
    Clock clock4 = Clock.systemUTC();           // 构建UTC时钟,[Z]0时区时钟
    System.out.println(clock1);             // SystemClock[GMT+08:00]
    System.out.println(clock2);             // SystemClock[Asia/Shanghai]
    System.out.println(clock3);             // SystemClock[Asia/Shanghai]
    System.out.println(clock4);             // SystemClock[Z]
    System.out.println(clock1.millis());    // 1721292336535
    System.out.println(clock2.millis());    // 1721292336535
    System.out.println(clock3.millis());    // 1721292336535
    System.out.println(clock4.millis());    // 1721292336535
    // 虽然看上面打印的毫秒数都是相同的,但是通过这个Clock创建日期对象就会不一样了
    // 毫秒数一样,但时区不一样,转换日期的同时还要将日期时区偏移加上,如下:
    System.out.println(LocalDateTime.now(clock3)); // 2024-07-18T16:45:36.535
    System.out.println(LocalDateTime.now(clock4)); // 2024-07-18T08:45:36.535
    // 转换成日期,clock3转成日期是要比clock4转成日期增加了8个小时的时区。
    System.out.println("========== 通过Clock构建OffsetClock对象 ==========");
    Clock clock5 = Clock.systemUTC();
    Clock clock6 = Clock.offset(clock5, Duration.ofHours(2)); // 在clock5基础上再加2小时
    System.out.println(clock6);     // OffsetClock[SystemClock[Z],PT2H]
    System.out.println(LocalDateTime.now(clock5)); // 2024-07-18T08:45:36.535
    System.out.println(LocalDateTime.now(clock6)); // 2024-07-18T10:45:36.535
    System.out.println("=========== 通过Clock构建TickClock对象 ===========");
    //  tickMinutes、tickSeconds就是创建当前时刻的Clock对象,但该对象计时只能精确到分、秒。
    Clock clock7 = Clock.tickMinutes(zone1); // 精确到分钟
    Clock clock8 = Clock.tickSeconds(zone1); // 精确到秒钟
    System.out.println(clock7); // TickClock[SystemClock[GMT+08:00],PT1M]
    System.out.println(clock8); // TickClock[SystemClock[GMT+08:00],PT1S]
    System.out.println(clock7.millis()); // 1721292300000
    System.out.println(clock8.millis()); // 1721292336000
    System.out.println(LocalDateTime.now(clock7)); // 2024-07-18T16:45
    System.out.println(LocalDateTime.now(clock8)); // 2024-07-18T16:45:36
    // 关于tick用法,若单位
    Clock clock = Clock.systemDefaultZone();
    Clock tick = Clock.tick(clock, Duration.ofHours(2));
    System.out.println(LocalDateTime.now(clock)); // 2024-07-18T16:45:36.536
    System.out.println(LocalDateTime.now(tick));  // 2024-07-18T16:00
    System.out.println("=========== 通过Clock构建FixedClock对象 ===========");
    Clock fixed = Clock.fixed(Instant.now(), ZoneId.systemDefault());
    System.out.println(fixed); // FixedClock[2024-07-18T08:45:36.536Z,Asia/Shanghai]
}

十一:ZonedDateTime日期时间(时区)

  Java 8 引入了 ZonedDateTime 类,用于表示 带有时区信息的日期和时间 。内部其实是由三个类组成:LocalDateTime、ZoneOffset、ZoneId;在这里的ZoneOffset是用来处理夏令时的;   注意ZonedDateTime是不可变的,一旦创建后,其值不能更改,这有助于线程安全和更可靠的时间处理。

(一):方法列表

  其实ZonedDateTime方法使用起来也很简单,记住如下几点:以now开头的一般是创建当前日期、以of开头的一般是创建指定日期、以get开头的一般是获取日期时间分量、以with开头的一般是修改日期时间分量、以plus开头的一般是增加指定日期时间、以minus开头的一般是减少指定日期时间、以to开头的一般是转换指定类型格式;其它的也就没啥了。具体的可以参考如上的LocalDateTime。下面我只介绍一些特有的方法。

点击查看详情:关于ZonedDateTime对象的基本方法
'1:关于对象的创建方法'
    static ZonedDateTime now()
    static ZonedDateTime now(Clock clock)
    static ZonedDateTime now(ZoneId zone)
        上面这3个方法用来创建具体的日期时间,不同的是不带参数的会创建当地默认时区的日期时间;
        注:关于带参数创建会在指定的now章节详解。
    static ZonedDateTime of(int year, int month, int dayOfMonth.....)
        通过年、月、日、时、分、秒、纳秒、时区来构建一个带时区的ZonedDateTime日期时间
    static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone)
    static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
        通过日期时间、日期、时间、时区来构建带时区的ZonedDateTime日期时间
    static ZonedDateTime ofInstant(Instant instant, ZoneId zone)
        将给定的Instant对象和时区信息转换为ZonedDateTime对象。instant会转换到指定的时区时间。
    static ZonedDateTime ofInstant(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone)
        将localDateTime和offset进行偏移后得到标准UTC时间后,再根据zone创建ZonedDateTime日期
    static ZonedDateTime ofStrict(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone)
        使用严格的偏移模式创建日期时间,offset和zone必须一致;然后将它们3个参数组合起来创建成日期时间;
        比如offset是+08:00;那么zone必须指定和当前偏移相等的城市,如:Asia/Shanghai
    static ZonedDateTime ofLocal(LocalDateTime ldt,ZoneId zone,ZoneOffset preferredOffset)
        创建夏令时时间,对于我们国家已经不在使用夏令时了,很少使用。
 
'2:日期时间分量获取方法'
    getYear()、getMonthValue()、getDayOfMonth()、getHour()、getMinute()、getSecond()、getNano()
        获取年、月(1~12)、日、时、分、秒、纳秒
    getDayOfYear()、getMonth()、getDayOfWeek()、getOffset()、getZone()
        一年中的第多少天、月份(枚举)、星期(枚举)、区域偏移量(如"+01:00")、时区(如"Asia/Shanghai")
    int get(TemporalField field)
    long getLong(TemporalField field)
        通过ChronoField枚举来获取指定位置分量的值。
 
'3:关于日期时间的增加、减少'
    以plus开头的都是增加日期时间、以minus开头的都是减少日期时间;
    具体参考之前写的LocalDate和LocalTime;这两个类里的修改这里面都有。
 
'4:关于日期时间的修改(这里我只介绍特殊的)'
    以with开头的方法都是修改日期时间,具体参考之前写的LocalDate和LocalTime;
    但是它有几个特殊的方法分别如下:
    ZonedDateTime withFixedOffsetZone()
        将日期时间的区域ID(城市时区)替换成具体偏移量(时差)并返回新日期时间对象。
    ZonedDateTime withLaterOffsetAtOverlap()
        获得标准/夏令时较晚的时间。(没夏令时的基本不用)
    ZonedDateTime withEarlierOffsetAtOverlap()
        获得标准/夏令时较早的时间。(没夏令时的基本不用)
    ZonedDateTime withZoneSameLocal(ZoneId zone)
        将日期时间修改成指定的时区,但不会修改当前对象的时间信息,只替换一个时区。
    ZonedDateTime withZoneSameInstant(ZoneId zone)
        将日期时间修改成指定的时区,并同时将当前对象的时间信息转换成对应的时间值。
 
'5:关于日期时间的枚举校验'
    boolean isSupported(TemporalUnit unit)
    boolean isSupported(TemporalField field)
        通过此方法来校验ChronoField、ChronoUnit枚举内的枚举是否可使用。
 
'6:日期时间的转换'
    LocalDate toLocalDate()
    LocalTime toLocalTime()
    LocalDateTime toLocalDateTime()
    OffsetDateTime toOffsetDateTime()
        从ZonedDateTime中获取 日期、时间、日期时间、偏移日期时间 这几个指定部分。
 
'7:日期时间的格式化已经从字符串中提取对象(请参考日期格式化)'
    String format(DateTimeFormatter formatter)
        用于将日期对象格式化为一个字符串,使用指定的DateTimeFormatter格式化程序。
    static ZonedDateTime parse(CharSequence text)
    static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter)
        用于从一个文本字符串中解析并获取一个ZonedDateTime实例。
        默认只能传如下格式字符串:"2007-12-03T10:15:30+01:00[Europe/Paris]"
        但是若传了formatter对象,则可用自定义指定字符串传入的规范。
 
'8:其它方法'
    boolean equals(Object obj)
        检查当前日期时间是否等于另一个日期时间。
    ValueRange range(TemporalField field)
        获取当前日期时间的指定ChronoField枚举字段的有效值的范围。
    long until(Temporal endExclusive, TemporalUnit unit)
        根据指定的ChronoUnit枚举单位计算直到另一个日期时间的间隔。
    ZonedDateTime truncatedTo(TemporalUnit unit)
        用于将当前日期时间对象按照指定的粒度单位进行截断操作,并返回一个新的ZonedDateTime对象。
    static ZonedDateTime from(TemporalAccessor temporal)
        从时间对象里获取一个ZonedDateTime的实例。具体查看from()方法详讲。
    〈R〉 R query(TemporalQuery〈R〉 query)
        使用指定的查询查询此日期时间。具体查看query()方法详讲。 

(二):常用方法讲解

1:带时区的日期时间创建:\color{#CD9B1D}{1:带时区的日期时间创建:}1:带时区的日期时间创建:

点击查询详情:带时区的日期时间创建
public static void main(String[] args) {
    // ========================== 创建ZonedDateTime几种方式 ==========================
    // 创建下面可以使用到的对象
    Instant instant = Instant.now();    // 创建一个UTC格式的标准时区,可以理解为GMT+00时区
    System.out.println(instant);        // 2024-07-18T15:11:46.349Z
    LocalDateTime dt1 = LocalDateTime.of(2024, 7, 18, 10, 30, 30);
    System.out.println(dt1);            // 2024-07-18T10:30:30
    // ### 创建默认带时区的日期时间的方式
    ZonedDateTime zdt1 = ZonedDateTime.now();
     
    // ### 使用区域创建日期时间,zdt2是东京东七区、zdt3是上海东八区;所以上海要比东京早一小时。
    ZonedDateTime zdt2 = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
    ZonedDateTime zdt3 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(zdt2); // 2024-07-19T00:11:46.410+09:00[Asia/Tokyo]
    System.out.println(zdt3); // 2024-07-18T23:11:46.410+08:00[Asia/Shanghai]
     
    // ### 通过of传入LocalDateTime创建带时区的日期时间
    ZonedDateTime zdt4 = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("+08:00"));
    System.out.println(zdt4); // 2024-07-18T23:11:46.410+08:00
     
    // ### 关于ofInstant方法创建ZonedDateTime对象
    ZonedDateTime zdt5 = ZonedDateTime.ofInstant(instant, ZoneId.of("GMT+05:00"));
    System.out.println(zdt5); // 2024-07-18T20:11:46.349+05:00[GMT+05:00]
    // 由于Instant构建的时刻是没有时区的,组合一下时区信息就可以生成一个带时区的ZonedDateTime对象。
    // 所以zdt5的时间是在instant时刻上加上5个时区的偏移(+5小时)
     
    ZonedDateTime zdt6 = ZonedDateTime
            .ofInstant(dt1, ZoneOffset.of("-05:00"), ZoneId.of("+08:00"));
    System.out.println(zdt6); // 2024-07-18T23:30:30+08:00
    // 分析一下zdt6为什么得出2024-07-18T23:30:30+08:00:
    //  参数一dt1它就是一个普通时间,不要将它和时区挂钩,因为它就没有时区信息。
    //  参数二其实就是一个偏移,此时需要偏移-5小时。
    //  参数三就是最终日期的偏移时区。
    // 首先拿参数一和参数二计算,就是将参数一变为秒,参数二的偏移也转换为秒,然后它们数值进行相减;
    //      公式为:dt1秒 - (ZoneOffset秒)
    //      ZoneOffset.of("-01")转换秒等于-3600;若是+01则转换秒为+3600;-05则为-18000秒;
    //      最终上面的dt1转换秒就是:1721298630 - (-18000);转换公式等于:1721298630+18000
    //      源码核心计算:secs -= offset.getTotalSeconds(); 其中secs就为日期时间的秒。
    // 其次将计算出来的秒转换成时间后再加上指定的时区即可。
    // 所以就是:2024-07-18T15:30:30加上偏移+8时区就是 2024-07-18T23:30:30+08:00
     
    // ### 严格时区偏移模式
    ZonedDateTime zdt7 = ZonedDateTime
            .ofStrict(dt1, ZoneOffset.of("+08"), ZoneId.of("Asia/Shanghai"));
    System.out.println(zdt7); // 2024-07-18T10:30:30+08:00[Asia/Shanghai]
    // ZonedDateTime.ofStrict(dt1, ZoneOffset.of("+08"), ZoneId.of("Asia/Tokyo"));
    // +08时区和Asia/Tokyo组合会报错;因为Asia/Tokyo是+09时区。
     
    // ### 夏令时
    // 创建夏令时,首先会查询当前这个时区是否存在夏令时,若存在则会进行使用最后一个偏移;
    // 比如Asia/Beirut黎巴嫩的夏令时是从2020-3-29~2020-10-24,此时为GMT+03;非夏令时则为GMT+02
    ZonedDateTime zonedDateTime = ZonedDateTime.ofLocal(dt1,
            ZoneId.of("Asia/Beirut"), ZoneOffset.of("+02"));
    System.out.println(zonedDateTime); // 2024-07-18T10:30:30+03:00[Asia/Beirut]
}

2:时区日期时间的其它使用:\color{#CD9B1D}{2:时区日期时间的其它使用:}2:时区日期时间的其它使用:

点击查看详情:关于时区日期时间的其它使用
public static void main(String[] args) {
    // 创建一个日期时间:2024-07-05T10:30:20
    LocalDateTime ldt = LocalDateTime.of(2024, 7, 5, 10, 30, 20);
    // 创建一个日期时间:2024-07-05T10:40:20
    LocalDateTime ldt1 = LocalDateTime.of(2024, 7, 5, 10, 40, 20);
    // 创建一个带时区的日期时间:2024-07-05T10:30:20+08:00[Asia/Shanghai]
    ZonedDateTime zdt1 = ZonedDateTime.of(ldt, ZoneId.of("Asia/Shanghai"));
    // 创建一个带时区的日期时间:2024-07-05T10:40:20+08:00[Asia/Shanghai]
    ZonedDateTime zdt2 = ZonedDateTime.of(ldt1, ZoneId.of("Asia/Shanghai"));
    // ========================== 日期时间修改特殊方法(with) ==========================
    // 修改成以偏移方式表示,没有了时区ID信息了。
    System.out.println(zdt1.withFixedOffsetZone()); // 2024-07-05T10:30:20+08:00
    // 修改了时区信息,但原来的日期时间未发生改变
    System.out.println(zdt1.withZoneSameLocal(ZoneId.of("Asia/Tokyo")));
    //      打印:2024-07-05T10:30:20+09:00[Asia/Tokyo]
    // 修改了时区信息,原来的日期时间也发生了改变
    System.out.println(zdt1.withZoneSameInstant(ZoneId.of("Asia/Tokyo")));
    //      打印:2024-07-05T11:30:20+09:00[Asia/Tokyo]
    // ============================= 日期时间转换方法(to) =============================
    System.out.println(zdt1.toLocalDate());     // 2024-07-05
    System.out.println(zdt1.toLocalTime());     // 10:30:20
    System.out.println(zdt1.toLocalDateTime()); // 2024-07-05T10:30:20
    System.out.println(zdt1.toOffsetDateTime());// 2024-07-05T10:30:20+08:00
    // ================================ 日期时间其它方法 ================================
    System.out.println("zdt1据zdt2多少天:" + zdt1.until(zdt2, ChronoUnit.DAYS));
    //      打印:zdt1据zdt2多少天:0
    System.out.println("zdt1据zdt2多少分:" + zdt1.until(zdt2, ChronoUnit.HOURS));
    //      打印:zdt1据zdt2多少分:10
    // 日期时间的裁剪
    ZonedDateTime zdt3 = zdt1.truncatedTo(ChronoUnit.DAYS);
    ZonedDateTime zdt4 = zdt1.truncatedTo(ChronoUnit.MINUTES);
    System.out.println(zdt3); // 2024-07-05T00:00+08:00[Asia/Shanghai]
    System.out.println(zdt4); // 2024-07-05T10:30+08:00[Asia/Shanghai]
}

十二:OffsetDateTime日期时间(偏移)

  Java 8 引入了 OffsetDateTime 类。它表示具有特定时区偏移量的日期和时间。其内部由两个类组成,分别为:LocalDateTime、ZoneOffset。ZoneOffset包含一个与UTC的偏移量,这个偏移量可以是正数(东经时间)或负数(西经时间)。需要注意的是这个类是不可变的,一旦创建则不能更改。

OffsetDateTime和ZonedDateTime区别:\color{#f00}{OffsetDateTime和ZonedDateTime区别:}OffsetDateTimeZonedDateTime区别:

OffsetDateTimeZonedDateTime都可以表示偏差信息,那么它们具体啥区别,其实有些细微差别:
    OffsetDateTime 日期时间(偏移):
            1:表示的是日期和时间,以及与UTC的偏移量。它不依赖于特定的时区,而是依赖于一个固定的时区偏移量。
            2:它主要用于处理不依赖于特定地理时区的时间戳。
            3:偏移量表示的范围是从-24:0024:00,因为它只表示偏移量,不考虑实际的时区。
    ZonedDateTime 日期时间(时区):
            1:它结合了LocalDateTimeZoneId,其中ZoneId用于表示特定的地理时区,依赖于特定的时区ID
            2:它用于处理依赖于特定地理时区的时间戳。
            3:时区ID必须是一个合法的特定时区,比如“Asia/Shanghai”;它无法创建偏移几分几秒。

一般整点偏移如"GMT+08:00"都可用使用指定的时区ID代替如"Asia/Shanghai"
但是偏移时差如"GMT+08:15"貌似ZonedDateTime就无法代替了。
所有总结:OffsetDateTime可以创建更精准的时间偏移信息,而ZonedDateTime只能创建一些整点时区ID的时间。

(一):方法列表

  其实它的方法和上面说的ZonedDateTime基本上一模一样,下面我按照特殊的方法来简单说明,其它的就可以参考ZonedDateTime来学习使用。

点开查看详情:关于OffsetDateTime对象的基本方法
'下面只是它的部分方法,具体参考ZonedDateTime类,它们之间的方法使用基本一样'
'下面只是它的部分方法,具体参考ZonedDateTime类,它们之间的方法使用基本一样'
'1:静态常量'
    static OffsetDateTime MAX
        支持的最大值;返回:“+999999999-12-31T23:59:59.999999999-18:00
    static OffsetDateTime MIN
        支持的最小值;返回:“999999999-01-01T00:0000 + 18:00
 
'2:日期判断和比较'
    boolean isBefore(ChronoLocalDate other)
        检查当前日期时间是否在指定日期时间之前。true代表是,反之false
    boolean isAfter(ChronoLocalDate other)
        检查当前日期时间是否在指定日期时间之后。true代表是,反之false
    boolean equals(Object obj)
        检查当前日期时间是否等于另一个日期时间,它只能比较同类型的历法日期时间对象
    boolean isEqual(ChronoLocalDate other)
        检查当前日期时间是否等于指定日期,它可以比较不同类型历法的日期时间,只要最终日期时间一样即可。
    int compareTo(OffsetDateTime other)
       将此日期时间与另一个日期时间进行比较。最终会返回:负数、0、正数;
       0代表日期时间相同、正数代表当前日期时间比传入的日期时间大、负数代表当前日期时间比传入的日期时间小。
 
'3:关于日期转换指定格式'
    OffsetTime toOffsetTime()
        将当前的日期时间对象转换为 OffsetTime 对象。
    long toEpochSecond()
        将当前的对象转换为自1970-01-01T00:00:00Z开始的秒数。
    Instant toInstant()
        将此日期时间转换为Instant时刻
    ZonedDateTime toZonedDateTime()
        使用偏移量作为区域ID将此日期时间转换为ZonedDateTime 。
 
'4:关于偏移日期时间的补充修改方法(with)'
    OffsetDateTime withOffsetSameLocal(ZoneOffset offset)
        将日期时间修改成指定的偏差,但不会修改当前对象的时间信息,只替换一个偏差。
    OffsetDateTime withOffsetSameInstant(ZoneOffset offset)
        将日期时间修改成指定的偏差,并同时将当前对象的时间信息转换成对应的时间值。
 
'5:其它方法'
    ZonedDateTime atZoneSimilarLocal(ZoneId zone)
        将此日期时间与时区相结合,以创建一个ZonedDateTime对象,但是不会修改当前的日期时间;
    ZonedDateTime atZoneSameInstant(ZoneId zone)
        将此日期时间与时区相结合,创建一个ZonedDateTime对象,并且同时将日期时间转换成对于时区。
    static OffsetDateTime parse(CharSequence text)
    static OffsetDateTime parse(CharSequence text, DateTimeFormatter formatter)
        用于从一个文本字符串中解析并获取一个OffsetDateTime实例。
        默认只能传如下格式字符串:"2007-12-03T10:15:30+01:00"
        但是若传了formatter对象,则可用自定义指定字符串传入的规范。
    Temporal adjustInto(Temporal temporal)
        调整指定的时间对象与此对象具有相同的偏移量,日期和时间。
    static Comparator〈OffsetDateTime〉 timeLineOrder()
        获取一个比较器,只比较两个OffsetDateTime实例。

(二):基本使用

点开查看详情:关于OffsetDateTime对象的基本使用
public static void main(String[] args) {
    // 创建一个日期时间:2024-07-05T10:30:20
    LocalDateTime ldt = LocalDateTime.of(2024, 7, 5, 10, 30, 20);
    // 创建一个带偏移时差的日期时间:2024-07-05T10:30:20+08:00
    OffsetDateTime odt1 = OffsetDateTime.of(ldt, ZoneOffset.of("+08:00"));
    // ============================== 关于日期时间的转换 ==============================
    // 提取偏差时间 OffsetTime
    System.out.println(odt1.toOffsetTime());    // 10:30:20+08:00
    // 转换为从纪元开始计算的秒数
    System.out.println(odt1.toEpochSecond());   // 1720146620
    // 将此日期时间转换为Instant时刻
    System.out.println(odt1.toInstant());       // 2024-07-05T02:30:20Z
    // 转换为ZonedDateTime对象
    System.out.println(odt1.toZonedDateTime()); // 2024-07-05T10:30:20+08:00
    // ============================== 关于日期时间的修改 ==============================
    // 修改对于偏移量,但不修改时间信息,如下打印:2024-07-05T10:30:20+09:00
    System.out.println(odt1.withOffsetSameLocal(ZoneOffset.of("+09:00")));
    // 修改对于偏移量,并且修改时间信息,如下打印:2024-07-05T11:30:20+09:00
    System.out.println(odt1.withOffsetSameInstant(ZoneOffset.of("+09:00")));
    // ============================= 关于日期时间其它方法 =============================
    // 从偏移时间中设置时区来转变成ZonedDateTime;并且不会改变原时间
    System.out.println(odt1.atZoneSimilarLocal(ZoneId.of("Asia/Tokyo")));
    //      打印:2024-07-05T10:30:20+09:00[Asia/Tokyo]
    // 从偏移时间中设置时区来转变成ZonedDateTime;并且会按照时区来改变原日期时间
    System.out.println(odt1.atZoneSameInstant(ZoneId.of("Asia/Tokyo")));
    //      打印:2024-07-05T11:30:20+09:00[Asia/Tokyo]
}

结尾END


转载自:https://juejin.cn/post/7396325195072602150
评论
请登录