Thread unsafe issues with SimpleDateFormat & new date handling

Table of contents

SimpleDateFormat problem

I. Introduction

2. Overview

3. Simulate thread safety issues

Fourth, the reason for thread insecurity

5. How to solve thread safety

6. The recommended writing method in the project

7. Final summary

Introducing the new date for 1.8

LocalDate uses

LocalDate time creation method

localDate set a custom date to create an object based on the specified date/time

date time addition and subtraction

Modify the year, month, day, etc. to the specified value, and return a new date (time) object

Get the year, month, day, week, hour, minute, and second of the date

Comparison and judgment before and after time and date

java8 clock: clock()

timestamp

Calculate time, date interval

date formatting

convert time object to date string

Convert time string form to date object

Convert time and date objects to formatted time and date objects

convert

localDate 转 date

date 转 localDate

localDate to timestamp

Timestamp to localDate


SimpleDateFormat problem

I. Introduction


Date conversion and formatting should be more commonly used in projects

A question: How to use the date conversion in the project? Was SimpleDateFormat used? Can you tell me about SimpleDateFormat thread safety issue and how to solve it?

Answer: Usually, a static SimpleDateFormat is defined globally, and then used directly in the business processing method (controller). As for thread safety... this... I have never encountered a thread safety problem.

Let me explain.

2. Overview


The SimpleDateFormat class is mainly responsible for date conversion and formatting operations. In a multi-threaded environment, using this class may easily cause incorrect data conversion and processing, because the SimpleDateFormat class is not thread-safe, but it is not in a single-threaded environment. questionable.

SimpleDateFormat also reminds you that it is not suitable for multi-threaded scenarios in the class comments:

Date formats are not synchronized.
It is recommended to create separate format instances for each thread.
If multiple threads access a format concurrently, it must be synchronized
externally.

日期格式不同步。
建议为每个线程创建单独的格式实例。 
如果多个线程同时访问一种格式,则必须在外部同步该格式。

Let's take a look at how the Alibaba java development specification describes SimpleDateFormat:

3. Simulate thread safety issues

No code, no truth, next we create a thread to simulate the SimpleDateFormat thread safety issue:

Create the MyThread.java class:

public class MyThread extends Thread{
  
    private SimpleDateFormat simpleDateFormat;
    /* 要转换的日期字符串 */
    private String dateString;

    public MyThread(SimpleDateFormat simpleDateFormat, String dateString){
        this.simpleDateFormat = simpleDateFormat;
        this.dateString = dateString;
    }

    @Override
    public void run() {
        try {
            Date date = simpleDateFormat.parse(dateString);
            String newDate = simpleDateFormat.format(date).toString();
            if(!newDate.equals(dateString)){
                System.out.println("ThreadName=" + this.getName()
                    + " 报错了,日期字符串:" + dateString
                    + " 转换成的日期为:" + newDate);
            }
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

 Create the execution class Test.java class:

public class Test {

    // 一般我们使用SimpleDateFormat的时候会把它定义为一个静态变量,避免频繁创建它的对象实例
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd");

    public static void main(String[] args) {

        String[] dateStringArray = new String[] { "2020-09-10", "2020-09-11", "2020-09-12", "2020-09-13", "2020-09-14"};

        MyThread[] myThreads = new MyThread[5];

        // 创建线程
        for (int i = 0; i < 5; i++) {
            myThreads[i] = new MyThread(simpleDateFormat, dateStringArray[i]);
        }

        // 启动线程
        for (int i = 0; i < 5; i++) {
            myThreads[i].start();
        }
    }
}

The execution screenshot is as follows:

From the results printed on the console, using the singleton SimpleDateFormat class to process date conversion in a multi-threaded environment is prone to conversion exceptions (java.lang.NumberFormatException: multiple points) and conversion errors.

Fourth, the reason for thread insecurity


At this time, you need to look at the source code, format() format conversion method:

// 成员变量 Calendar
protected Calendar calendar;

private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
    // Convert input date to time field list
    calendar.setTime(date);

    boolean useDateFormatSymbols = useDateFormatSymbols();

    for (int i = 0; i < compiledPattern.length; ) {
        int tag = compiledPattern[i] >>> 8;
        int count = compiledPattern[i++] & 0xff;
        if (count == 255) {
            count = compiledPattern[i++] << 16;
            count |= compiledPattern[i++];
        }

        switch (tag) {
        case TAG_QUOTE_ASCII_CHAR:
            toAppendTo.append((char)count);
            break;

        case TAG_QUOTE_CHARS:
            toAppendTo.append(compiledPattern, i, count);
            i += count;
            break;

        default:
            subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
            break;
        }
    }
    return toAppendTo;
}

We focus on the calendar. During the execution of this format method, the member variable calendar will be operated to save the time calendar.setTime(date).

But because when declaring SimpleDateFormat, it uses static definition, then this SimpleDateFormat is a shared variable, and the calendar in SimpleDateFormat can be accessed by multiple threads, so the problem arises, for example:

Assume that thread A has just executed the calendar.setTime(date) statement and set the time to 2020-09-01, but the thread has not yet finished executing, and thread B executes the calendar.setTime(date) statement again and sets the time to 2020- 09-02, phantom reading occurs at this time. When thread A continues to execute, the time obtained from calendar.getTime is the time obtained by thread B after modification.

Apart from the format() method, the parse method of SimpleDateFormat has the same problem.

So far, we have discovered the disadvantages of SimpleDateFormat, so in order to solve this problem is not to use SimpleDateFormat as a shared variable.

5. How to solve thread safety


1. Create a new SimpleDateFormat every time you use it
to create a global tool class DateUtils.java

public class DateUtils {
    public static Date parse(String formatPattern, String dateString) throws ParseException {
        return new SimpleDateFormat(formatPattern).parse(dateString);
    }

    public static String  format(String formatPattern, Date date){
        return new SimpleDateFormat(formatPattern).format(date);
    }
}

The principle of this error handling error is to create multiple instances of the SimpleDateFormat class, and create a new instance where it needs to be used, so there is no thread safety problem, but it also increases the burden of creating objects, which will be created and destroyed frequently object, less efficient.

2. Synchronized lock

synchronized will not introduce

Change DateUtils.java

public class DateUtils {

    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static Date parse(String formatPattern, String dateString) throws ParseException {
        synchronized (simpleDateFormat){
            return simpleDateFormat.parse(dateString);
        }
    }

    public static String format(String formatPattern, Date date) {
        synchronized (simpleDateFormat){
            return simpleDateFormat.format(date);
        }
    }
}

Simple and rude, synchronized can also solve the problem of thread safety. The disadvantage is that when the amount of concurrency is large, it will affect the performance, because the multi-thread after using synchronized lock is equivalent to serial, thread blocking, and execution efficiency. Low.

3. ThreadLocal (the best MVP)
ThreadLocal is a special variable in java. ThreadLocal provides a thread-local instance. The difference between it and ordinary variables is that each thread that uses the thread variable will initialize a completely independent instance copy. .

Continue to transform DateUtils.java

public class DateUtils {

    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };

    public static Date parse(String formatPattern, String dateString) throws ParseException {
        return threadLocal.get().parse(dateString);
    }

    public static String format(String formatPattern, Date date) {
        return threadLocal.get().format(date);
    }
}

ThreadLocal can ensure that each thread can get a single SimpleDateFormat object, so there will be no competition problem.

If SimpleDateFormat is still used in the project, this way of writing is recommended, but is this the end?

Obviously not…

6. The recommended writing method in the project


The Alibaba java development manual mentioned above gives instructions: If it is an application of JDK8, you can use Instant instead of Date, LocalDateTime instead of Calendar, and DateTimeFormatter instead of SimpleDateFormat. The official explanation: simple beautiful strong immutable thread-safe.

Date conversion, SimpleDateFormat is easy to use, but now we have a better choice, Java 8 introduces a new date and time API, and introduces a thread-safe date class, let's take a look.

Instant: Instantaneous instance.
LocalDate: local date, excluding specific time. For example: 2014-01-14 can be used to record birthdays, anniversaries, joining days, etc.
LocalTime: local time, excluding date.
LocalDateTime: combines date and time, but does not contain time difference and time zone information.
ZonedDateTime: The most complete datetime, including time zone and time difference relative to UTC or Greenwich.
The new API also introduces the ZoneOffSet and ZoneId classes, making it easier to resolve time zone issues.

The DateTimeFormatter class that parses and formats time has also been completely redesigned.

For example, we use LocalDate instead of Date and DateTimeFormatter instead of SimpleDateFormat as follows:

public class DateUtils {

    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static LocalDate parse(String dateString){
        return LocalDate.parse(dateString, DATE_TIME_FORMATTER);
    }

    public static String format(LocalDate target) {
        return target.format(DATE_TIME_FORMATTER);
    }
}

7. Final summary

SimpleDateFormart inherits from DateFormart, and there is a Calendar object reference inside the DataFormat class. SimpleDateFormat converts dates based on this Calendar object, such as parse(String), format(date) and similar methods. When using Calendar, it is directly It is used, and the value of Calendar is changed. In this case, there will be thread safety problems under multi-threading. If SimpleDateFormart is static, then this SimpleDateFormart will be shared between multiple threads, and this Calendar reference will also be shared. Then there will be data assignment coverage, that is, thread safety issues. (The date conversion used in the project now uses LocalDate or LocalDateTime in java 8. The essence is that these classes are immutable classes, and immutability guarantees thread safety to a certain extent).

1. Solution:

ThreadLocal can be used to modify SimpleDateFormat under multi-threading. ThreadLocal can ensure that each thread can get a single SimpleDateFormat object, so there will be no competition problem.

2. Recommended method:

The new date class API introduced in java 8, these classes are immutable and thread safe.

Introducing the new date for 1.8

LocalDate uses

LocalDate time creation method

/**
     * localDate时间创建方式
     * 获取当前时间
     *
     * @author hq
     * Created in 2020/10/16 9:40
     */
    @Test
    public void localDateCreate() {
        LocalDate yyyyMMdd = LocalDate.now();
        LocalTime HHmmssSSS = LocalTime.now();
        LocalDateTime yyyyMMddHHmmssSSS = LocalDateTime.now();
        System.out.println("年月日: " + yyyyMMdd);
        System.out.println("时分秒毫秒: " + HHmmssSSS);
        System.out.println("年月日时分秒毫秒: " + yyyyMMddHHmmssSSS);
        //输出:
        //        年月日: 2020-10-16
        //        时分秒毫秒: 09:55:49.448
        //        年月日时分秒毫秒: 2020-10-16T09:55:49.448
    }

localDate set a custom date to create an object based on the specified date/time

/**
     * localDate 设值自定义日期
     * 根据指定日期/时间创建对象
     *
     * @author hq
     * Created in 2020/10/16 9:40
     */
    @Test
    public void setDate() {
        LocalDate yyyyMMdd = LocalDate.of(2020, 10, 15);
        LocalTime HHmmssSSS = LocalTime.of(10, 10, 10);
        LocalDateTime yyyyMMddHHmmssSSS = LocalDateTime.of(2020, 10, 15, 10, 10);
        System.out.println("自定义的年月日: " + yyyyMMdd);
        System.out.println("自定义时分秒毫秒: " + HHmmssSSS);
        System.out.println("自定义年月日时分秒毫秒: " + yyyyMMddHHmmssSSS);

        //输出:
        //        自定义的年月日: 2020-10-15
        //        自定义时分秒毫秒: 10:10:10
        //        自定义年月日时分秒毫秒: 2020-10-15T10:10
    }

date time addition and subtraction

/**
     * 日期时间的加减
     *
     * @author hq
     * Created in 2020/10/16 9:58
     */
    @Test
    public void lessOrAddDate() {
        LocalDate yyyyMMdd = LocalDate.now();
        LocalDate addOneDay = yyyyMMdd.plusDays(1L); //添加一日
        LocalDate addOneMonths = yyyyMMdd.plusMonths(1L);//添加一月
        LocalDate addOneYears = yyyyMMdd.plusYears(1L);//添加一年
        LocalDate addOneWeeks = yyyyMMdd.plusWeeks(1L);//添加一周

        LocalDate delOneDay = yyyyMMdd.minusDays(1L); //减去一日
        LocalDate delOneMonths = yyyyMMdd.minusMonths(1L);//减去一月
        LocalDate delOneYears = yyyyMMdd.minusYears(1L);//减去一年
        LocalDate delOneWeeks = yyyyMMdd.minusWeeks(1L);//减去一周
        System.out.println("LocalDate yyyyMMdd 当前时间: " + yyyyMMdd);
        System.out.println("*********LocalDate yyyyMMdd 添加 操作*********");
        System.out.println("LocalDate yyyyMMdd 添加一日: " + addOneDay);
        System.out.println("LocalDate yyyyMMdd 添加一月: " + addOneMonths);
        System.out.println("LocalDate yyyyMMdd 添加一年: " + addOneYears);
        System.out.println("LocalDate yyyyMMdd 添加一周: " + addOneWeeks);
        System.out.println("*********LocalDate yyyyMMdd 减 操作*********");
        System.out.println("LocalDate yyyyMMdd 减去一日: " + delOneDay);
        System.out.println("LocalDate yyyyMMdd 减去一月: " + delOneMonths);
        System.out.println("LocalDate yyyyMMdd 减去一年: " + delOneYears);
        System.out.println("LocalDate yyyyMMdd 减去一周: " + delOneWeeks);
        System.out.println("####################################################################");
        LocalTime HHmmssSSS = LocalTime.now();
        LocalTime addOneHours = HHmmssSSS.plusHours(1L); //添加1小时
        LocalTime addOneMinutes = HHmmssSSS.plusMinutes(1L);//添加1分钟
        LocalTime addOneSeconds = HHmmssSSS.plusSeconds(1L);//添加1秒
        LocalTime addOneNanos = HHmmssSSS.plusNanos(1L);//添加1纳秒

        LocalTime delOneHours = HHmmssSSS.minusHours(1L); //减去1小时
        LocalTime delOneMinutes = HHmmssSSS.minusMinutes(1L);//减去1分钟
        LocalTime delOneSeconds = HHmmssSSS.minusSeconds(1L);//减去1秒
        LocalTime delOneNanos = HHmmssSSS.minusNanos(1L);//减去1纳秒

        System.out.println("LocalTime HHmmssSSS 当前时间: " + HHmmssSSS);
        System.out.println("*********LocalTime HHmmssSSS 添加 操作*********");
        System.out.println("LocalTime HHmmssSSS 添加1小时: " + addOneHours);
        System.out.println("LocalTime HHmmssSSS 添加1分钟: " + addOneMinutes);
        System.out.println("LocalTime HHmmssSSS 添加1秒: " + addOneSeconds);
        System.out.println("LocalTime HHmmssSSS 添加1纳秒: " + addOneNanos);
        System.out.println("*********LocalTime HHmmssSSS 减 操作*********");
        System.out.println("LocalTime HHmmssSSS 减去1小时: " + delOneHours);
        System.out.println("LocalTime HHmmssSSS 减去1分钟: " + delOneMinutes);
        System.out.println("LocalTime HHmmssSSS 减去1秒: " + delOneSeconds);
        System.out.println("LocalTime HHmmssSSS 减去1纳秒: " + delOneNanos);
        System.out.println("####################################################################");
        LocalDateTime yyyyMMddHHmmssSSS = LocalDateTime.now();
        LocalDateTime localDateTimeaddOneDay = yyyyMMddHHmmssSSS.plusDays(1L); //添加一日
        LocalDateTime localDateTimeaddOneMonths = yyyyMMddHHmmssSSS.plusMonths(1L);//添加一月
        LocalDateTime localDateTimeaddOneYears = yyyyMMddHHmmssSSS.plusYears(1L);//添加一年
        LocalDateTime localDateTimeaddOneWeeks = yyyyMMddHHmmssSSS.plusWeeks(1L);//添加一周
        LocalDateTime localDateTimeaddOneHours = yyyyMMddHHmmssSSS.plusHours(1L); //添加1小时
        LocalDateTime localDateTimeaddOneMinutes = yyyyMMddHHmmssSSS.plusMinutes(1L);//添加1分钟
        LocalDateTime localDateTimeaddOneSeconds = yyyyMMddHHmmssSSS.plusSeconds(1L);//添加1秒
        LocalDateTime localDateTimeaddOneNanos = yyyyMMddHHmmssSSS.plusNanos(1L);//添加1纳秒
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 当前时间: " + yyyyMMddHHmmssSSS);
        System.out.println("*********LocalDateTime yyyyMMddHHmmssSSS 添加 操作*********");
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加一日: " + localDateTimeaddOneDay);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加一月: " + localDateTimeaddOneMonths);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加一年: " + localDateTimeaddOneYears);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加一周: " + localDateTimeaddOneWeeks);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加1小时: " + localDateTimeaddOneHours);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加1分钟: " + localDateTimeaddOneMinutes);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加1秒: " + localDateTimeaddOneSeconds);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 添加1纳秒: " + localDateTimeaddOneNanos);
        LocalDateTime localDateTimedelOneDay = yyyyMMddHHmmssSSS.minusDays(1L); //减去一日
        LocalDateTime localDateTimedelOneMonths = yyyyMMddHHmmssSSS.minusMonths(1L);//减去一月
        LocalDateTime localDateTimedelOneYears = yyyyMMddHHmmssSSS.minusYears(1L);//减去一年
        LocalDateTime localDateTimedelOneWeeks = yyyyMMddHHmmssSSS.minusWeeks(1L);//减去一周
        LocalDateTime localDateTimedelOneHours = yyyyMMddHHmmssSSS.minusHours(1L); //减去1小时
        LocalDateTime localDateTimedelOneMinutes = yyyyMMddHHmmssSSS.minusMinutes(1L);//减去1分钟
        LocalDateTime localDateTimedelOneSeconds = yyyyMMddHHmmssSSS.minusSeconds(1L);//减去1秒
        LocalDateTime localDateTimedelOneNanos = yyyyMMddHHmmssSSS.minusNanos(1L);//减去1纳秒
        System.out.println("*********LocalDateTime yyyyMMddHHmmssSSS 减 操作*********");
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去一日: " + localDateTimedelOneDay);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去一月: " + localDateTimedelOneMonths);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去一年: " + localDateTimedelOneYears);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去一周: " + localDateTimedelOneWeeks);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去1小时: " + localDateTimedelOneHours);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去1分钟: " + localDateTimedelOneMinutes);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去1秒: " + localDateTimedelOneSeconds);
        System.out.println("LocalDateTime yyyyMMddHHmmssSSS 减去1纳秒: " + localDateTimedelOneNanos);

        /*
        输出:
            LocalDate yyyyMMdd 当前时间: 2020-10-16

            *********LocalDate yyyyMMdd 添加 操作*********
            LocalDate yyyyMMdd 添加一日: 2020-10-17
            LocalDate yyyyMMdd 添加一月: 2020-11-16
            LocalDate yyyyMMdd 添加一年: 2021-10-16
            LocalDate yyyyMMdd 添加一周: 2020-10-23

            *********LocalDate yyyyMMdd 减 操作*********
            LocalDate yyyyMMdd 减去一日: 2020-10-15
            LocalDate yyyyMMdd 减去一月: 2020-09-16
            LocalDate yyyyMMdd 减去一年: 2019-10-16
            LocalDate yyyyMMdd 减去一周: 2020-10-09

            ####################################################################
            LocalTime HHmmssSSS 当前时间: 10:20:22.164

            *********LocalTime HHmmssSSS 添加 操作*********
            LocalTime HHmmssSSS 添加1小时: 11:20:22.164
            LocalTime HHmmssSSS 添加1分钟: 10:21:22.164
            LocalTime HHmmssSSS 添加1秒: 10:20:23.164
            LocalTime HHmmssSSS 添加1纳秒: 10:20:22.164000001

            *********LocalTime HHmmssSSS 减 操作*********
            LocalTime HHmmssSSS 减去1小时: 09:20:22.164
            LocalTime HHmmssSSS 减去1分钟: 10:19:22.164
            LocalTime HHmmssSSS 减去1秒: 10:20:21.164
            LocalTime HHmmssSSS 减去1纳秒: 10:20:22.163999999

            ####################################################################
            LocalDateTime yyyyMMddHHmmssSSS 当前时间: 2020-10-16T10:20:22.165

            *********LocalDateTime yyyyMMddHHmmssSSS 添加 操作*********
            LocalDateTime yyyyMMddHHmmssSSS 添加一日: 2020-10-17T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 添加一月: 2020-11-16T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 添加一年: 2021-10-16T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 添加一周: 2020-10-23T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 添加1小时: 2020-10-16T11:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 添加1分钟: 2020-10-16T10:21:22.165
            LocalDateTime yyyyMMddHHmmssSSS 添加1秒: 2020-10-16T10:20:23.165
            LocalDateTime yyyyMMddHHmmssSSS 添加1纳秒: 2020-10-16T10:20:22.165000001

            *********LocalDateTime yyyyMMddHHmmssSSS 减 操作*********
            LocalDateTime yyyyMMddHHmmssSSS 减去一日: 2020-10-15T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 减去一月: 2020-09-16T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 减去一年: 2019-10-16T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 减去一周: 2020-10-09T10:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 减去1小时: 2020-10-16T09:20:22.165
            LocalDateTime yyyyMMddHHmmssSSS 减去1分钟: 2020-10-16T10:19:22.165
            LocalDateTime yyyyMMddHHmmssSSS 减去1秒: 2020-10-16T10:20:21.165
            LocalDateTime yyyyMMddHHmmssSSS 减去1纳秒: 2020-10-16T10:20:22.164999999
         */
    }

Modify the year, month, day, etc. to the specified value, and return a new date (time) object

/**
     * 将年、月、日等修改为指定的值,并返回新的日期(时间)对象
     * 析: 其效果与时间日期相加减差不多,如今天是2018-01-13,要想变为2018-01-20有两种方式
     * a. localDate.plusDays(20L) -> 相加指定的天数
     * b. localDate.withDayOfYear(20) -> 直接指定到哪一天
     * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
     *
     * @author hq
     * Created in 2020/10/16 10:34
     */
    @Test
    public void directDesignationTime() {
        LocalDate yyyyMMdd = LocalDate.now();
        System.out.println("LocalDate yyyyMMdd 当前时间: " + yyyyMMdd);

        LocalDate addDay = yyyyMMdd.plusDays(4);
        System.out.println("LocalDate yyyyMMdd 添加4天后的日期: " + addDay);

        LocalDate directDesignationDate = yyyyMMdd.withDayOfMonth(20);
        System.out.println("LocalDate yyyyMMdd 直接指定到当月第几号: " + directDesignationDate);

        LocalDate directDesignationYearDate = yyyyMMdd.withDayOfYear(20);
        System.out.println("LocalDate yyyyMMdd 直接指定到当年第几天: " + directDesignationYearDate);

        LocalDate directDesignationYear = yyyyMMdd.withYear(2000);
        System.out.println("LocalDate yyyyMMdd 当前时间直接指定年份: " + directDesignationYear);

        LocalDate directDesignationMonth = yyyyMMdd.withMonth(6);
        System.out.println("LocalDate yyyyMMdd 当前时间直接指定月份: " + directDesignationMonth);
    }

Get the year, month, day, week, hour, minute, and second of the date

 /**
     * 获取日期的年月日周时分秒
     *
     * @author hq
     * Created in 2020/10/16 10:48
     */
    @Test
    public void getDateInfo() {
        LocalDateTime yyyyMMddHHmmssSSS = LocalDateTime.now();
        //本年当中第多少天
        int dayOfYear = yyyyMMddHHmmssSSS.getDayOfYear();
        //本月当中第多少天
        int dayOfMonth = yyyyMMddHHmmssSSS.getDayOfMonth();
        DayOfWeek dayOfWeek = yyyyMMddHHmmssSSS.getDayOfWeek();
        //本周中星期几
        int value = dayOfWeek.getValue();
        System.out.println("今天是" + yyyyMMddHHmmssSSS + "\n"
                + "本年当中第" + dayOfYear + "天" + "\n"
                + "本月当中第" + dayOfMonth + "天" + "\n"
                + "本周中星期" + value + "-即" + dayOfWeek + "\n");

        //年
        int year = yyyyMMddHHmmssSSS.getYear();
        //月
        Month month = yyyyMMddHHmmssSSS.getMonth();
        //直接获取
        int monthValue = yyyyMMddHHmmssSSS.getMonthValue();
        //日
        int dayOfMonth1 = yyyyMMddHHmmssSSS.getDayOfMonth();
        //时
        int hour = yyyyMMddHHmmssSSS.getHour();
        //分
        int minute = yyyyMMddHHmmssSSS.getMinute();
        //秒
        int second = yyyyMMddHHmmssSSS.getSecond();
        //纳秒
        int nano = yyyyMMddHHmmssSSS.getNano();

        System.out.println("今天是" + yyyyMMddHHmmssSSS + "\n"
                + "年 : " + year + "\n"
                + "月 : " + monthValue + "-即 " + month + "\n"
                + "日 : " + dayOfMonth1 + "\n"
                + "时 : " + hour + "\n"
                + "分 : " + minute + "\n"
                + "秒 : " + second + "\n"
                + "纳秒 : " + nano + "\n"
        );
    }

Comparison and judgment before and after time and date

/**
     * 时间日期前后的比较与判断
     *
     * @author hq
     * Created in 2020/10/16 11:08
     */
    @Test
    public void isBefore() {
        LocalDate now = LocalDate.now();
        LocalDate of = LocalDate.of(2020, 10, 15);
        //判断of 是否在 now 时间之前
        boolean before = of.isBefore(now);
        System.out.println("判断of 是否在 now 时间之前 " + before);
        //判断of 是否在 now 时间之后
        boolean after = of.isAfter(now);
        System.out.println("判断of 是否在 now 时间之后 " + after);
        //判断两个时间是否相等
        boolean equal = of.isEqual(now);
        System.out.println("判断两个时间是否相等 " + equal);
        //判断是否为闰年
        boolean leapYear = now.isLeapYear();
        System.out.println("判断是否为闰年 " + leapYear);

        /*
            判断of 是否在 now 时间之前true
            判断of 是否在 now 时间之后false
            判断两个时间是否相等false
            判断是否为闰年true
         */
    }

java8 clock: clock()

/**
     * java8时钟 : clock()
     *
     * @author hq
     * Created in 2020/10/16 11:16
     */
    @Test
    public void clock() {
        //返回当前时间,根据系统时间和UTC
        Clock clock = Clock.systemUTC();
        System.out.println(clock);
        // 运行结果: SystemClock[Z]
    }

timestamp

/**
     * 时间戳
     * 事实上Instant就是java8以前的Date,
     * 可以使用以下两个类中的方法在这两个类型之间进行转换,
     * 比如Date.from(Instant)就是用来把Instant转换成java.util.date的,
     * 而new Date().toInstant()就是将Date转换成Instant的
     *
     * @author hq
     * Created in 2020/10/16 11:17
     */
    @Test
    public void instant() {
        Instant instant = Instant.now();
        System.out.println(instant);

        Date date = Date.from(instant);

        Instant instant2 = date.toInstant();
        System.out.println(date);
        System.out.println(instant2);
    }

Calculate time, date interval

/**
     * 计算时间、日期间隔
     * Duration:用于计算两个“时间”间隔
     * Period:用于计算两个“日期”间隔
     *
     * @author hq
     * Created in 2020/10/16 14:28
     */
    @Test
    public void durationOrPeriod() {
        LocalDateTime now = LocalDateTime.now();
        System.out.println("duration当前时间:" + now);
        LocalDateTime of = LocalDateTime.of(2020, 10, 15, 10, 24);
        System.out.println("duration自定义时间:" + of);
        //Duration:用于计算两个“时间”间隔
        Duration duration = Duration.between(now, of);
        System.out.println(now + " 与 " + of + " 间隔  " + "\n"
                + " 天 :" + duration.toDays() + "\n"
                + " 时 :" + duration.toHours() + "\n"
                + " 分 :" + duration.toMinutes() + "\n"
                + " 毫秒 :" + duration.toMillis() + "\n"
                + " 纳秒 :" + duration.toNanos() + "\n"
        );

        LocalDate nowDate = LocalDate.now();
        System.out.println("period当前时间:" + now);
        LocalDate OfDate = LocalDate.of(2020, 10, 15);
        System.out.println("period自定义时间:" + of);
        //Period:用于计算两个“日期”间隔
        Period period = Period.between(nowDate, OfDate);
        System.out.println("Period相差年数 : " + period.getYears());
        System.out.println("Period相差月数 : " + period.getMonths());
        System.out.println("Period相差日数 : " + period.getDays());
        //还可以这样获取相差的年月日
        System.out.println("-------------------------------");
        long years = period.get(ChronoUnit.YEARS);
        long months = period.get(ChronoUnit.MONTHS);
        long days = period.get(ChronoUnit.DAYS);
        System.out.println("Period相差的年月日分别为 : " + years + "," + months + "," + days);
        //注意,当获取两个日期的间隔时,并不是单纯的年月日对应的数字相加减,而是会先算出具体差多少天,在折算成相差几年几月几日的

        /*
        输出:
            duration当前时间:2020-10-16T14:41:40.235
            duration自定义时间:2020-10-15T10:24
            2020-10-16T14:41:40.235 与 2020-10-15T10:24 间隔
             天 :-1
             时 :-28
             分 :-1697
             毫秒 :-101860235
             纳秒 :-101860235000000

            period当前时间:2020-10-16T14:41:40.235
            period自定义时间:2020-10-15T10:24
            Period相差年数 : 0
            Period相差月数 : 0
            Period相差日数 : -1
            -------------------------------
            Period相差的年月日分别为 : 0,0,-1
         */
    }

date formatting

convert time object to date string

 /**
     * 将时间对象转化为日期字符串
     * 时间日期的格式化(格式化后返回的类型是String) 自定格式 使用 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");
     * 注:自定义转化的格式一定要与日期类型对应
     * LocalDate只能设置仅含年月日的格式
     * LocalTime只能设置仅含时分秒的格式
     * LocalDateTime可以设置含年月日时分秒的格式
     *
     * @author hq
     * Created in 2020/10/16 14:42
     */
    @Test
    public void formatter1() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");
        String format = dtf.format(now);
        System.out.println(format);
        //输出: 2020-10-16 14:41:01:086
    }

Convert time string form to date object

/**
     * 将时间字符串形式转化为日期对象
     * <p>
     * 注:格式的写法必须与字符串的形式一样
     * 2018-01-13 21:27:30 对应 yyyy-MM-dd HH:mm:ss
     * 20180113213328 对应 yyyyMMddHHmmss
     * 否则会报运行时异常!
     * <p>
     * 但要记住:得到的最终结果都是类似2018-01-13T21:27:30的格式
     * 因为在输出LocalDateTime对象时,会调用其重写的toString方法。
     *
     * @author hq
     * email [email protected]
     * Created in 2020/10/16 14:44
     */
    @Test
    public void formatter2() {
        String dateStr = "2020-11-12";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate formatterDate = LocalDate.parse(dateStr, dtf);
        System.out.println(formatterDate);
        //输出 2020-11-12
    }

Convert time and date objects to formatted time and date objects

/**
     * 将时间日期对象转为格式化后的时间日期对象
     *
     * @author hq
     * Created in 2020/10/16 14:49
     */
    @Test
    public void formatter3() {
        //新的格式化API中,格式化后的结果都默认是String,有时我们也需要返回经过格式化的同类型对象
        LocalDateTime ldt1 = LocalDateTime.now();
        DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String temp = dtf1.format(ldt1);
        LocalDateTime formatedDateTime = LocalDateTime.parse(temp, dtf1);
        System.out.println(formatedDateTime);
    }

convert

localDate 转 date

/**
     * localDate 转 date
     * localDateTime 转 date
     *
     * @author hq
     * email [email protected]
     * Created in 2020/10/16 15:05
     */
    @Test
    public void localDateToDate() {
        LocalDate now = LocalDate.now();
        Date from = Date.from(now.atStartOfDay(ZoneOffset.systemDefault()).toInstant());
        LocalDateTime localDateTime = LocalDateTime.now();
        Date date = Date.from(localDateTime.atZone(ZoneOffset.ofHours(8)).toInstant());
        System.out.println(from);
        System.out.println(date);
    }

date 转 localDate

/**
     * date 转 localDate
     * date 转 localDateTime
     *
     * @author hq
     * Created in 2020/10/16 15:05
     */
    @Test
    public void dateToLocalDate() {
        Date date = new Date();
        LocalDate localDate = date.toInstant().atZone(ZoneOffset.systemDefault()).toLocalDate();
        System.out.println(localDate);
        LocalDateTime localDateTime = date.toInstant().atZone(ZoneOffset.systemDefault()).toLocalDateTime();
        System.out.println(localDateTime);
    }

localDate to timestamp

/**
     * LocalDate 转 时间戳
     * LocalDateTime 转 时间戳
     * Created in 2020/10/16 15:28
     */
    @Test
    public void localDateToInstant() {
        LocalDate localDate = LocalDate.now();
        long instant = localDate.atStartOfDay(ZoneOffset.systemDefault()).toInstant().toEpochMilli();
        System.out.println(instant);
        LocalDateTime now = LocalDateTime.now();
        long instant1 = now.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
        System.out.println(instant1);
    }

Timestamp to localDate

/**
     * 时间戳 转 LocalDate
     * 时间戳 转 LocalDateTime
     * Created in 2020/10/16 15:28
     */
    @Test
    public void instantToLocalDate() {
        long time = new Date().getTime();
        LocalDateTime localDateTime = Instant.ofEpochMilli(time).atZone(ZoneOffset.systemDefault()).toLocalDateTime();
        System.out.println(localDateTime);
        LocalDate localDate = Instant.ofEpochMilli(time).atZone(ZoneOffset.systemDefault()).toLocalDate();
        System.out.println(localDate);

    }

Guess you like

Origin blog.csdn.net/lan861698789/article/details/131179201