Java —— 日期时间 API

一、java.util.Date

在 JDK 1.1 之前, Date 有两个附加功能。 它允许将日期解释为年,月,日,小时,分钟和第二个值。 它还允许格式化和解析日期字符串。 不幸的是,这些功能的 API 不适合国际化。 从 JDK 1.1 开始, Calendar 类应该用于在日期和时间字段之间进行转换,并且 DateFormat 类应用于格式化和解析日期字符串

1、构造函数

// 分配一个 Date对象,并初始化它,以便它代表它被分配的时间,测量到最近的毫秒。
Date()
// 分配一个 Date对象,并将其初始化为表示自称为“时代”的标准基准时间以后的指定毫秒数,即1970年1月1日00:00:00 GMT。
Date(long date)

2、常用方法

// 测试此日期是否在指定日期之后。
boolean	after(Date when)
// 测试此日期是否在指定日期之前。
boolean	before(Date when)
// 返回此对象的副本。
Object	clone()
// 比较两个日期进行订购。
int	compareTo(Date anotherDate)
// 比较两个日期来平等。
boolean	equals(Object obj)
// 从 Instant对象获取一个 Date的实例。
static Date	from(Instant instant)
// 设置此 Date对象以表示1970年1月1日00:00:00 GMT后的 time毫秒的时间点。
void setTime(long time)
// 以毫秒数返回从 1970 年 01 月 01 日 00 时到目前的时间。
long getTime()	
// 将此 Date对象转换为 Instant 。
Instant	toInstant()
// 将此 Date对象转换为 String的形式
String	toString()

二、java.sql.Date

Java 的 java.sql.Date 类在 JDBC API 中被使用。如果你需要在 java.sql.PreparedStatement 上设置日期或者从 java.sql.ResultSet 获取日期,你将会和 java.sql.Date 打交道
实际上,java.sql.Date 继承于 java.util.Date,所以你可以用 java.util.Date 做的任何事同样适用于 java.sql.Date。

java.sql.Date 和 java.util.Date 最大的不同在于 java.sql.Date 所表示的日期中只保留了日期,而没有时间。举个例子,如果你用 2009-12-24 23:20 来创建一个 java.sql.Date,那么其中的时间(23:20)将会被切掉。如果你需要保留时间,使用 java.sql.Timestamp 来代替 java.sql.Date。

三、java.util.Calendar

Calendar 日历类是弥补了 Date 类的缺陷的一个表示日历的抽象类并能操纵该日历。
Calendar 不能直接创建对象,但可以使用静态方法 getInstance() 获得代表当前日期的实例
Calendar 对象可以产生实现特定语言和日历风格的日期时间格式化所需的所有日历字段值(例如日语 - 公历,日语 - 繁体)。

1、获得 Calendar 对象

Calender 类是一个表示日历的抽象类,不能直接实例化,程序只能创建 calender 子类的实例。但是可以使用静态方法 getInstance() 获得代表当前日期的实例

Calendar ca = Calendar.getInstance();	// 获取自身的时间对象

2、常用方法

// 返回给定日历字段的值,参数 field 的值由 Calendar 类的静态常量决定。其中:YEAR 代表年,MONTH 代表月,HOUR 代表小时,MINUTE 代表分等。 0 代表当前日历是一月份,如果返回 1 代表二月份,依此类推。
int	get(int field)
// 根据日历的规则,将指定的时间量添加或减去给定的日历字段。
abstract void add(int field, int amount)
// 返回 Calendar是否 Calendar指定时间之后的时间 Object 。
boolean	after(Object when)
// 返回此 Calendar是否 Calendar指定的时间之前指定的时间 Object 。
boolean	before(Object when)
// 对调用对象包含的所有时间组成部分清零
void clear()
// 对调用对象所指定的字段部分清零
void clear(int field)
// 创建并返回此对象的副本。
Object	clone()
// 比较时间值(从毫秒偏移量 Epoch由两个表示) Calendar对象。
int	compareTo(Calendar anotherCalendar)
// 填写日历字段中的任何未设置的字段。
protected void	complete()
// 当前毫秒时间值转换为time的日历字段值 fields[] 。
protected abstract void	computeFields()
// 将 fields[]中的当前日历字段值 转换为毫秒时间值 time 。
protected abstract void	computeTime()
// 如果调用 Calendar 对象所包含的日期和 obj 指定的对象所包含的日期相等,返回 true,否则返回 false
boolean	equals(Object obj)
// 返回一个 Set ,其中包含运行时环境中Set支持的所有日历类型。
static Set<String>	getAvailableCalendarTypes()
// 返回一个所有区域设置的数组,该类的 getInstance 方法可以返回本地化实例。
static Locale[]	getAvailableLocales()
// 返回此 Calendar的日历类型。
String	getCalendarType()
// 返回给定的 style和 locale中的日历 field的字符串表示
String getDisplayName(int field, int style, Locale locale)
// 返回 Map包含日历的所有名称 field在给定 style和 locale及其相应的字段值。
Map<String,Integer>	getDisplayNames(int field, int style, Locale locale)
// 得到一周的第一天是什么 例如, SUNDAY在美国, MONDAY在法国。
int	getFirstDayOfWeek()
// 返回此 Calendar实例的给定日历字段的最高最小值。
abstract int	getGreatestMinimum(int field)
// 使用默认时区和区域设置获取日历。
static Calendar	getInstance()
// 使用默认时区和指定的区域设置获取日历。
static Calendar	getInstance(Locale aLocale)
// 使用指定的时区和默认语言环境获取日历。
static Calendar	getInstance(TimeZone zone)
// 获取具有指定时区和区域设置的日历。
static Calendar	getInstance(TimeZone zone, Locale aLocale)
// 返回此 Calendar实例的给定日历字段的最低最大值。
abstract int getLeastMaximum(int field)
// 返回此 Calendar实例的给定日历字段的最大值
abstract int getMaximum(int field)
// 获得一年中第一周所需的最低限度的日子; 例如,如果第一周被定义为包含一年中的第一个月的第一个星期,则此方法返回1。
int	getMinimalDaysInFirstWeek()
// 返回此 Calendar实例的给定日历字段的最小值
abstract int getMinimum(int field)
// 返回一个和调用对象时间相等的 Date 对象
Date getTime()
// 以毫秒为单位返回此日历的时间值。
long getTimeInMillis()
// 获取时区。
TimeZone getTimeZone()
// 返回由这个 Calendar 表示的星期内的星期 Calendar 。
int	getWeeksInWeekYear()
// 返回这个 Calendar 。
int	getWeekYear()
// 返回此日历的哈希码。
int	hashCode()
// 返回给定日历字段的值。
protected int internalGet(int field)
// 告诉日期/时间的解释是否宽松。
boolean	isLenient()
// 确定给定的日历字段是否具有值集
boolean	isSet(int field)
// 返回此 Calendar是否支持周日期。
boolean	isWeekDateSupported()
// 将给定的日历字段设置为给定的值。
void set(int field, int value)
// 设置日历字段中的值 YEAR , MONTH和 DAY_OF_MONTH 。
void set(int year, int month, int date)
// 设置日历字段中的值 YEAR , MONTH , DAY_OF_MONTH , HOUR_OF_DAY和 MINUTE 。
void set(int year, int month, int date, int hourOfDay, int minute)
// 设置字段中的值 YEAR , MONTH , DAY_OF_MONTH , HOUR_OF_DAY , MINUTE和 SECOND 。
void set(int year, int month, int date, int hourOfDay, int minute, int second)
// 设置一周的第一天是什么? 例如, SUNDAY在美国, MONDAY在法国。
void setFirstDayOfWeek(int value)
// 设定一年中第一个星期所需的最短时间是多少? 例如,如果第一周被定义为包含一年中第一个月的第一天的第一周,请调用此值为1的方法。
void setMinimalDaysInFirstWeek(int value)
// 使用给定的 Date设置此日历的时间。
void setTime(Date date)
// 从给定的值设置此日历的当前时间。
void setTimeInMillis(long millis)
// 以给定的时区值设置时区。
void setTimeZone(TimeZone value)
// 设置这个 Calendar的日期与给定的日期说明符 - 周年,年周和星期几。
void setWeekDate(int weekYear, int weekOfYear, int dayOfWeek)
// 将此对象转换为Instant 。
Instant	toInstant()
// 返回此日历的字符串表示形式。
String	toString()

四、java.text.DateFormat

DateFormat 是日期/时间格式化子类的抽象类,它以语言无关的方式格式化和分析日期或时间。 日期/时间格式化子类(唯一子类:SimpleDateFormat )允许格式化(即日期文本),解析(文本日期)和归一化。

1、DateFormat

DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。

(1)获得对象

DateFormat 没有构造函数,因此在创建 DateFormat 对象时不能使用 new 关键字,而应该使用 DateFormat 类中的静态方法 getDateInstance(),示例代码如下:

DateFormat df = DateFormat.getDatelnstance(); 

在创建了一个 DateFormat 对象后,可以调用该对象中的方法来对日期/时间进行格式化。DateFormat 类中常用方法如表 1 所示。
在这里插入图片描述
格式化样式主要通过 DateFormat 常量设置。将不同的常量传入到表 1 所示的方法中,以控制结果的长度。DateFormat 类的常量如下。
在这里插入图片描述
使用 DateFormat 类格式化曰期/时间的示例如下:

// 获取不同格式化风格和中国环境的日期
DateFormat df1 = DateFormat.getDateInstance(DateFormat.SHORT, Locale.CHINA);
DateFormat df2 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA);
DateFormat df3 = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.CHINA);
DateFormat df4 = DateFormat.getDateInstance(DateFormat.LONG, Locale.CHINA);
// 获取不同格式化风格和中国环境的时间
DateFormat df5 = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.CHINA);
DateFormat df6 = DateFormat.getTimeInstance(DateFormat.FULL, Locale.CHINA);
DateFormat df7 = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.CHINA);
DateFormat df8 = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA);
// 将不同格式化风格的日期格式化为日期字符串
String date1 = df1.format(new Date());
String date2 = df2.format(new Date());
String date3 = df3.format(new Date());
String date4 = df4.format(new Date());
// 将不同格式化风格的时间格式化为时间字符串
String time1 = df5.format(new Date());
String time2 = df6.format(new Date());
String time3 = df7.format(new Date());
String time4 = df8.format(new Date());
// 输出日期
System.out.println("SHORT:" + date1 + " " + time1);
System.out.println("FULL:" + date2 + " " + time2);
System.out.println("MEDIUM:" + date3 + " " + time3);
System.out.println("LONG:" + date4 + " " + time4);

运行该段代码,输出的结果如下:
在这里插入图片描述

2、SimpleDateFormat

如果使用 DateFormat 类格式化日期/时间并不能满足要求,那么就需要使用 DateFormat 类的子类——SimpleDateFormat。

SimpleDateFormat 是一个以与语言环境有关的方式来格式化和解析日期的具体类,它允许进行格式化(日期→文本)、解析(文本→日期)和规范化。SimpleDateFormat 使得可以选择任何用户定义的日期/时间格式的模式

(1)SimpleDateFormat 类构造函数

// 构造一个 SimpleDateFormat使用默认模式和日期格式符号为默认的 FORMAT区域设置。
SimpleDateFormat()
// 使用给定模式 SimpleDateFormat并使用默认的 FORMAT语言环境的默认日期格式符号。
SimpleDateFormat(String pattern)
// 使用给定的模式和日期格式符号构造一个 SimpleDateFormat 。
SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
// 构造一个 SimpleDateFormat使用给定的模式和给定的区域设置的默认日期格式符号。
SimpleDateFormat(String pattern, Locale locale)

(2)SimpleDateFormat 类方法

// 将给定的本地化模式字符串应用于此日期格式。
void applyLocalizedPattern(String pattern)
// 将给定的模式字符串应用于此日期格式。
void applyPattern(String pattern)
// 创建一个这个 SimpleDateFormat的副本。
Object clone()
// 将给定的对象与此 SimpleDateFormat进行比较以获得相等性。
boolean	equals(Object obj)
// 将给定的 Date 日期/时间字符串转换成指定格式,并将结果追加到给定的 StringBuffer 。
StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos)
// 格式化一个对象,生成一个 AttributedCharacterIterator 。
AttributedCharacterIterator	formatToCharacterIterator(Object obj)
// 返回100年期间的开始日期,2位数年份被解释为在内。
Date get2DigitYearStart()
// 获取此日期格式的日期和时间格式符号的副本。
DateFormatSymbols getDateFormatSymbols()
// 返回此 SimpleDateFormat对象的哈希码值。
int	hashCode()
// 从字符串中解析文本以产生一个 Date 。
Date parse(String text, ParsePosition pos)
// 设置100年期间,两位数年份将被解释为在用户指定的日期开始。
void set2DigitYearStart(Date startDate)
// 设置此日期格式的日期和时间格式符号。
void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
// 返回描述此日期格式的本地化模式字符串。
String	toLocalizedPattern()
// 返回描述此日期格式的模式字符串。
String	toPattern()

SimpleDateFormat 自定义格式中常用的字母及含义如表所示:
在这里插入图片描述
编写 Java 程序,使用 SimpleDateFormat 类格式化当前日期并打印,日期格式为“xxxx 年 xx 月 xx 日星期 xxx 点 xx 分 xx 秒”,具体的实现代码如下:

import java.text.SimpleDateFormat;
import java.util.Date;
public class Test13 {
    public static void main(String[] args) {
        Date now = new Date(); // 创建一个Date对象,获取当前时间
        // 指定格式化格式
        SimpleDateFormat f = new SimpleDateFormat("今天是 " + "yyyy 年 MM 月 dd 日 E HH 点 mm 分 ss 秒");
        System.out.println(f.format(now)); // 将当前时间袼式化为指定的格式
    }
}

该程序的运行结果如下:
在这里插入图片描述

五、Java 8 的日期类和时间类

在 JDK8 之前,我们经常使用到的时间API包括(Date、Calendar),Date 与字符串之间的转换使用 SimpleDateFormat 进行转换(parse()、format() 方法),然而 SimpleDateFormat 不是线程安全的。在设计上也是存在一些缺陷的,比如有两个 Date 类,一个在 java.util 包中,一个在 java.sql 包中。
在 JDK8 中,引入了一套全新的时间日期 API,这套 API 在设计上比较合理,使用时间操作也变得更加方便,并且支持多线程安全操作
新的日期及时间 API 位于 java.time 包下,如下是一些该包下的关键类:
在这里插入图片描述

1、Java8 日期和时间类

LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601 日历系统的日期、时间、日期和时间。他们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
对日期时间的修改,就是对已经存在的 LocalDate 对象,根据需求创建它的修改版,最简单的方式是使用 withAttribute() 方法。withAttribute() 方法会创建对象的一个副本,并按照需要修改它的属性

(1)LocalTime

LocalTime 是一个不可变的日期时间对象,代表一个时间,包含时分秒。 时间表示为纳秒精度。
例如,值“13:45.30.123456789”可以存储在 LocalTime 。它不存储或表示日期或时区。 这个类是不可变的和线程安全的。

public final class LocalTime implements Temporal, TemporalAdjuster, Comparable<LocalTime>, Serializable {

    public static final LocalTime MIN;
   
    public static final LocalTime MAX;
   
    public static final LocalTime MIDNIGHT;
    
    public static final LocalTime NOON;
    
    private static final LocalTime[] HOURS = new LocalTime[24];
    static {
        for (int i = 0; i < HOURS.length; i++) {
            HOURS[i] = new LocalTime(i, 0, 0, 0);
        }
        MIDNIGHT = HOURS[0];
        NOON = HOURS[12];
        MIN = HOURS[0];
        MAX = new LocalTime(23, 59, 59, 999_999_999);
    }

    static final int HOURS_PER_DAY = 24;
    
    static final int MINUTES_PER_HOUR = 60;
    
    static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
   
    static final int SECONDS_PER_MINUTE = 60;
    
    static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
    
    static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
  
    static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L;
   
    static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
    
    static final long NANOS_PER_SECOND = 1000_000_000L;
   
    static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
  
    static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
  
    static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY;

    private static final long serialVersionUID = 6414437269572265201L;

    private final byte hour;
   
    private final byte minute;
   
    private final byte second;
   
    private final int nano;

	// 从默认时区的系统时钟获取当前时间。
    public static LocalTime now() {
        return now(Clock.systemDefaultZone());
    }

	// 从指定时区的系统时钟获取当前时间。
    public static LocalTime now(ZoneId zone) {
        return now(Clock.system(zone));
    }

	// 从指定的时钟获取当前时间。
    public static LocalTime now(Clock clock) {
        Objects.requireNonNull(clock, "clock");
        // inline OffsetTime factory to avoid creating object and InstantProvider checks
        final Instant now = clock.instant();  // called once
        ZoneOffset offset = clock.getZone().getRules().getOffset(now);
        long localSecond = now.getEpochSecond() + offset.getTotalSeconds();  // overflow caught later
        int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
        return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + now.getNano());
    }

    public static LocalTime of(int hour, int minute) {
        HOUR_OF_DAY.checkValidValue(hour);
        if (minute == 0) {
            return HOURS[hour];  // for performance
        }
        MINUTE_OF_HOUR.checkValidValue(minute);
        return new LocalTime(hour, minute, 0, 0);
    }

    public static LocalTime of(int hour, int minute, int second) {
        HOUR_OF_DAY.checkValidValue(hour);
        if ((minute | second) == 0) {
            return HOURS[hour];  // for performance
        }
        MINUTE_OF_HOUR.checkValidValue(minute);
        SECOND_OF_MINUTE.checkValidValue(second);
        return new LocalTime(hour, minute, second, 0);
    }

	// 根据时,分,秒和纳秒获取 LocalTime 实例。
    public static LocalTime of(int hour, int minute, int second, int nanoOfSecond) {
        HOUR_OF_DAY.checkValidValue(hour);
        MINUTE_OF_HOUR.checkValidValue(minute);
        SECOND_OF_MINUTE.checkValidValue(second);
        NANO_OF_SECOND.checkValidValue(nanoOfSecond);
        return create(hour, minute, second, nanoOfSecond);
    }

    public static LocalTime ofSecondOfDay(long secondOfDay) {
        SECOND_OF_DAY.checkValidValue(secondOfDay);
        int hours = (int) (secondOfDay / SECONDS_PER_HOUR);
        secondOfDay -= hours * SECONDS_PER_HOUR;
        int minutes = (int) (secondOfDay / SECONDS_PER_MINUTE);
        secondOfDay -= minutes * SECONDS_PER_MINUTE;
        return create(hours, minutes, (int) secondOfDay, 0);
    }

    public static LocalTime ofNanoOfDay(long nanoOfDay) {
        NANO_OF_DAY.checkValidValue(nanoOfDay);
        int hours = (int) (nanoOfDay / NANOS_PER_HOUR);
        nanoOfDay -= hours * NANOS_PER_HOUR;
        int minutes = (int) (nanoOfDay / NANOS_PER_MINUTE);
        nanoOfDay -= minutes * NANOS_PER_MINUTE;
        int seconds = (int) (nanoOfDay / NANOS_PER_SECOND);
        nanoOfDay -= seconds * NANOS_PER_SECOND;
        return create(hours, minutes, seconds, (int) nanoOfDay);
    }

    public static LocalTime from(TemporalAccessor temporal) {
        Objects.requireNonNull(temporal, "temporal");
        LocalTime time = temporal.query(TemporalQueries.localTime());
        if (time == null) {
            throw new DateTimeException("Unable to obtain LocalTime from TemporalAccessor: " +
                    temporal + " of type " + temporal.getClass().getName());
        }
        return time;
    }

    public static LocalTime parse(CharSequence text) {
        return parse(text, DateTimeFormatter.ISO_LOCAL_TIME);
    }

    public static LocalTime parse(CharSequence text, DateTimeFormatter formatter) {
        Objects.requireNonNull(formatter, "formatter");
        return formatter.parse(text, LocalTime::from);
    }

    private static LocalTime create(int hour, int minute, int second, int nanoOfSecond) {
        if ((minute | second | nanoOfSecond) == 0) {
            return HOURS[hour];
        }
        return new LocalTime(hour, minute, second, nanoOfSecond);
    }

    private LocalTime(int hour, int minute, int second, int nanoOfSecond) {
        this.hour = (byte) hour;
        this.minute = (byte) minute;
        this.second = (byte) second;
        this.nano = nanoOfSecond;
    }

    @Override
    public boolean isSupported(TemporalField field) {
        if (field instanceof ChronoField) {
            return field.isTimeBased();
        }
        return field != null && field.isSupportedBy(this);
    }

    @Override  // override for Javadoc
    public boolean isSupported(TemporalUnit unit) {
        if (unit instanceof ChronoUnit) {
            return unit.isTimeBased();
        }
        return unit != null && unit.isSupportedBy(this);
    }

    @Override  // override for Javadoc
    public ValueRange range(TemporalField field) {
        return Temporal.super.range(field);
    }

    @Override  // override for Javadoc and performance
    public int get(TemporalField field) {
        if (field instanceof ChronoField) {
            return get0(field);
        }
        return Temporal.super.get(field);
    }

    @Override
    public long getLong(TemporalField field) {
        if (field instanceof ChronoField) {
            if (field == NANO_OF_DAY) {
                return toNanoOfDay();
            }
            if (field == MICRO_OF_DAY) {
                return toNanoOfDay() / 1000;
            }
            return get0(field);
        }
        return field.getFrom(this);
    }

    private int get0(TemporalField field) {
        switch ((ChronoField) field) {
            case NANO_OF_SECOND: return nano;
            case NANO_OF_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'NanoOfDay' for get()
method, use getLong() instead");
            case MICRO_OF_SECOND: return nano / 1000;
            case MICRO_OF_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'MicroOfDay' for get()
method, use getLong() instead");
            case MILLI_OF_SECOND: return nano / 1000_000;
            case MILLI_OF_DAY: return (int) (toNanoOfDay() / 1000_000);
            case SECOND_OF_MINUTE: return second;
            case SECOND_OF_DAY: return toSecondOfDay();
            case MINUTE_OF_HOUR: return minute;
            case MINUTE_OF_DAY: return hour * 60 + minute;
            case HOUR_OF_AMPM: return hour % 12;
            case CLOCK_HOUR_OF_AMPM: int ham = hour % 12; return (ham % 12 == 0 ? 12 : ham);
            case HOUR_OF_DAY: return hour;
            case CLOCK_HOUR_OF_DAY: return (hour == 0 ? 24 : hour);
            case AMPM_OF_DAY: return hour / 12;
        }
        throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    }

    public int getHour() {
        return hour;
    }

    public int getMinute() {
        return minute;
    }

    public int getSecond() {
        return second;
    }

    public int getNano() {
        return nano;
    }

    @Override
    public LocalTime with(TemporalAdjuster adjuster) {
        // optimizations
        if (adjuster instanceof LocalTime) {
            return (LocalTime) adjuster;
        }
        return (LocalTime) adjuster.adjustInto(this);
    }

    @Override
    public LocalTime with(TemporalField field, long newValue) {
        if (field instanceof ChronoField) {
            ChronoField f = (ChronoField) field;
            f.checkValidValue(newValue);
            switch (f) {
                case NANO_OF_SECOND: return withNano((int) newValue);
                case NANO_OF_DAY: return LocalTime.ofNanoOfDay(newValue);
                case MICRO_OF_SECOND: return withNano((int) newValue * 1000);
                case MICRO_OF_DAY: return LocalTime.ofNanoOfDay(newValue * 1000);
                case MILLI_OF_SECOND: return withNano((int) newValue * 1000_000);
                case MILLI_OF_DAY: return LocalTime.ofNanoOfDay(newValue * 1000_000);
                case SECOND_OF_MINUTE: return withSecond((int) newValue);
                case SECOND_OF_DAY: return plusSeconds(newValue - toSecondOfDay());
                case MINUTE_OF_HOUR: return withMinute((int) newValue);
                case MINUTE_OF_DAY: return plusMinutes(newValue - (hour * 60 + minute));
                case HOUR_OF_AMPM: return plusHours(newValue - (hour % 12));
                case CLOCK_HOUR_OF_AMPM: return plusHours((newValue == 12 ? 0 : newValue) - (hour % 12));
                case HOUR_OF_DAY: return withHour((int) newValue);
                case CLOCK_HOUR_OF_DAY: return withHour((int) (newValue == 24 ? 0 : newValue));
                case AMPM_OF_DAY: return plusHours((newValue - (hour / 12)) * 12);
            }
            throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
        }
        return field.adjustInto(this, newValue);
    }

    public LocalTime withHour(int hour) {
        if (this.hour == hour) {
            return this;
        }
        HOUR_OF_DAY.checkValidValue(hour);
        return create(hour, minute, second, nano);
    }

    public LocalTime withMinute(int minute) {
        if (this.minute == minute) {
            return this;
        }
        MINUTE_OF_HOUR.checkValidValue(minute);
        return create(hour, minute, second, nano);
    }

    public LocalTime withSecond(int second) {
        if (this.second == second) {
            return this;
        }
        SECOND_OF_MINUTE.checkValidValue(second);
        return create(hour, minute, second, nano);
    }

    public LocalTime withNano(int nanoOfSecond) {
        if (this.nano == nanoOfSecond) {
            return this;
        }
        NANO_OF_SECOND.checkValidValue(nanoOfSecond);
        return create(hour, minute, second, nanoOfSecond);
    }

    public LocalTime truncatedTo(TemporalUnit unit) {
        if (unit == ChronoUnit.NANOS) {
            return this;
        }
        Duration unitDur = unit.getDuration();
        if (unitDur.getSeconds() > SECONDS_PER_DAY) {
            throw new UnsupportedTemporalTypeException("Unit is too large to be used for truncation");
        }
        long dur = unitDur.toNanos();
        if ((NANOS_PER_DAY % dur) != 0) {
            throw new UnsupportedTemporalTypeException("Unit must divide into a standard day without remainder");
        }
        long nod = toNanoOfDay();
        return ofNanoOfDay((nod / dur) * dur);
    }

    @Override
    public LocalTime plus(TemporalAmount amountToAdd) {
        return (LocalTime) amountToAdd.addTo(this);
    }

    @Override
    public LocalTime plus(long amountToAdd, TemporalUnit unit) {
        if (unit instanceof ChronoUnit) {
            switch ((ChronoUnit) unit) {
                case NANOS: return plusNanos(amountToAdd);
                case MICROS: return plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
                case MILLIS: return plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000_000);
                case SECONDS: return plusSeconds(amountToAdd);
                case MINUTES: return plusMinutes(amountToAdd);
                case HOURS: return plusHours(amountToAdd);
                case HALF_DAYS: return plusHours((amountToAdd % 2) * 12);
            }
            throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
        }
        return unit.addTo(this, amountToAdd);
    }

    public LocalTime plusHours(long hoursToAdd) {
        if (hoursToAdd == 0) {
            return this;
        }
        int newHour = ((int) (hoursToAdd % HOURS_PER_DAY) + hour + HOURS_PER_DAY) % HOURS_PER_DAY;
        return create(newHour, minute, second, nano);
    }

    public LocalTime plusMinutes(long minutesToAdd) {
        if (minutesToAdd == 0) {
            return this;
        }
        int mofd = hour * MINUTES_PER_HOUR + minute;
        int newMofd = ((int) (minutesToAdd % MINUTES_PER_DAY) + mofd + MINUTES_PER_DAY) % MINUTES_PER_DAY;
        if (mofd == newMofd) {
            return this;
        }
        int newHour = newMofd / MINUTES_PER_HOUR;
        int newMinute = newMofd % MINUTES_PER_HOUR;
        return create(newHour, newMinute, second, nano);
    }

    public LocalTime plusSeconds(long secondstoAdd) {
        if (secondstoAdd == 0) {
            return this;
        }
        int sofd = hour * SECONDS_PER_HOUR +
                    minute * SECONDS_PER_MINUTE + second;
        int newSofd = ((int) (secondstoAdd % SECONDS_PER_DAY) + sofd + SECONDS_PER_DAY) % SECONDS_PER_DAY;
        if (sofd == newSofd) {
            return this;
        }
        int newHour = newSofd / SECONDS_PER_HOUR;
        int newMinute = (newSofd / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
        int newSecond = newSofd % SECONDS_PER_MINUTE;
        return create(newHour, newMinute, newSecond, nano);
    }

    public LocalTime plusNanos(long nanosToAdd) {
        if (nanosToAdd == 0) {
            return this;
        }
        long nofd = toNanoOfDay();
        long newNofd = ((nanosToAdd % NANOS_PER_DAY) + nofd + NANOS_PER_DAY) % NANOS_PER_DAY;
        if (nofd == newNofd) {
            return this;
        }
        int newHour = (int) (newNofd / NANOS_PER_HOUR);
        int newMinute = (int) ((newNofd / NANOS_PER_MINUTE) % MINUTES_PER_HOUR);
        int newSecond = (int) ((newNofd / NANOS_PER_SECOND) % SECONDS_PER_MINUTE);
        int newNano = (int) (newNofd % NANOS_PER_SECOND);
        return create(newHour, newMinute, newSecond, newNano);
    }

    @Override
    public LocalTime minus(TemporalAmount amountToSubtract) {
        return (LocalTime) amountToSubtract.subtractFrom(this);
    }

    @Override
    public LocalTime minus(long amountToSubtract, TemporalUnit unit) {
        return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract,
unit));
    }

    public LocalTime minusHours(long hoursToSubtract) {
        return plusHours(-(hoursToSubtract % HOURS_PER_DAY));
    }

    public LocalTime minusMinutes(long minutesToSubtract) {
        return plusMinutes(-(minutesToSubtract % MINUTES_PER_DAY));
    }

    public LocalTime minusSeconds(long secondsToSubtract) {
        return plusSeconds(-(secondsToSubtract % SECONDS_PER_DAY));
    }

    public LocalTime minusNanos(long nanosToSubtract) {
        return plusNanos(-(nanosToSubtract % NANOS_PER_DAY));
    }

    @SuppressWarnings("unchecked")
    @Override
    public <R> R query(TemporalQuery<R> query) {
        if (query == TemporalQueries.chronology() || query == TemporalQueries.zoneId() ||
                query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
            return null;
        } else if (query == TemporalQueries.localTime()) {
            return (R) this;
        } else if (query == TemporalQueries.localDate()) {
            return null;
        } else if (query == TemporalQueries.precision()) {
            return (R) NANOS;
        }
        // inline TemporalAccessor.super.query(query) as an optimization
        // non-JDK classes are not permitted to make this optimization
        return query.queryFrom(this);
    }

    @Override
    public Temporal adjustInto(Temporal temporal) {
        return temporal.with(NANO_OF_DAY, toNanoOfDay());
    }

    @Override
    public long until(Temporal endExclusive, TemporalUnit unit) {
        LocalTime end = LocalTime.from(endExclusive);
        if (unit instanceof ChronoUnit) {
            long nanosUntil = end.toNanoOfDay() - toNanoOfDay();  // no overflow
            switch ((ChronoUnit) unit) {
                case NANOS: return nanosUntil;
                case MICROS: return nanosUntil / 1000;
                case MILLIS: return nanosUntil / 1000_000;
                case SECONDS: return nanosUntil / NANOS_PER_SECOND;
                case MINUTES: return nanosUntil / NANOS_PER_MINUTE;
                case HOURS: return nanosUntil / NANOS_PER_HOUR;
                case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
            }
            throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
        }
        return unit.between(this, end);
    }

    public String format(DateTimeFormatter formatter) {
        Objects.requireNonNull(formatter, "formatter");
        return formatter.format(this);
    }

    public LocalDateTime atDate(LocalDate date) {
        return LocalDateTime.of(date, this);
    }

    public OffsetTime atOffset(ZoneOffset offset) {
        return OffsetTime.of(this, offset);
    }

    public int toSecondOfDay() {
        int total = hour * SECONDS_PER_HOUR;
        total += minute * SECONDS_PER_MINUTE;
        total += second;
        return total;
    }

    public long toNanoOfDay() {
        long total = hour * NANOS_PER_HOUR;
        total += minute * NANOS_PER_MINUTE;
        total += second * NANOS_PER_SECOND;
        total += nano;
        return total;
    }

    @Override
    public int compareTo(LocalTime other) {
        int cmp = Integer.compare(hour, other.hour);
        if (cmp == 0) {
            cmp = Integer.compare(minute, other.minute);
            if (cmp == 0) {
                cmp = Integer.compare(second, other.second);
                if (cmp == 0) {
                    cmp = Integer.compare(nano, other.nano);
                }
            }
        }
        return cmp;
    }

    public boolean isAfter(LocalTime other) {
        return compareTo(other) > 0;
    }

    public boolean isBefore(LocalTime other) {
        return compareTo(other) < 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof LocalTime) {
            LocalTime other = (LocalTime) obj;
            return hour == other.hour && minute == other.minute &&
                    second == other.second && nano == other.nano;
        }
        return false;
    }

    @Override
    public int hashCode() {
        long nod = toNanoOfDay();
        return (int) (nod ^ (nod >>> 32));
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(18);
        int hourValue = hour;
        int minuteValue = minute;
        int secondValue = second;
        int nanoValue = nano;
        buf.append(hourValue < 10 ? "0" : "").append(hourValue)
            .append(minuteValue < 10 ? ":0" : ":").append(minuteValue);
        if (secondValue > 0 || nanoValue > 0) {
            buf.append(secondValue < 10 ? ":0" : ":").append(secondValue);
            if (nanoValue > 0) {
                buf.append('.');
                if (nanoValue % 1000_000 == 0) {
                    buf.append(Integer.toString((nanoValue / 1000_000) + 1000).substring(1));
                } else if (nanoValue % 1000 == 0) {
                    buf.append(Integer.toString((nanoValue / 1000) + 1000_000).substring(1));
                } else {
                    buf.append(Integer.toString((nanoValue) + 1000_000_000).substring(1));
                }
            }
        }
        return buf.toString();
    }

    private Object writeReplace() {
        return new Ser(Ser.LOCAL_TIME_TYPE, this);
    }

    private void readObject(ObjectInputStream s) throws InvalidObjectException {
        throw new InvalidObjectException("Deserialization via serialization delegate");
    }

    void writeExternal(DataOutput out) throws IOException {
        if (nano == 0) {
            if (second == 0) {
                if (minute == 0) {
                    out.writeByte(~hour);
                } else {
                    out.writeByte(hour);
                    out.writeByte(~minute);
                }
            } else {
                out.writeByte(hour);
                out.writeByte(minute);
                out.writeByte(~second);
            }
        } else {
            out.writeByte(hour);
            out.writeByte(minute);
            out.writeByte(second);
            out.writeInt(nano);
        }
    }

    static LocalTime readExternal(DataInput in) throws IOException {
        int hour = in.readByte();
        int minute = 0;
        int second = 0;
        int nano = 0;
        if (hour < 0) {
            hour = ~hour;
        } else {
            minute = in.readByte();
            if (minute < 0) {
                minute = ~minute;
            } else {
                second = in.readByte();
                if (second < 0) {
                    second = ~second;
                } else {
                    nano = in.readInt();
                }
            }
        }
        return LocalTime.of(hour, minute, second, nano);
    }

}

(2)LocalDate

LocalDate 是一个不可变的日期时间对象,表示日期,包含年月日。 也可以访问其他日期字段,例如日期,星期几和星期。 例如,值“2007年10月2日”可存储在 LocalDate 。
这个类是不可变的和线程安全的。 该类不存储或表示时间或时区

(3)LocalDateTime

LocalDateTime 是一个不可变的日期时间对象,代表日期时间,包含年月日时分秒。 也可以访问其他日期和时间字段,例如日期,星期几和星期。 时间精度为纳秒精度
例如,值“2007年10月2日在13:45.30.123456789”可以存储在LocalDateTime 。 该类不存储或表示时区
这个类是不可变的和线程安全的

(4)ZonedDateTime

ZonedDateTime 是具有时区的日期时间的不可变表示。 此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。 例如,值“2007年10月2日在13:45.30.123456789 +欧洲/巴黎时区的+02:00”可以存储在ZonedDateTime。
ZonedDateTime 拥有相当于三个独立对象的状态,一个 LocalDateTime ,一个 ZoneId 和
ZoneOffset 。 偏移量和本地日期时间用于在必要时定义一个瞬间。 区域 ID 用于获取偏移量如何以及何时更改的规则。 偏移不能自由设置,因为区域控制哪些偏移是有效的。
这个类是不可变的和线程安全的

以下所有方法都返回了一个修改属性的对象,它们并不会影响原来的日期对象。(即:修改后的日期与原来的日期不是一个对象,原日期不受影响)


public class DateDemo02 {
 
    // LocalDate 日期类(年月日)
    @Test
    public void testLocalDate() {
        //获取当前日期
        LocalDate now = LocalDate.now();
        //指定日期 LocalDate.of(year,month,day)
        LocalDate date = LocalDate.of(2008, 8, 8);
        //获取年
        System.out.println("年:" + date.getYear());
        //获取月(英文)
        System.out.println("月(英文):" + date.getMonth());
        //获取月(阿拉伯数字)
        System.out.println("月(数字):" + date.getMonthValue());
        //获取日
        System.out.println("日:" + date.getDayOfMonth());
        //是否是闰年
        System.out.println("是否是闰年:" + date.isLeapYear());
        //...其他方法,自行研究
    }
 
    // LocalDate 时间类(时分秒)
    @Test
    public void testLocalTime() {
        //获取当前时间
        LocalTime now = LocalTime.now();
        LocalTime date = LocalTime.of(13, 26, 39);
    }
 
    // LocalDateTime 日期时间类(年月日 时分秒)
    @Test
    public void testLocalDateTime() {
        //LocalDateTime: LocalDate + LocalTime,有年月日 时分秒
        LocalDateTime now = LocalDateTime.now();
    }
 
     // 修改时间
    @Test
    public void modifyTime() {
        //以LocalDateTime为例(LocalDate、LocalTime与此类似)
        LocalDateTime now = LocalDateTime.now();
        //修改年[修改时间(不是JDK8之前的setXXX(),而是使用withXXX())]
        System.out.println("修改年后:" + now.withYear(9102));
        //增加年(减使用 minusYear()方法)
        System.out.println("+2年后:" + now.plusYears(2));
        //增加日(减使用 minusDays()方法)
        System.out.println("47天后:" + now.plusDays(47));
    }
 
    // 时间比较
    @Test
    public void compareTime() {
        //以LocalDateTime为例(LocalDate、LocalTime与此类似)
        //时间1
        LocalDateTime now = LocalDateTime.now();
        //时间2
        LocalDateTime dateTime = LocalDateTime.of(2018, 7, 12, 13, 28, 51);
        //判断前面日期是否在后面日期后
        System.out.println("A时间是否晚于B时间:" + now.isAfter(dateTime));
        //判断前面日期是否在后面日期前
        System.out.println("A时间是否早于B时间:" + now.isBefore(dateTime));
        //判断两个日期时间是否相等
        System.out.println("两个时间是否相等:" + now.isEqual(dateTime));
        //...其他方法,自行研究
    }
}

2、Java 8 日期时间格式化与解析

/**
 * TODO 日期时间格式化 + 解析 + 多线程执行(安全)
 *
 * @author liuzebiao
 * @Date 2020-1-13 14:12
 */
public class DateDemo03 {
 
    @Test
    public void dateFormat(){
        LocalDateTime now = LocalDateTime.now();
 
        //格式化
        //使用JDK自带的时间格式:ISO_DATE_TIME(默认提供了很多格式,请自行查看)
        DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
        String format = now.format(dtf);
        System.out.println("format="+format);
 
        //指定时间格式(ofPattern()方法)
        DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
        String format1 = now.format(dtf1);
        System.out.println(format1);
 
        //解析(parse()方法)
        LocalDateTime parse = LocalDateTime.parse(format1, dtf1);
        System.out.println("parse="+parse);
 
        /**
         * 多线程执行(验证线程安全性)
         * 1.返回结果正确   2.不抛异常
         */
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                LocalDateTime parse1 = LocalDateTime.parse(format1, dtf1);
                System.out.println("parse="+parse1);
            }).start();
        }
    }
}

3、Java 8 Instant 类

Instant 类,就是时间戳,内部保存了从1970年1月1日 00:00:00以来的秒和纳秒。

public class DateDemo04 {
 
    @Test
    public void Instant(){
        //Instant 
        // 内部保存了秒和纳秒,一般不是给用户使用的,而是方便程序做一些统计的(比如:统计方法耗时)
        Instant now = Instant.now();
        System.out.println("当前时间戳:"+now);//2020-01-13T06:48:46.267Z
        //Instant类 并没有修改年月日等操作.因为 Instant 本来就不是给用户使用的
        //Instant类:对 秒、纳秒等操作方便
        Instant plus = now.plusSeconds(20);
        System.out.println("+20秒后:"+plus);
 
        Instant minus = now.minusSeconds(20);
        System.out.println("-20秒后:"+minus);
 
        //获取秒、毫秒、纳秒
        long second = now.getEpochSecond();
        System.out.println("秒:"+second);
        int nano = now.getNano();
        System.out.println("纳秒:"+nano);
        //...其他方法,自行研究
    }
}

4、Java 8 计算日期时间差类

Duration/Period 类:主要用来计算日期时间差

Duration:用于计算 2 个时间(LocalTime,时分秒)的差值;
Period:用于计算 2 个日期(LocalDate,年月日)的差值;

public class DateDemo05 {
 
    /**
     * Duration类:计算时间的差值
     */
    @Test
    public void testTimeDiff(){
        //时间1
        LocalTime now = LocalTime.now();
        //时间2
        LocalTime dateTime = LocalTime.of(8, 15, 46);
        //计算两个时间的差值
        //计算规则:让第二个参数 减去 第一个参数(位置错误可能出现负数)
        Duration duration = Duration.between(dateTime,now);
        System.out.println("相差的天数:"+duration.toDays());
        System.out.println("相差的小时数:"+duration.toHours());
        System.out.println("相差的分钟数:"+duration.toMinutes());
        System.out.println("相差的秒数:"+duration.toSeconds());//JDK 9+ 出现(JDK8会报错误)
        System.out.println("相差的纳秒数:"+duration.toNanos());
        //...其他方法,自行研究
    }
 
    /**
     * Period类:计算日期的差值
     */
    @Test
    public void testDateDiff(){
        //日期1
        LocalDate now = LocalDate.now();
        //日期2
        LocalDate date = LocalDate.of(1999,5,29);
        //计算两个日期的差值
        //计算规则:让第二个参数 减去 第一个参数(位置错误可能出现负数)
        Period period = Period.between(date,now);
        System.out.println("相差的年:"+period.getYears());
        System.out.println("相差的月:"+period.getMonths());
        System.out.println("相差的日:"+period.getDays());
        //...其他方法,自行研究
    }
}

5、Java 8 设置日期时间的时区

Java8 中加入了对时区的支持。LocalDate、LocalTime、LocalDateTime 是不带时区的,带时区的日期时间类分别为:ZonedDate、ZonedTime、ZonedDateTime类。

其中每个时区都有对应着的 ID,ID的格式为"区域/城市",例如:Asia/Shanghai 等。
ZoneId类:该类中包含了所有的时区信息。

public class DateDemo07 {
 
    /**
     * 获取时区ID
     */
    @Test
    public void getZoneIds(){
        //1.获取所有的时区ID
        Set<String> zoneIds = ZoneId.getAvailableZoneIds();
        zoneIds.forEach(System.out::println);//返回600来个时区
    }
 
    /**
     * 不带时区 Vs 带时区的日期时间
     */
    @Test
    public void ZonedDemo(){
        //2.操作带时区的类
        //不带时间,获取计算机的当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("now:"+now);
 
        //中国使用的是东八区的时间,比标准时间早8个小时
        //操作带时间的类
        ZonedDateTime zdt = ZonedDateTime.now(Clock.systemUTC());//创建出来的时间是世界标准时间
        System.out.println("世界标准时间:"+zdt);
    }
 
    /**
     * 本地时间
     */
    @Test
    public void localTime(){
        //now():使用计算机的默认的时区,创建日期时间
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println("本地时间:"+now);//本地时间:2020-01-13T15:52:43.633+08:00[Asia/Shanghai]
    }
 
    /**
     * 使用指定的时区来创建时间
     */
    @Test
    public void ZoneTime(){
 
        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println("设置指定时间:"+now);//设置指定时间:2020-01-13T02:56:24.776-05:00[America/New_York]
    }
 
    /**
     * 修改时区
     */
    @Test
    public void modifyZone(){
        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
 
        //withZoneSameInstant():既更改时区,也更改时间
        ZonedDateTime modifyTime = now.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
        System.out.println("修改时区后的时间:"+modifyTime);
 
        //withZoneSameLocal():只更改时区,不更改时间
        ZonedDateTime modifyTime2 = now.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));
        System.out.println("修改时区后的时间:"+modifyTime2);
    }
    //...其他方法,自行研究
}

6、java8 日期时间调整器

有时我们可能需要获取,例如:"将日期调整到下一个月的第一天"等操作,此时我们可以通过时间调整器来进行操作。

TemporalAdjuster:时间调整器;
TemporalAdjusters:工具类。该类通过静态方法提供了大量的常用 TemporalAdjuster 的实现;

/**
 * TODO JDK8 时间调整器
 *
 * @author liuzebiao
 * @Date 2020-1-13 15:10
 */
public class DateDemo06 {
 
    /**
     * TemporalAdjuster类:自定义调整时间
     */
    @Test
    public void timeCorrector(){
        //将日期调整到"下一个月的第一天"操作
        LocalDateTime now = LocalDateTime.now();
        //参数:TemporalAdjuster adjuster。TemporalAdjuster是一个接口,里面只有 Temporal adjustInto(Temporal temporal); 这一个方法,支持接入 lambda 表达式
        //此处 Temporal 就是指时间(包括 LocalDate、LocalTime、LocalDateTime 都是继承自该类。继承关系:如下图所示)
        TemporalAdjuster adjuster = ( Temporal temporal)->{
            LocalDateTime dateTime = (LocalDateTime)temporal;
            return dateTime.plusMonths(1).withDayOfMonth(1);//下一个月第一天
        };
        LocalDateTime newDateTime = now.with(adjuster);
        System.out.println("下个月第一天:"+newDateTime);
    }
 
    /**
     * TemporalAdjusters工具类:使用JDK提供的时间调整器
     */
    @Test
    public void JDKTimeCorrector(){
        //JDK中自带了很多时间调整器,其他调整器请自行查看
        //使用 TemporalAdjusters 工具类
        //TemporalAdjusters.firstDayOfNextYear()--->根据内容可知:下一年第一天
        TemporalAdjuster temporalAdjuster = TemporalAdjusters.firstDayOfNextYear();
 
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime newDateTime = now.with(temporalAdjuster);
        System.out.println("下个月第一天:"+newDateTime);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值