Preface

Hello , I am a A Brother (YourBatman).

The purpose of this series is to be clear 、 A complete date / Almost everything that time deals with case. Last article Lay out all the concepts involved , for example GMT、UTC、 Daylight saving time 、 Time stamp and so on , If you haven't seen it yet , Not only strongly recommended, but Mandatory advice You go to use flowers 5 Take a minute to see , Because date time processing is more special , The actual combat must be based on the understanding of the concept , Otherwise, it's likely that you'll still see flowers in the fog .

explain : date / Time management is a common problem in daily development , The reason is the concept of date and time 、 Unfamiliar with application scenarios , So don't ignore it

First concept , This article is a practical operation , They complement each other , Be short of one cannot . There are many contents in this article , Longer text , Expected to exceed 2w word , It's designed to help you completely Java Processing of date and time , I suggest you collect it , Keep it for reference .

Outline of this article

Version conventions

  • JDK:8

Text

So many concepts have been laid out above , As a Javaer Of course, they are the most concerned “ Concept ” stay Java The landing in the garden . How to deal with time in daily work ? use Date still JDK 8 Date time after API? How to solve the big problems such as cross time zone conversion .A Elder brother has always been in charge of production and maintenance , Kill and bury , So this article takes you to appreciate it ,Java How to achieve GMT and UTC Of ?

as everyone knows ,JDK In version 8 As a boundary , There are two sets of processing dates / The time of the API:

Although I always encourage abandonment Date The support is only used in the project JSR 310 Date time type , But what? , because Date There is still a huge stock of users , So this article is not alone , The realization of both are described .

Date Type implementation

java.util.Date stay JDK 1.0 existed , Used to represent a date + The type of time , Even though it's very old , And the responsibilities of this kind are not single , It is very inconvenient to use , However, due to the historical reasons of more than ten or twenty years , Its vitality is still tenacious , Huge number of users .

Let's meet first Date, Take a look at the output of this example :

@Test
public void test1() {
Date currDate = new Date();
System.out.println(currDate.toString());
// already @Deprecated
System.out.println(currDate.toLocaleString());
// already @Deprecated
System.out.println(currDate.toGMTString());
}

Run the program , Output :

Fri Jan 15 10:22:34 CST 2021
2021-1-15 10:22:34
15 Jan 2021 02:22:34 GMT

first : The standard UTC Time (CST That's the offset +0800)

the second : Local time , Time format displayed according to local time zone

Third :GTM Time , That's Greenwich time , You can see it's early in the morning 2 spot ( Beijing time is in the morning 10 Oh, oh. )

the second 、 The third is actually JDK 1.1 It's all marked as @Deprecated Out of date , Basically no reuse . If necessary, switch to local time or GTM Time output , Please use the formatter java.text.DateFormat To deal with .

The time zone / Offset TimeZone

stay JDK8 Before ,Java The time zone and offset are used java.util.TimeZone To represent the .

In general , Use static methods TimeZone#getDefault() You can get the current JVM The time zone in which you are running , For example, you run programs in China , This method returns the Chinese time zone ( It's also called Beijing time zone 、 Beijing time. ).

Sometimes you need to do it With time zone Time conversion of , for example : The return value of the interface should include both the display time and Beijing time , New York time, too . It's time to get the time zone of New York , Take Beijing time as the benchmark, and make a time zone conversion on it :

@Test
public void test2() {
String patternStr = "yyyy-MM-dd HH:mm:ss";
// Beijing time. (new This is the default time zone )
Date bjDate = new Date(); // Get the time zone of New York
TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York");
// According to the time zone Convert Beijing time to New York time Date
DateFormat newYorkDateFormat = new SimpleDateFormat(patternStr);
newYorkDateFormat.setTimeZone(newYorkTimeZone);
System.out.println(" This is Beijing time :" + new SimpleDateFormat(patternStr).format(bjDate));
System.out.println(" It's New York time :" + newYorkDateFormat.format(bjDate));
}

Run the program , Output :

 This is Beijing time :2021-01-15 11:48:16
It's New York time :2021-01-14 22:48:16

(11 + 24) - 22 = 13, Beijing is faster than New York 13 There's nothing wrong with it for an hour .

Be careful : Two times should mean the same moment , That is to say, the timestamp values are equal

So here comes the question , How do you know how to get the time zone of New York America/New_York This zoneId Well ? Can I just write a string ?

The answer is of course not , There are rules to follow . Next, I will introduce two kinds of search methods zoneId The way , choose any as you like :

Mode one : use Java The program takes all available zoneId Print out , Then look it up

@Test
public void test3() {
String[] availableIDs = TimeZone.getAvailableIDs();
System.out.println(" You can use zoneId total :" + availableIDs.length);
for (String zoneId : availableIDs) {
System.out.println(zoneId);
}
}

Run the program , Output ( Most of them conform to the law :/ State before ,/ Indicates the name of the city ):

 You can use zoneId total :628
Africa/Abidjan
Africa/Accra
...
Asia/Chongqing // Asia / Chongqing
Asia/Shanghai // Asia / Shanghai
Asia/Dubai // Asia / dubai
...
America/New_York // America / New York
America/Los_Angeles // America / Los Angeles
...
Europe/London // The European / London
...
Etc/GMT
Etc/GMT+0
Etc/GMT+1
...

It's worth noting that there is not Asia/Beijing Oh .

explain : This result is based on JDK 8 edition , There may be differences in the total number of outputs from different versions , But it's mainstream ZoneId Generally, there will be no change

Mode two

zoneId The list is jre Maintenance of a text file , The path is you JDK/JRE Installation path for . The address in .\jre\lib The name of the directory is not found tzmappings In the text file of . Open this file ctrl + f Searching can also achieve the purpose of searching .

These two kinds of houses can help you find them ZoneId It's easy to look up in your dictionary , But there is another situation : What about the current city , stay tzmappings There's nothing in the file ( For example, not included ), Then we need to get the time of this place to show how to break it ? Although the probability is very small , But not necessarily , After all, there are so many countries and cities in the world ~

Java Naturally, this was taken into account , So there are ways : Specifies the numeric representation of its time zone , It's also called offset ( Don't tell me the time zone of this place is unknown , That's hopeless ), The following example

@Test
public void test4() {
System.out.println(TimeZone.getTimeZone("GMT+08:00").getID());
System.out.println(TimeZone.getDefault().getID()); // New York time
System.out.println(TimeZone.getTimeZone("GMT-05:00").getID());
System.out.println(TimeZone.getTimeZone("America/New_York").getID());
}

Run the program , Output :

GMT+08:00 // The effect is equivalent to Asia/Shanghai
Asia/Shanghai
GMT-05:00 // The effect is equivalent to America/New_York
America/New_York

It is worth noting that , You can only use GMT+08:00, But can't use UTC+08:00, The reasons are explained below .

Set the default time zone

Generally speaking ,JVM Where to run , What is the default time zone . For domestic programmers , Generally, we only touch the East eighth district , It's Beijing time ( Local time ). As international cooperation becomes closer and closer , Most of the time, it needs date and time internationalization , Take a very practical example : Deployment of the same application in alicloud 、 stay AWS( overseas ) We have also deployed one for overseas users , here The same code Deployed in different time zones , How to deal with it ?

If the time zone is different , So it will affect the running result of the program , It's easy to make mistakes in computational logic , It's probably going to be a mess .Java Let's do it in a variety of ways Manual Set up / Change the default time zone :

  1. API The way : Force time zone to Beijing TimeZone.setDefault(TimeZone.getDefault().getTimeZone("GMT+8"));
  2. JVM Parameters of the way :-Duser.timezone=GMT+8
  3. Operation and maintenance setting mode : Set the operating system host time zone to Beijing time zone , This is the recommended way , Can be totally insensitive to developers , It also facilitates the unified management of operation and maintenance

As far as I know , Many companies in Alibaba cloud 、 Tencent cloud 、 When deploying applications on cloud hosts at home and abroad , All of them adopt operation and maintenance to set up a unified time zone : China time zone , It's managed in this way , This eliminates the inconsistency of the default time zone for the program , Developer friendly .

Annoying daylight saving time

You know what? , China used to use daylight saving time .

What is daylight saving time ? Poke it here

What's the closest to now 1986 - 1991 Summer time was used in ( Every year, 4 The first Sunday in the middle of the month 2 when - 9 The first Sunday in the middle of the month 2 When the check ):

1986 year 5 month 4 solstice 9 month 14 Japan

1987 year 4 month 12 solstice 9 month 13 Japan

1988 year 4 month 10 solstice 9 month 11 Japan

1989 year 4 month 16 solstice 9 month 17 Japan

1990 year 4 month 15 solstice 9 month 16 Japan

1991 year 4 month 14 solstice 9 month 15 Japan

Daylight saving time is a good time “ It's very annoying ” Things that are , It greatly increases the complexity of date time processing . Like this soul torture : If your date of birth is 1988-09-11 00:00:00( Last day of daylight saving time ) And stored in the database , Think about it , Is it possible that there will be a problem with the formatting of this date , Is it possible for you to format it as 1988-09-10 23:00:00 Well ?

For this torture , I simulated the following code :

@Test
public void test5() throws ParseException {
String patterStr = "yyyy-MM-dd";
DateFormat dateFormat = new SimpleDateFormat(patterStr); String birthdayStr = "1988-09-11";
// character string -> Date -> character string
Date birthday = dateFormat.parse(birthdayStr);
long birthdayTimestamp = birthday.getTime();
System.out.println(" What's Lao Wang's birthday :" + birthday);
System.out.println(" What is the time stamp of Lao Wang's birthday :" + birthdayTimestamp); System.out.println("============== The program went through a turnaround , At the same time as I Method input parameter passed the date of birth =============");
// character string -> Date -> Time stamp -> Date -> character string
birthday = new Date(birthdayTimestamp);
System.out.println(" What's Lao Wang's birthday :" + birthday);
System.out.println(" What is the time stamp of Lao Wang's birthday :" + dateFormat.format(birthday));
}

This code , In different JDK Run... Under version , Probably There are different results , If you are interested, you can copy Go and try it yourself .

About JDK Processing daylight saving time ( Summer time in China ) There have been problems and causes bug, The corresponding JDK The version is 1.8.0_2xx There's something wrong with the date formatting in the previous version , The version after that seems to be OK . The version information I provide here is for reference only , In case of similar problems case Just upgrade JDK Let's get the latest version , Generally, there will be no problem .

What's going on here JDK Very small version numbers , It's not easy to locate the precise version number limit , So for reference only

in general , As long as you are using a newer version JDK, Developers don't have to care about daylight saving time , Even though there are still many countries around the world using daylight saving time , We just need to face The time zone It's OK to change time .

Date Time zone independence

class Date Represents a specific time Instantaneous , Precision in milliseconds . Since it means instant / moment , Then it must have nothing to do with the time zone , Look at the code below :

@Test
public void test6() {
String patterStr = "yyyy-MM-dd HH:mm:ss";
Date currDate = new Date(System.currentTimeMillis()); // Beijing time zone
DateFormat bjDateFormat = new SimpleDateFormat(patterStr);
bjDateFormat.setTimeZone(TimeZone.getDefault());
// New York time zone
DateFormat newYorkDateFormat = new SimpleDateFormat(patterStr);
newYorkDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
// London time zone
DateFormat londonDateFormat = new SimpleDateFormat(patterStr);
londonDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/London")); System.out.println(" Number of milliseconds :" + currDate.getTime() + ", Beijing local time :" + bjDateFormat.format(currDate));
System.out.println(" Number of milliseconds :" + currDate.getTime() + ", New York local time :" + newYorkDateFormat.format(currDate));
System.out.println(" Number of milliseconds :" + currDate.getTime() + ", London local time :" + londonDateFormat.format(currDate));
}

Run the program , Output :

 Number of milliseconds :1610696040244, Beijing local time :2021-01-15 15:34:00
Number of milliseconds :1610696040244, New York local time :2021-01-15 02:34:00
Number of milliseconds :1610696040244, London local time :2021-01-15 07:34:00

in other words , Same millisecond value , According to the time zone / Different offsets can show the time of many places , This proves Date Its time zone independence .

To be exact :Date The object is from Greenwich mean time ( GMT)1970 year 1 month 1 Japan 0 Point to the Date The number of milliseconds elapsed at the time represented , It's a number .

Read the string as Date type

This is a very common requirement in development :client The requester throws you a string like "2021-01-15 18:00:00", And then you need to turn it into Date type , How to deal with it ?

The problem is coming. , Throw me a string naked and say it's 15 No. In the evening 6 Point time , How do I know you mean the evening in Beijing 6 spot , It's the night in Tokyo 6 How about it? ? Or new york night 6 How about it? ?

therefore , For date time in string form , It makes sense only if you specify a time zone . in other words character string + The time zone To know exactly when it is , Otherwise, there is ambiguity .

Maybe you will , In my normal development, the front end is to throw a string to me , Then I format it as a Date type , There is no time zone parameter passed in , I haven't seen any problems since I've been running for so long . As shown below :

@Test
public void test7() throws ParseException {
String patterStr = "yyyy-MM-dd HH:mm:ss"; // Time string to simulate request parameters
String dateStrParam = "2020-01-15 18:00:00"; // The simulation server converts this service to Date type
DateFormat dateFormat = new SimpleDateFormat(patterStr);
System.out.println(" What is the time zone for the formatter :" + dateFormat.getTimeZone().getID());
Date date = dateFormat.parse(dateStrParam);
System.out.println(date);
}

Run the program , Output :

 What is the time zone for the formatter :Asia/Shanghai
Wed Jan 15 18:00:00 CST 2020

It seems that the result is OK . in fact , That's because by default, you have a contract with both sides : Both parties use Beijing time ( The time zone ), Since it's the same time zone , So there won't be any problem in exchanging what you need . I don't believe you give your interface to overseas users for debugging ?

For formatters , Generally speaking, we don't need to give DateFormat Set time zone ( Then use the default time zone ) It can be converted normally . But as a master, you have to be clear , Clearly know that this is due to interactive double default There is a contract in the same time zone .

SimpleDateFormat format

Java Chinese vs Date Input and output of type / format , Recommended DateFormat Instead of using it toString() Method .

DateFormat Is a time formatter abstract class ,SimpleDateFormat Is its concrete implementation class , Used to Language environment sensitivity Format and parse dates in the same way . It allows formatting ( date → Text )、 analysis ( Text → date ) And standardization .

Focus on : Sensitive to language environment , That is to say, to the environment Locale、 The time zone TimeZone It's all sensitive . Since it's sensitive , That's it Customizable

For a formatter , Pattern ( Template ) Is the key factor , Get to know :

date / Time pattern

The format pattern consists of the specified string , Unquoted capitalization / Lowercase letters (A-Z a-z) Represents a specific pattern , Used to express the meaning of a pattern , If you want to Original output You can use single quotes '' wrap up , Except for the English letters, no other explanation of the original output / matching . Here is the pattern letter it specifies ( Other letters output as is ):

Letter meaning Match type Example
y year Year 2020,20
M month Month July; Jul; 07
d Days of the month ( Commonly known as day , Maximum 31) Number 10
H Hours (0-23) Number 0,23
m minute (0-59) Number 30,59
s second (0-59) Number 30,59
--- --- --- yyyy-MM-dd HH:mm:ss( The separator can be any character , Even Chinese characters )
Y Year of current week Year 2020( Not recommended , Zhou ruo's new year )
S Number of milliseconds (1-999) Number 999
a am/pm Text PM
z The time zone Universal time zone Pacific Standard Time; PST; GMT-08:00
Z The time zone RFC 822 The time zone -0800,+0800
X The time zone ISO 8601 The time zone -08; -0800; -08:00
G years Text AD( A.D. )、BC( B.c. )
D Days of the year (1-366) Number 360
w The number of weeks in a year (1-54) Number 27
W Number of weeks in the month (1-5) Number 3
E Day of the week Text Tuesday; Tue
u Day of the week (1=Monday...) Number 1
k Hours (1-24) Number Not recommended
K/h am/pm The number of hours Number General coordination a Use it together

There are some problems in this table “ special ” Match type for , Explain as follows :

  • Text: format (Date -> String), If the number of pattern letters is 4 One or more , Use the full form ; otherwise , If possible , Use short or abbreviated forms . For parsing (String -> Date), Both forms are the same , It has nothing to do with the number of pattern letters
@Test
public void test9() throws ParseException {
String patternStr = "G GG GGGGG E EE EEEEE a aa aaaaa";
Date currDate = new Date(); System.out.println("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Chinese regional model ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
System.out.println("====================Date->String====================");
DateFormat dateFormat = new SimpleDateFormat(patternStr, Locale.CHINA);
System.out.println(dateFormat.format(currDate)); System.out.println("====================String->Date====================");
String dateStrParam = " A.D. A.D. A.D. Saturday Saturday Saturday Afternoon Afternoon Afternoon ";
System.out.println(dateFormat.parse(dateStrParam)); System.out.println("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ English regional model ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
System.out.println("====================Date->String====================");
dateFormat = new SimpleDateFormat(patternStr, Locale.US);
System.out.println(dateFormat.format(currDate)); System.out.println("====================String->Date====================");
dateStrParam = "AD ad bC Sat SatUrday sunDay PM PM Am";
System.out.println(dateFormat.parse(dateStrParam));
}

Run the program , Output :

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Chinese regional model ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
====================Date->String====================
A.D. A.D. A.D. Saturday Saturday Saturday Afternoon Afternoon Afternoon
====================String->Date====================
Sat Jan 03 12:00:00 CST 1970
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ English regional model ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
====================Date->String====================
AD AD AD Sat Sat Saturday PM PM PM
====================String->Date====================
Sun Jan 01 00:00:00 CST 1970

Observe the print results , In addition to conforming to the pattern rules , It can still be in String -> Date When parsing Two conclusions are drawn :

  1. English words , No partition case . Such as SatUrday sunDay No problem , however You can't There are spelling mistakes
  2. If there are multiple part To express an idea , that last win. Such as Sat SatUrday sunDay The last one comes into effect

about Locale Regional parameters , Because there is no format in Chinese 、 Characteristics of abbreviations , Therefore, these rules only apply to English speaking regions ( Such as Locale.US take effect )

  • Number: format (Date -> String), The number of pattern letters is numeric 【 Minimum 】 Number , Shorter numbers are filled with zeros to this number . For parsing (String -> Date), The number of pattern letters will be ignored , Unless you need to separate two adjacent fields
  • Year: For formatting and parsing , If the number of pattern letters is 4 One or more , A calendar specific long format is used . otherwise , Use a calendar specific short or abbreviated form
  • Month: If the number of pattern letters is 3 One or more , Is interpreted as text ; otherwise , It will be interpreted as a number .
  • Universal time zone : If the time zone has a name , Such as Pacific Standard Time、PST、CST Wait, then use the name , Otherwise it will be used. GMT The string of the rule , Such as :GMT-08:00
  • RFC 822 The time zone : follow RFC 822 Format , Downward compatible universal time zone ( Except the name part )
  • ISO 8601 The time zone : For formatting , If the GMT The offset value of is 0( That's Greenwich mean time ), Generate a “Z”; If the number of pattern letters is 1, Any fraction of the hour is ignored . for example , If the mode is “X”, The time zone is “GMT+05:30”, Generate a “+05”. When parsing ,“Z” Is interpreted as UTC Time zone indicator . General time zones are not accepted . If the number of pattern letters is 4 One or more , In the structure SimpleDateFormat Or application mode IllegalArgumentException.
    • It's hard to understand this rule , It is generally not recommended to use this mode in development . If you want to use it, be sure to test it locally

SimpleDateFormat It's easy to use , The key is to understand its rules and patterns . Last on SimpleDateFormat Let's emphasize these two points again :

  1. SimpleDateFormat Is not a thread safe class , Please pay attention to concurrency security when using
  2. If you use SimpleDateFormat To format as a non local region ( Default Locale) Words , It must be specified at the time of construction , Such as Locale.US
  3. about Date Any formatting of type 、 Please use it uniformly SimpleDateFormat

JSR 310 type

There was a man who made an interesting vote , Statistical right Java API Of dissatisfied Degree of . Final Java Date/Calendar API The second worst ( The first one is bad Java XML/DOM), It has more rotten points , Here are some examples :

  1. The definitions are not consistent , stay java.util and java.sql It's all in the bag Date class , And format it / The analytic class ran to java.text Went to the , Schizophrenia
  2. java.util.Date The behavior of the class is inconsistent in the design of the modeling date , Obvious defects . Including variability 、 Bad offset 、 The default value is 、 Naming and so on
  3. java.util.Date Include both date and time , And its subclass java.sql.Date But only the date , What kind of divine inheritance is this ?

@Test
public void test10() {
long currMillis = System.currentTimeMillis(); java.util.Date date = new Date(currMillis);
java.sql.Date sqlDate = new java.sql.Date(currMillis);
java.sql.Time time = new Time(currMillis);
java.sql.Timestamp timestamp = new Timestamp(currMillis); System.out.println("java.util.Date:" + date);
System.out.println("java.sql.Date:" + sqlDate);
System.out.println("java.sql.Time:" + time);
System.out.println("java.sql.Timestamp:" + timestamp);
}

Run the program , Output :

java.util.Date:Sat Jan 16 21:50:36 CST 2021
java.sql.Date:2021-01-16
java.sql.Time:21:50:36
java.sql.Timestamp:2021-01-16 21:50:36.733
  • Internationalization is not well supported , For example, cross time zone operation 、 Summer time and so on

Java I can't stand such a hard time API 了 , So in 2014 Years as Java 8 The release of introduced a new JSR 310 Date time .JSR-310 From the boutique time library joda-time make , Solve the above problems All questions , As a whole Java 8 One of the biggest highlights .

JSR 310 date / Time be-all API All in java.time It's in this bag , No exceptions .

Of course , This article does not focus on discussion JSR 310 date / Time system , But look at JSR 310 How do date time types handle the above Date The types encountered case Of .

The time zone / Offset ZoneId

stay JDK 8 Before ,Java Use java.util.TimeZone To represent the time zone . And in the JDK 8 We use the ZoneId Time zone ,ZoneOffset Express UTC The offset .

It is worth emphasizing in advance , Time zone and offset are quite different in concept and in practice , Mainly reflected in :

  1. UTC The offset only records the hours and minutes of the offset , There is no other information . for instance :+08:00 It means better than UTC It's early 8 Hours , There is no geography / Time zone meaning , Corresponding -03:30 It's just more than UTC It's late 3 One and a half hours
  2. Time zones are region specific , It's not the same as geographical areas ( Including rules ) Strong binding . For example, the whole of China is called the eighth East District , New York is in west five and so on

There is no daylight saving time in China , The corresponding offset of all eight East regions is always +8; New York has summer time , So its offset may be -4 It could be -5 Oh

Taken together , Better time zone . The irritating issue of daylight saving time , If you use UTC It's a lot of trouble to express the offset , Because it's variable : Some periods of the year are offset from the original +1, Certain periods -1; But if you use ZoneId Time zone is very convenient , For example, New York is west five , You can get the right answer anytime you get the local time , Because it has built-in processing of daylight saving time rules , Which means when +1 When -1 The time zone is clear , Unwanted API Callers care .

UTC Offset is more like a way to write the offset value , There is no time zone rule in China ( There is no daylight saving time ) There will be no problems in our country , East eight district and UTC+08:00 The effect will always be the same . But in some summer time countries ( Such as the United States 、 France and so on ), You can only get the local time according to the time zone . So when you don't know the local rules , It's better to use time zones rather than offsets .

ZoneId

It represents a time zone ID, Such as Europe/Paris. It sets out some rules that can be used to put a Instant Time stamp to local date / Time LocalDateTime.

It says time zone ZoneId There are rules , In fact, the actual rules that describe when and how the offset changes are created by java.time.zone.ZoneRules Definition .ZoneId It's just a tool to get the underlying rules ID. The reason for this approach , Because Rules are defined by the government , And it changes all the time , and ID It is stable. .

about API This is all the caller needs to use ID( That is to say ZoneId) that will do , And don't care about the lower level of time zone rules ZoneRules, and “ The government ” The thing about the synchronization rule is that the things in its domain are left to it . Such as : The rule of daylight saving time is made by governments , And different countries and years are generally different , It's up to you JDK At the bottom ZoneRules The mechanism itself sync, Users don't need to care .

ZoneId It's unique in the system , It consists of three types ID:

  1. The simplest ID type :ZoneOffset, It consists of 'Z' And with '+' or '-' At the beginning id form . Such as :Z、+18:00、-18:00
  2. Another type of ID It's an offset style with some form of prefix ID, for example 'GMT+2' or 'UTC+01:00'. Recognizable ( legal ) The prefix is 'UTC', 'GMT' and 'UT'
  3. The third type is region based ID( Recommended ). Region based ID Must contain two or more characters , And not with 'UTC'、'GMT'、'UT' '+' or '-' start . Region based id Defined by configuration , Such as Europe/Paris

A big push on the concept , Let's give a few code examples to feel it .

1、 Get the system default ZoneId:

@Test
public void test1() {
// JDK 1.8 What we did before
System.out.println(TimeZone.getDefault());
// JDK 1.8 What to do after
System.out.println(ZoneId.systemDefault());
} Output :
Asia/Shanghai
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null]

The results are the same , All are Asia/Shanghai. because ZoneId The bottom layer of a method is dependency TimeZone, Pictured :

2、 Specifies the string to get a ZoneId:

@Test
public void test2() {
System.out.println(ZoneId.of("Asia/Shanghai"));
// Report errors :java.time.zone.ZoneRulesException: Unknown time-zone ID: Asia/xxx
System.out.println(ZoneId.of("Asia/xxx"));
}

Obviously , This string can't be written casually . So here comes the question , What can be written ? alike ZoneId Provides API For you to get all the available strings id, If you are interested, please try it yourself :

@Test
public void test3() {
ZoneId.getAvailableZoneIds();
}

3、 We get one from the offset ZoneId:

@Test
public void test4() {
ZoneId zoneId = ZoneId.ofOffset("UTC", ZoneOffset.of("+8"));
System.out.println(zoneId);
// It must be in capital Z
zoneId = ZoneId.ofOffset("UTC", ZoneOffset.of("Z"));
System.out.println(zoneId);
} Output :
UTC+08:00
UTC

Here is the prefix of the first parameter , The available values are :"GMT", "UTC", or "UT". Of course, it can also pass empty strings , Then return the second parameter directly ZoneOffset. If none of the above is true, report an error

Be careful : According to the offset ZoneId There are no ready-made time zone rules available internally , So for countries with summer camps, the transition may be problematic , This is generally not recommended .

4、 Get the time zone from the date :

@Test
public void test5() {
System.out.println(ZoneId.from(ZonedDateTime.now()));
System.out.println(ZoneId.from(ZoneOffset.of("+8"))); // Report errors :java.time.DateTimeException: Unable to obtain ZoneId from TemporalAccessor:
System.out.println(ZoneId.from(LocalDateTime.now()));
System.out.println(ZoneId.from(LocalDate.now()));
}

Although the method into the parameter is TemporalAccessor, But only types with time zone are accepted ,LocalXXX You can't , Pay attention when using .

ZoneOffset

Distance to Greenwich /UTC Time zone offset for , for example +02:00. It's worth noting that it's inherited from ZoneId, So it can also be used as an example ZoneId To use the , Of course, it's not recommended , Please use it independently .

The time zone offset is the time zone and Greenwich /UTC Time difference between . It's usually a fixed number of hours and minutes . Different parts of the world have different time zone offsets . stay ZoneId Class to capture rules about how the offset changes with the place and time of the year ( It's mainly the summer time rule ), So inherited from ZoneId.

1、 Minimum / Maximum offset : Because the offset is a number , This is limited

@Test
public void test6() {
System.out.println(" Minimum offset :" + ZoneOffset.MIN);
System.out.println(" Minimum offset :" + ZoneOffset.MAX);
System.out.println(" Center offset :" + ZoneOffset.UTC);
// Out of range
System.out.println(ZoneOffset.of("+20"));
} Output :
Minimum offset :-18:00
Minimum offset :+18:00
Center offset :Z java.time.DateTimeException: Zone offset hours not in valid range: value 20 is not in the range -18 to 18

2、 The offset is constructed by time, minute and second ( Easy to use , recommend ):

@Test
public void test7() {
System.out.println(ZoneOffset.ofHours(8));
System.out.println(ZoneOffset.ofHoursMinutes(8, 8));
System.out.println(ZoneOffset.ofHoursMinutesSeconds(8, 8, 8)); System.out.println(ZoneOffset.ofHours(-5)); // Specify an exact number of seconds Get instance ( Sometimes it's useful )
System.out.println(ZoneOffset.ofTotalSeconds(8 * 60 * 60));
} // Output :
+08:00
+08:08
+08:08:08
-05:00
+08:00

It seems , The offset can be accurate to seconds , It's just that it's generally accurate to the minute, and it's at the top .

Set the default time zone

ZoneId There is no way to set the default time zone , But we can see from the article ZoneId To get the default time zone, the underlying dependency is TimeZone.getDefault() Method , So setting the default time zone completely follows TimeZone In this way ( There are three ways , Do you remember? ?).

Annoying daylight saving time

Because of the existence of daylight saving time rules , Let operation date / The time complexity is greatly increased . But also good JDK Try to shield the impact of these rules on users . therefore : Time zone recommended (ZoneId) Date of conversion / Time , Generally, it is not recommended to use offset ZoneOffset Go ahead , So there won't be any worries about daylight saving time .

JSR 310 Time zone correlation

java.util.Date It is time zone independent , The drawback is that when it comes to international time conversion and other needs , Use Date It's very inconvenient to deal with it .

JSR 310 It's solved Date There are a series of problems : Right date 、 Time is expressed separately (LocalDate、LocalTime、LocalDateTime), Local time and time with time zone are managed separately .LocalXXX Represents local time , That is to say, the current situation JVM Time in your time zone ;ZonedXXX It means a With time zone Date time of , They can easily transform each other .

@Test
public void test8() {
// Local date / Time
System.out.println("================ Local time ================");
System.out.println(LocalDate.now());
System.out.println(LocalTime.now());
System.out.println(LocalDateTime.now()); // Time zone time
System.out.println("================ Time with time zone ZonedDateTime================");
System.out.println(ZonedDateTime.now()); // Use system time zone
System.out.println(ZonedDateTime.now(ZoneId.of("America/New_York"))); // Specify your own time zone
System.out.println(ZonedDateTime.now(Clock.systemUTC())); // Specify your own time zone
System.out.println("================ Time with time zone OffsetDateTime================");
System.out.println(OffsetDateTime.now()); // Use system time zone
System.out.println(OffsetDateTime.now(ZoneId.of("America/New_York"))); // Specify your own time zone
System.out.println(OffsetDateTime.now(Clock.systemUTC())); // Specify your own time zone
}

Run the program , Output :

================ Local time ================
2021-01-17
09:18:40.703
2021-01-17T09:18:40.703
================ Time with time zone ZonedDateTime================
2021-01-17T09:18:40.704+08:00[Asia/Shanghai]
2021-01-16T20:18:40.706-05:00[America/New_York]
2021-01-17T01:18:40.709Z
================ Time with time zone OffsetDateTime================
2021-01-17T09:18:40.710+08:00
2021-01-16T20:18:40.710-05:00
2021-01-17T01:18:40.710Z

The output of local time is very good “ clean ”, It can be directly used for display . The time with time zone shows which time zone the time represents , After all, there's no point in not specifying a time zone .LocalXXX Because it is time zone independent , So it doesn't represent a moment / moment .

in addition , About LocalDateTime、OffsetDateTime、ZonedDateTime Cross time zone conversion of the three , And their detailed explanation , Because the content is too much in the following special article , Keep an eye on .

Read the string as JSR 310 type

A separate date time type string, such as 2021-05-05T18:00-04:00 It doesn't make any sense , Because there's no time zone to be sure it represents that moment , It's a theory, and of course it suits JSR 310 Type .

A date time format string was encountered , To analyze it, there are two cases :

  1. No time zone / Offset string : Or ignore it and say you can't change , Or Set a time zone ( Generally, the system default time zone is used ), Use LocalDateTime Parsing
@Test
public void test11() {
String dateTimeStrParam = "2021-05-05T18:00";
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStrParam);
System.out.println(" After the parsing :" + localDateTime);
} Output :
After the parsing :2021-05-05T18:00
  1. Time zone word / Character string of offset :
@Test
public void test12() {
// With offset Use OffsetDateTime
String dateTimeStrParam = "2021-05-05T18:00-04:00";
OffsetDateTime offsetDateTime = OffsetDateTime.parse(dateTimeStrParam);
System.out.println(" After analysis with offset :" + offsetDateTime); // With time zone Use ZonedDateTime
dateTimeStrParam = "2021-05-05T18:00-05:00[America/New_York]";
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTimeStrParam);
System.out.println(" After parsing with time zone :" + zonedDateTime);
} Output :
After analysis with offset :2021-05-05T18:00-04:00
After parsing with time zone :2021-05-05T18:00-04:00[America/New_York]

Please note that After parsing with time zone This result : The string parameter offset is explicitly -05, For wool to ZonedDateTime The post offset becomes -04 Well ???

Here's one I built on purpose case To get your attention , I'll explain the result as follows :

Pictured , stay 2021.03.14 - 2021.11.07 period , What's the offset for New York -4, The rest of the time -5. The date of this example is 2021-05-05 In daylight saving time , So the offset is -4, That's why what you show is written -5 In the end, it did -4.

JSR 310 format

in the light of JSR 310 Format date time type / analysis , There is a special class java.time.format.DateTimeFormatter Used for processing .

DateTimeFormatter It is also an immutable class , So it's thread safe , Than SimpleDateFormat It's much more reliable . In addition, it also has a lot of built-in formatting templates example For use , Form like :

formatter Example
ofLocalizedDate(dateStyle) '2021-01-03'
ofLocalizedTime(timeStyle) '10:15:30'
ofLocalizedDateTime(dateTimeStyle) '3 Jun 2021 11:05:30'
ISO_LOCAL_DATE '2021-12-03'
ISO_LOCAL_TIME '10:15:30'
ISO_LOCAL_DATE_TIME '2021-12-03T10:15:30'
ISO_OFFSET_DATE_TIME '2021-12-03T10:15:30+01:00'
ISO_ZONED_DATE_TIME '2021-12-03T10:15:30+01:00[Europe/Paris]'
@Test
public void test13() {
System.out.println(DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.now()));
System.out.println(DateTimeFormatter.ISO_LOCAL_TIME.format(LocalTime.now()));
System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now()));
} Output :
2021-01-17
22:43:21.398
2021-01-17T22:43:21.4

If you want to customize the mode pattern, and Date It can also specify any pattern date / Time pattern . Since this paper Date The date section details the date / Time pattern , What do the letters mean and how to use them , I won't repeat it here .

although DateTimeFormatter Supported mode ratio Date Slightly increased , But it's roughly the same , I don't think we need to spend any more energy on this . If you really need to check the official website is not too late

@Test
public void test14() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(" The first Q quarter yyyy-MM-dd HH:mm:ss", Locale.US); // Format output
System.out.println(formatter.format(LocalDateTime.now())); // analysis
String dateTimeStrParam = " The first 1 quarter 2021-01-17 22:51:32";
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStrParam, formatter);
System.out.println(" Results after analysis :" + localDateTime);
}

Q/q: quarter , Such as 3; 03; Q3; 3rd quarter.

Best practices

  • Abandoning Date, hug JSR 310

Every time I talk about it JSR 310 date / I'll call in time , Keep the routine, I'll keep on saying : give up Date Even disabled Date, Use JSR 310 date / Time , It's the best practice of date time processing .

in addition , About setting time zone during use ( Default time zone ) There is still a set of best practices in my mind , Here to share with you :

  • Always explicitly specify the time zone you need , Even if you want to get the default time zone
// Mode one : Common practice 
LocalDateTime.now(); // Mode two : Best practices
LocalDateTime.now(ZoneId.systemDefault());

The code is as like as two peas, and the two are the same. . But the second way is best practice .

The reason is that : Doing so can make the code with Clear intent , Eliminate the possibility of ambiguity , Even if you get the default time zone . Take way one , It is where the intention is not clear : In the end, the coder forgot to specify the time zone , Or do you want to use the default time zone ? The answer can't be determined without reading the context , This leads to unnecessary communication and maintenance costs . So even if you want to get the default time zone , Please also use the ZoneId.systemDefault() Write it up .

  • Use JVM Be careful with your default time zone , It is recommended that the time zone and the current session remain bound

This best practice can be used in special situations . The reason for this is :JVM The default time zone of the TimeZone#setDefault() It can be set globally , therefore JVM Any thread of can change the default time zone at will . If the code about time processing Very sensitive to time zones Words , The best practice is that you bind the time zone information to the current session , So that you don't have to be potentially affected by other threads , Robustness is ensured .

explain : The session may only be the current request , It could be one Session, Specifically case make a concrete analysis

summary

adopt Last article Foreshadowing the concept of date and time , With the demonstration of practical operation code in this paper , Get to the bottom of it Java There is no problem with date and time .

The content of the two articles is more , The amount of information is relatively large , It takes time to digest . On the one hand, I suggest you search and keep them as reference books , On the other hand, more practice is suggested , Only by writing more code can we have a deeper understanding .

Later on Reuse 3 -4 An article For the details of the previous two articles 、 Use scenarios to supplement , For example, how to match ZoneId and Offset Correspondence of ,LocalDateTime、OffsetDateTime、ZonedDateTime Cross time zone transfer problem 、 stay Spring MVC Best practices used in scenarios, etc , Stay tuned , Progress together .

In this paper, I think about the problem

I don't necessarily understand after reading it , It doesn't have to be understood . Come on , At the end of the article 3 This is a question to help you recover :

  1. Date How do types handle daylight saving time ?
  2. ZoneId and ZoneOffset What's the difference? ?
  3. Usually, if the project encounters the processing of date and time , What are the best practices ?

Recommended reading

GMT UTC CST ISO Daylight saving time Time stamp , What kind of ghosts are they ?

Pay attention to me

Share 、 grow up , Refuse to hide and stop . Focus on 【BAT The Utopia of 】 Reply key special column Yes Spring Technology stack 、 Middleware and other small and beautiful pure original Columns . This article has been https://www.yourbatman.cn Included .

This article belongs to the column :JDK Date time , You can get all the content by replying to the column name in the background .

A Brother (YourBatman):Spring Framework/Boot Open source contributor ,Java Architects . Very focused Basic skills training , Believe that the superstructure depends on the foundation , Only a solid foundation can make programmers more energetic . The article is characterized by reconstructing the knowledge system in the form of small and beautiful columns , The whole , Committed to do everyone can understand the best column series . You can add my friends (fsx1056342982) Let's share !

The most complete network ! Get to the bottom of it Java Handle GMT/UTC More articles about date and time

  1. Wu Yuxiong -- It's natural JAVA Developing learning : Date time

    import java.util.Date; public class DateDemo { public static void main(String args[]) { // initialization Date ...

  2. java data structure ( 3、 ... and ):java Common classes 3、 ... and Date time API

    JDK 8 Before date time API 1. Get the current time of the system :System Class currentTimeMillis()long time = System.currentTimeMillis();// Return to current time ...

  3. java Use simpleDateFormat Format date Time

    Time date identifier : yyyy: year MM: month dd: Japan hh:1~12 hourly (1-12) HH:24 hourly (0-23) mm: branch ss: second S: millisecond E: What day D: The day of the year F: The week of the month ...

  4. java Use simpleDateFormat Format date Time .RP

    Let's first look at all the representations of the formatted log . Time date identifier : yyyy: year MM: month dd: Japan hh:1~12 hourly (1-12) HH:24 hourly (0-23) mm: branch ss: second S: millisecond E: What day D: One ...

  5. 【 turn 】JAVA 8 date / Time (Date Time)API guide

    Preface I wanted to write it down Java 8 Date / Time API, I found a good article , Then directly reprint it ~ PS: The main content has not changed , Some changes have been made . Link to the original text : journaldev translate : ImportNew.com - ...

  6. paip. Date time operation and time stamp uapi php java python summary

    paip. Date time operation and time stamp uapi php java python summary ///uapi Date function | Day function | Hour function | Minute function | Month function | ...

  7. Java Magic Hall :Date Date and time formatting

    One . Preface                                                                                       Acquisition of date and time . display ...

  8. Java 8 Date time API

    Java 8 An important new feature is the introduction of new time and date API, They are included in the java.time In bag . With new time and date API Time and date can be handled in a more concise way ; Before introducing the content of this article , Let's talk about Jav ...

  9. stay Java 8 Get date from

    Preface The previous article wrote <SimpleDateFormat How to use it safely ?>, It introduces SimpleDateFormat How to deal with dates / Time , And how to ensure thread safety , And its introduction in Jav ...

  10. 10.3(Java Learning notes )JDBC Time operation

    One . Time classification database      java class Date  ---- java.sql.Date    Indicates the date yyyy-MM--dd ( Specific date ) Time  ----java.sql.Time    Express ...

Random recommendation

  1. mysql Update the password

    mysql -u root mysql> use mysql; mysql> UPDATE user SET Password = PASSWORD('newpass') WHERE us ...

  2. For class network -Java Season one -6-10 Exercises

    source :http://www.imooc.com/ceping/1596 The correct definition and access to two-dimensional arrays is () A int[ ][ ] num = new int[ ][ ]; B int[ ][ ...

  3. Oracle Flashback Technologies - Flashback by drop Table of

    Oracle Flashback Technologies - Flashback by drop Table of stay oracle10g in ,drop After a table , The table was not really deleted , Support by rename Put it after recyclebin in . # newly build ...

  4. For class network - Android engineers are just beginning to develop -3-3 Java Assignment operators in

    source :http://www.imooc.com/code/1298 Assignment operators are symbols that specify values for variables or constants . If available  “=” Assign the result of the expression on the right to the operands on the left . Java Supported common assignment operators , ...

  5. android Test and analysis 1

    Android The test framework , Part of integration in the development environment , Provide an architecture and powerful tools It can help test every aspect of your application from the unit to the framework . The testing framework has these main characteristics : 1.Android Test component based on Junit. You can use ...

  6. Three C#.net Method of generating static page

    ASP.NET There are three main methods to generate static pages   The first method : Send requests to the server's dynamic pages , Get the html Code . The disadvantages of this method are obvious : Slow speed . In addition, if the requested dynamic page has a validation control , Back to html There is no page ...

  7. This program cannot be started , Because it's lost in the computer QtCore4.dll. Try reinstalling the program to fix this problem ( Add to the system 3 A path )

    resolvent : Computer - attribute - Advanced system setup - senior - environment variable - System variables -Path add to E:\Qt\4.8.5\bin; E:\Qt\4.8.5\qmake; E:\mingw\bin Restart the computer http ...

  8. Team work 4-- The first project sprint (Alpha edition ) Preparatory work

    Group notes Our group started to study, discuss and program the project from Monday , Because we see that the deadline is Sunday , It only started on Monday , It's a late start , It's a mistake in our understanding , We didn't coordinate our pace on Monday and Tuesday , Instability of project progress , But we didn't do it last weekend ...

  9. A wonderful topic bzoj4300

    Description Given a length of n Sequence of numbers ai, seek ai The subsequence bi The longest length of , Satisfy bi&bi-1!=0(2<=i<=len). Input Input file total 2 That's ok . The first line contains an integer ...

  10. iview-admin Framework operation steps

    First step : Go to github Download the whole iview-admin Framework of the full source code github Address : https://github.com/iview/iview-admin The second step : Click on Clone or d ...