Wanzi blog teaches you to understand the date and time related usage of java source code

Technical work should be rewarded
 follow+Click three times (like, comment, collect) and watch again to form a good habit

hutool date time series

1dateutil (time tool class) - current time and current timestamp

2dateutil (time tool class) - common time types Date, DateTime, Calendar and TemporalAccessor (local DateTime) conversion

3dateutil (time tool class) - get various contents of the date

4dateutil (time tool class) - format time

5dateutil (time tool class) - resolves the formatted time

6dateutil (time tool class) - time offset acquisition

7dateutil (time tool class) - date calculation

8chinesedate (lunar date tool)

9localdatetimeutil (encapsulated by {@ link LocalDateTime} tool class in jdk8 +)

10TemporalAccessorUtil{@link TemporalAccessor} tool class encapsulation

introduce

This article mainly introduces which date and time classes are provided in the java source code

Two sets of API s for date and time

java provides two sets of API s for processing date and time

1. The old API is placed in Java Util: Date and Calendar are commonly used

2. The new API is newly introduced in java 8 and placed in Java Time: LocalDateTime, ZonedDateTime, DateTimeFormatter, Instant, etc

There are historical reasons why there are two sets of date and time APIs. The old API was provided by jdk at the beginning. With the upgrade of the version, it is gradually found that the original API does not meet the needs, exposing some problems. Therefore, the new API is reintroduced in java 8.

These two sets of API s should be understood. Why?

Because java 8 was released in 2014, many previous systems still use the old API, so we should understand both APIs and master the technology of mutual transformation between the two APIs.

Date

Supported version and above

JDK1.0

introduce

Date class description

The Date class is responsible for the representation of time. In computers, the representation of time is a big concept. The existing systems basically use the number of milliseconds from 1970.1.1 00:00:00 to the current time. This time is called epoch (timestamp)

package java.util;

public class Date
    implements java.io.Serializable, Cloneable, Comparable<Date>
{
 ...
     
      private transient long fastTime;
     ....
}

java.util.Date is a class provided by java to represent date and time. There is a long variable fastTime in the class, which is used to store the timestamp expressed in milliseconds.

Common usage of date

import java.util.Date;
-----------------------------------------
		//Get current time
		Date date = new Date();
		System.out.println("Get current time:"+date);
		//Get timestamp
		System.out.println("Get timestamp:"+date.getTime());

		// Whether the date time is greater than or equal to afterDate is also false
		Date afterDate = new Date(date.getTime()-3600*24*1000);
		System.out.println("after:"+date.after(afterDate));
		System.out.println("after:"+date.after(date));

		// Whether the date time is less than or equal to afterDate is also false
		Date beforeDate = new Date(date.getTime()+3600*24*1000);
		System.out.println("before:"+date.before(beforeDate));
		System.out.println("before:"+date.before(date));

		//Comparison of two dates
		System.out.println("compareTo:"+date.compareTo(date));
		System.out.println("compareTo:"+date.compareTo(afterDate));
		System.out.println("compareTo:"+date.compareTo(beforeDate));

		//Convert to string
		System.out.println("Convert to string:"+date.toString());
		//Convert to GMT time zone togmtstring() is obsolete in java8
		System.out.println("Turn into GMT time zone:"+date.toGMTString());
		//Convert to local time zone toLocaleString() java8 is obsolete
		System.out.println("Convert to local time zone:"+date.toLocaleString());

Custom time format - SimpleDateFormat

The toString method of date is converted to a string, which is not the time format we want. If you want to customize the time format, you need to use SimpleDateFormat

		//Get current time
		Date date = new Date();
		System.out.println("Get current time:"+date);
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println(simpleDateFormat.format(date));
		SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy year MM month dd day HH Time mm branch ss second");
		System.out.println(simpleDateFormat1.format(date));

SimpleDateFormat can also easily convert a string to Date

//Get current time
		String str = "2021-07-13 23:48:23";
		try {
			Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
			System.out.println(date);
		} catch (ParseException e) {
			e.printStackTrace();
		}

Description of date and time formatting parameters

yyyy: year
MM: month
dd: day
hh: 1~12 Hour system(1-12)
HH: 24 Hour system(0-23)
mm: branch
ss: second
S: millisecond
E: What day is today?
D: What day of the year
F: The first few weeks of January(Will divide the total number of days this month by 7)
w: The first few weeks of the year
W: What week in January(It will be calculated according to the actual situation)
a: Morning and afternoon signs
k: and HH Almost. It means 24 hours a day(1-24). 
K: and hh Almost, it means 12 hours a day(0-11). 
z: Represents the time zone

Why is SimpleDateFormat thread thread thread unsafe?

Take a look at the source code of SimpleDateFormat

// Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);
		...
    }

The problem lies in the member variable calendar. If you use static definition when using SimpleDateFormat, SimpleDateFormat becomes a shared variable. The calendar in SimpleDateFormat can be accessed by multiple threads.

Verifying that the SimpleDateFormat thread is unsafe

public class SimpleDateFormatDemoTest {

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

    public static void main(String[] args) {
    		//1. Create thread pool
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //2. Assign tasks to the thread pool
        ThreadPoolTest threadPoolTest = new ThreadPoolTest();
        for (int i = 0; i < 10; i++) {
            pool.submit(threadPoolTest);
        }
        //3. Close thread pool
        pool.shutdown();
    }


    static class  ThreadPoolTest implements Runnable{

        private volatile int i=0;

        @Override
        public void run() {
            while (i<10){
				String dateString = simpleDateFormat.format(new Date());
				try {
					Date parseDate = simpleDateFormat.parse(dateString);
					String dateString2 = simpleDateFormat.format(parseDate);
					System.out.println(Thread.currentThread().getName()+" : "+i++);
					System.out.println(dateString.equals(dateString2));
					System.out.println("-------------------------");
				} catch (ParseException e) {
					e.printStackTrace();
				}
            }
        }
    }
}

false occurs twice, indicating that the thread is unsafe.

Solution

This is stipulated in Alibaba java Development Manual:

1. Do not define as a static variable

2. Locked: synchronized(lock)

3. Use DateTimeFormatter instead of SimpleDateFormat (DateTimeFormatter is thread safe and supported by java 8 +)

problem

1. tostring() is always output in the default time zone format of the system, which is unfriendly.

2. Time zone cannot be converted

3. The calculation of date and time is not easy, such as calculating addition and subtraction, comparing the difference between two dates by a few days, etc.

4. SimpleDateFormat objects that format dates and times are thread unsafe

5. The Date object itself is thread unsafe

public class Date
    implements java.io.Serializable, Cloneable, Comparable<Date>
{
    ...
}

Calendar

Supported version and above

JDK1.1

introduce

Calendar Class Description

Calendar class provides various methods to obtain or set various calendar fields. It has one more function to calculate Date and time than Date class.

Common usage of Calendar

		// Get current time:
		Calendar c = Calendar.getInstance();
		int y = c.get(Calendar.YEAR);
		int m = 1 + c.get(Calendar.MONTH);
		int d = c.get(Calendar.DAY_OF_MONTH);
		int w = c.get(Calendar.DAY_OF_WEEK);
		int hh = c.get(Calendar.HOUR_OF_DAY);
		int mm = c.get(Calendar.MINUTE);
		int ss = c.get(Calendar.SECOND);
		int ms = c.get(Calendar.MILLISECOND);
		System.out.println("Week returned:"+w);
		System.out.println(y + "-" + m + "-" + d + " "  + " " + hh + ":" + mm + ":" + ss + "." + ms);

As shown in the above figure, + 1 is required for month calculation; The returned week is calculated from Sunday. Sunday is 1, and 1 ~ 7 represents week;

Calendar's cross year problems and Solutions

problem

Background: when using Calendar's api getWeekYear() to read the year, the year obtained by the program may not be what we want in the week of the new year. For example, on the 30th of 2019, we need to return to 2019, and the result is 2020. Is it poisonous

// Get current time:
		Calendar c = Calendar.getInstance();
		c.clear();
		String str = "2019-12-30";
		try {
			c.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(str));
			int y = c.getWeekYear();
			System.out.println(y);
		} catch (ParseException e) {
			e.printStackTrace();
		}

Analyze the cause

Old rules, start with the source code

Calendar class
    -------------------------
    //@since 1.7 
    public int getWeekYear() {
        throw new UnsupportedOperationException();
    }

The source code is a little strange. The getWeekYear () method was introduced by java 7. How does its implementation throw an exception, but when it is executed, the result is returned.

Breakpoint follow-up via Calendar The Calendar instance obtained by getInstance () is GregorianCalendar

GregorianCalendar
    --------------------------------------
public int getWeekYear() {
        int year = get(YEAR); // implicitly calls complete()
        if (internalGetEra() == BCE) {
            year = 1 - year;
        }

        // Fast path for the Gregorian calendar years that are never
        // affected by the Julian-Gregorian transition
        if (year > gregorianCutoverYear + 1) {
            int weekOfYear = internalGet(WEEK_OF_YEAR);
            if (internalGet(MONTH) == JANUARY) {
                if (weekOfYear >= 52) {
                    --year;
                }
            } else {
                if (weekOfYear == 1) {
                    ++year;
                }
            }
            return year;
        }
        ...
        }

The year obtained in the method is normal at first

In JDK, the days at the end of the previous year will be determined as the first week of the next year, so the result of the above program is 1

Solution

Use the Calendar class get(Calendar.YEAR) to get the year

problem

1. When reading the month, use + 1

2. The returned week is calculated from Sunday. Sunday is 1, and 1 ~ 7 represents week

3. For the cross year problem of Calendar, use c.get(Calendar.YEAR) instead of c.getWeekYear();

4. When the specified time is the week of the year, call cl.get(Calendar.WEEK_OF_YEAR). Pay attention to the cross year problem. The value obtained for the week of the cross year is 1. The week closest to the new year is 52.

LocalDateTime

Supported version and above

jdk8

introduce

LocalDateTime class description

Represents the current date and time, equivalent to: yyyy mm ddthh: mm: SS

Common usage of LocalDateTime

Get current date and time

		LocalDate d = LocalDate.now(); // current date
		LocalTime t = LocalTime.now(); // current time 
		LocalDateTime dt = LocalDateTime.now(); // Current date and time
		System.out.println(d); // Print in strict accordance with ISO 8601 format
		System.out.println(t); // Print in strict accordance with ISO 8601 format
		System.out.println(dt); // Print in strict accordance with ISO 8601 format

The running result is feasible. The local date and time obtained through now() is always returned in the current default time zone

Gets the specified date and time

		LocalDate d2 = LocalDate.of(2021, 07, 14); // July 14, 2021, note that July = July
		LocalTime t2 = LocalTime.of(13, 14, 20); // 13:14:20
		LocalDateTime dt2 = LocalDateTime.of(2021, 07, 14, 13, 14, 20);
		LocalDateTime dt3 = LocalDateTime.of(d2, t2);
		System.out.println("Specify date and time:"+dt2);
		System.out.println("Specify date and time:"+dt3);

Addition, subtraction and modification of date and time

		LocalDateTime currentTime = LocalDateTime.now(); // Current date and time
		System.out.println("------------------Addition and subtraction of time and its modification-----------------------");
		//3. The addition and subtraction method of localdatetime includes all additions and subtractions of LocalDate and LocalTime. As mentioned above, only a brief introduction is given here
		System.out.println("3.Current time:" + currentTime);
		System.out.println("3.Current time plus 5 years:" + currentTime.plusYears(5));
		System.out.println("3.Current time plus 2 months:" + currentTime.plusMonths(2));
		System.out.println("3.Current time minus 2 days:" + currentTime.minusDays(2));
		System.out.println("3.Current time minus 5 hours:" + currentTime.minusHours(5));
		System.out.println("3.Current time plus 5 minutes:" + currentTime.plusMinutes(5));
		System.out.println("3.Current time plus 20 seconds:" + currentTime.plusSeconds(20));
		//It can also be used flexibly, such as adding one year back, subtracting one day forward, adding two hours back and subtracting five minutes forward
		System.out.println("3.Simultaneous modification(Add one year backward, one day forward, two hours backward and five minutes forward): " + currentTime.plusYears(1).minusDays(1).plusHours(2).minusMinutes(5));
		System.out.println("3.The revised year is 2025:" + currentTime.withYear(2025));
		System.out.println("3.Revised to December:" + currentTime.withMonth(12));
		System.out.println("3.The revision date is 27th:" + currentTime.withDayOfMonth(27));
		System.out.println("3.Modify the hour to 12:" + currentTime.withHour(12));
		System.out.println("3.Modify the minutes to 12:" + currentTime.withMinute(12));
		System.out.println("3.Modify the second to 12:" + currentTime.withSecond(12));

LocalDateTime and Date are converted to each other

Date to LocalDateTime

		System.out.println("------------------Method 1: write step by step-----------------------");
		//Instantiate a time object
		Date date = new Date();
		//Returns a moment representing the same point on the timeline as a date object
		Instant instant = date.toInstant();
		//Get the system default time zone
		ZoneId zoneId = ZoneId.systemDefault();
		//Gets the date and time band based on the time zone
		ZonedDateTime zonedDateTime = instant.atZone(zoneId);
		//Convert to LocalDateTime
		LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
		System.out.println("Method 1: Original Date = " + date);
		System.out.println("Method 1: transformed LocalDateTime = " + localDateTime);

		System.out.println("------------------Method 2: one step in place (recommended)-----------------------");
		//Instantiate a time object
		Date todayDate = new Date();
		//Instant. Ofepochmili (long l) uses milliseconds in the era of 1970-01-01T00:00:00Z to obtain an instance of instant
		LocalDateTime ldt = Instant.ofEpochMilli(todayDate.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
		System.out.println("Method 2: Original Date = " + todayDate);
		System.out.println("Method 2: transformed LocalDateTime = " + ldt);

LocalDateTime to Date

		System.out.println("------------------Method 1: write step by step-----------------------");
		//Gets the LocalDateTime object and the current time
		LocalDateTime localDateTime = LocalDateTime.now();
		//Get the system default time zone
		ZoneId zoneId = ZoneId.systemDefault();
		//Get the date and time with time zone according to the time zone
		ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
		//Returns a moment representing the same point on the timeline as a date object
		Instant instant = zonedDateTime.toInstant();
		//Convert to Date
		Date date = Date.from(instant);
		System.out.println("Method 1: Original LocalDateTime = " + localDateTime);
		System.out.println("Method 1: transformed Date = " + date);

		System.out.println("------------------Method 2: one step in place (recommended)-----------------------");
		//Instantiate a LocalDateTime object
		LocalDateTime now = LocalDateTime.now();
		//Convert to date
		Date dateResult = Date.from(now.atZone(ZoneId.systemDefault()).toInstant());
		System.out.println("Method 2: Original LocalDateTime = " + now);
		System.out.println("Method 2: transformed Date = " + dateResult);

Thread safety

Everyone on the Internet says that the LocalDateTime provided by JAVA 8 is thread safe, but how is it implemented

Let's dig today

public final class LocalDateTime
        implements Temporal, TemporalAdjuster, ChronoLocalDateTime<LocalDate>, Serializable {
        ... 
        }

From the above source code, we can see that LocalDateTime is an immutable class. We all know a Java Concurrent Programming rule: immutable objects are always thread safe.

Compared with the source code of Date, Date is a variable class, so it is thread unsafe.

public class Date
    implements java.io.Serializable, Cloneable, Comparable<Date>
{
...
}

ZonedDateTime

Supported version and above

jdk8

introduce

ZonedDateTime class description

Represents a date and time with time zone. ZonedDateTime can be understood as LocalDateTime+ZoneId

As can be seen from the source code, the ZonedDateTime class defines two variables, LocalDateTime and ZoneId.

The ZonedDateTime class is also immutable and thread safe.

public final class ZonedDateTime
        implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable {

    /**
     * Serialization version.
     */
    private static final long serialVersionUID = -6260982410461394882L;

    /**
     * The local date-time.
     */
    private final LocalDateTime dateTime;
    /**
     * The time-zone.
     */
    private final ZoneId zone;
    
    ...
}

Common usage of ZonedDateTime

Get current time + with time zone + time zone conversion

		// The default time zone gets the current time
		ZonedDateTime zonedDateTime = ZonedDateTime.now();
		// Use the specified time zone to obtain the current time. Asia/Shanghai is the Shanghai time zone
		ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
		//Withzonesameeinstant is the conversion time zone, and the parameter is ZoneId
		ZonedDateTime zonedDateTime2 = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));
		System.out.println(zonedDateTime);
		System.out.println(zonedDateTime1);
		System.out.println(zonedDateTime2);

LocalDateTime+ZoneId becomes ZonedDateTime

		LocalDateTime localDateTime = LocalDateTime.now();
		ZonedDateTime zonedDateTime1 = localDateTime.atZone(ZoneId.systemDefault());
		ZonedDateTime zonedDateTime2 = localDateTime.atZone(ZoneId.of("America/New_York"));
		System.out.println(zonedDateTime1);
		System.out.println(zonedDateTime2);

The above example shows that LocalDateTime can be converted into ZonedDateTime.

DateTimeFormatter

Supported version and above

jdk8

introduce

DateTimeFormatter class description

DateTimeFormatter is used to format and display, and DateTimeFormatter is an immutable class and thread safe.

public final class DateTimeFormatter {
...
}

When it comes to the formatted display of time, we have to say that the old friend SimpleDateFormat needs to be used before formatting Date. However, we know that SimpleDateFormat is thread unsafe and unclear, Look here – >

Common usage of DateTimeFormatter

		ZonedDateTime zonedDateTime = ZonedDateTime.now();
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
		System.out.println(formatter.format(zonedDateTime));

		DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
		System.out.println(usFormatter.format(zonedDateTime));

		DateTimeFormatter chinaFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
		System.out.println(chinaFormatter.format(zonedDateTime));

Pit of DateTimeFormatter

1. In normal configuration, string dates in standard format can be converted normally. If the month, day, hour, minute and second are less than two digits, you need to fill in 0. Otherwise, the conversion will fail and an exception will be thrown.

		DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
		LocalDateTime dt1 = LocalDateTime.parse("2021-7-20 23:46:43.946", DATE_TIME_FORMATTER);
		System.out.println(dt1);

Error will be reported:

java.time.format.DateTimeParseException: Text '2021-7-20 23:46:43.946' could not be parsed at index 5

Analysis reason: the format string does not match the actual time

"yyyy-MM-dd HH:mm:ss.SSS"

"2021-7-20 23:46:43.946"

The middle month format is MM, and the actual time is 7

Solution: keep the format string matching the actual time

	DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
		LocalDateTime dt1 = LocalDateTime.parse("2021-07-20 23:46:43.946", DATE_TIME_FORMATTER);
		System.out.println(dt1);

2. YYYY and DD are used with caution

		LocalDate date = LocalDate.of(2020,12,31);
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYYMM");
		// The result is 202112
		System.out.println( formatter.format(date));

Java's DateTimeFormatter pattern "YYYY" gives you the week-based-year, (by default, ISO-8601 standard) the year of the Thursday of that week.

YYYY is the year of the current week, and the week based year is specified in ISO 8601. December 31, 2020, the weekly year, is 2021

 private static void tryit(int Y, int M, int D, String pat) {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pat);
        LocalDate         dat = LocalDate.of(Y,M,D);
        String            str = fmt.format(dat);
        System.out.printf("Y=%04d M=%02d D=%02d " +
            "formatted with " +
            "\"%s\" -> %s\n",Y,M,D,pat,str);
    }
    public static void main(String[] args){
        tryit(2020,01,20,"MM/DD/YYYY");
        tryit(2020,01,21,"DD/MM/YYYY");
        tryit(2020,01,22,"YYYY-MM-DD");
        tryit(2020,03,17,"MM/DD/YYYY");
        tryit(2020,03,18,"DD/MM/YYYY");
        tryit(2020,03,19,"YYYY-MM-DD");
    }
Y=2020 M=01 D=20 formatted with "MM/DD/YYYY" -> 01/20/2020
Y=2020 M=01 D=21 formatted with "DD/MM/YYYY" -> 21/01/2020
Y=2020 M=01 D=22 formatted with "YYYY-MM-DD" -> 2020-01-22
Y=2020 M=03 D=17 formatted with "MM/DD/YYYY" -> 03/77/2020
Y=2020 M=03 D=18 formatted with "DD/MM/YYYY" -> 78/03/2020
Y=2020 M=03 D=19 formatted with "YYYY-MM-DD" -> 2020-03-79

The last three dates are problematic, because the capitalized DD represents the day of the year, not the day of the month, but DD is no problem.

Examples refer to: https://www.cnblogs.com/tonyY/p/12153335.html

Therefore, yyyy and dd are recommended.

Instant

Supported version and above

jdk8

introduce

Instant class description

public final class Instant
        implements Temporal, TemporalAdjuster, Comparable<Instant>, Serializable {
        ...
        }

Instant is also immutable and thread safe. In fact, Java The time package is thread safe.

Instant is a new feature in java 8, which has two core fields

	...	
	private final long seconds;
    
    private final int nanos;
	...

One is a timestamp in seconds, and the other is a timestamp in nanoseconds.

Is it with * * system Currenttimemillis() * * the returned long timestamp is very similar to that of system Currenttimemillis() returns milliseconds, and Instant has a more accurate nanosecond timestamp.

Common usage of Instant

 		Instant now = Instant.now();
		System.out.println("now:"+now);
		System.out.println(now.getEpochSecond()); // second
		System.out.println(now.toEpochMilli()); // millisecond

Instant does not have a time zone, but it can be converted to ZonedDateTime after adding the time zone

		Instant ins = Instant.now();
		ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
		System.out.println(zdt);

long timestamp to Instant

Pay attention to the time unit of long timestamp. Select the conversion method corresponding to Instant

//1626796436 is a second timestamp
Instant ins = Instant.ofEpochSecond(1626796436);
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
System.out.println("Second timestamp conversion:"+zdt);
//1626796436111l is a second timestamp
Instant ins1 = Instant.ofEpochMilli(1626796436111l);
ZonedDateTime zdt1 = ins1.atZone(ZoneId.systemDefault());
System.out.println("Millisecond timestamp conversion:"+zdt1);

Instant pit

Instant. The time obtained by now() differs from Beijing time by 8 time zones. This is a detail and should be avoided.

Look at the source code, using UTC time.

public static Instant now() {
        return Clock.systemUTC().instant();
    }

Solution:

Instant now = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
System.out.println("now:"+now);

Keywords: Java date LocalDateTime

Added by php-phan on Tue, 18 Jan 2022 08:31:04 +0200