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
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);