This is the sixth day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021.”
❤️ Author profile: Java quality creator 🏆, CSDN blog expert certification 🏆, Huawei Cloud enjoy expert certification 🏆
❤️ technology live, the appreciation
❤️ like 👍 collect ⭐ look again, form a habit
Before reading this article, it is recommended to have some knowledge of the date and time of Java source code. If not, you can read this article first:
Swastika blog teaches you to understand Java source code date and time related usage
Related articles:
Hutool actual combat (take you to master the various tools) directory
1hutool: DateUtil(time tool class)- Current time
Source code analysis purposes
Know what it is and why
Project reference
The basis of this blog: Hutool -5.6.5 version of the source code
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.6.5</version>
</dependency>
Copy the code
Method name: dateutil.date ()
Methods described
The current time is converted to the {@link DateTime} object
Source code Analysis 1
New DateTime object, this object is defined by Hutool time object,DateTime object inherits Date object
/** * current time, convert to {@linkDateTime} object * *@returnCurrent time */
public static DateTime date(a) {
return newDateTime(); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --public class DateTime extends Date {... }Copy the code
DateTime object DateTime object DateTime object
- The current time
- Gets the first day of the week. Default is Monday
- Contains the time zone
// The current time
DateTime date = DateUtil.date();
System.out.println(date.toString());
----------------------------
public String toString(a) {
return toString(this.timeZone); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * Converted to "YYYY-MM-DD HH: MM: SS" format string <br> * If the time zone is not {@codeNull}, will be converted to the time of its time zone, otherwise converted to the current time zone * *@param* timeZone time zone@return"Yyyy-mm-dd HH: MM: SS" is a string in the format of *@since4.1.14 * /
public String toString(TimeZone timeZone) {
if (null! = timeZone) {return toString(DateUtil.newSimpleFormat(DatePattern.NORM_DATETIME_PATTERN, null, timeZone));
}
return toString(DatePattern.NORM_DATETIME_FORMAT);
}
Copy the code
DateTime overrides the toString() method to format the time, returning the format string “YYYY-MM-DD HH: MM: SS”
**DateTime(long timeMillis, TimeZone TimeZone); **DateTime(long Date, TimeZone TimeZone);
The traditional way of writing it is one
Get the current time
// Get the current time
Date date = new Date();
System.out.println(date.toString());
// Format the time
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("Current time is:" + ft.format(date));
Copy the code
contrast
Hutool is written more succinctly than the traditional method, and defines a DateTime object, which is the basis of Hutool’s DateUtil (time utility class).
Source code Analysis II
/** * The current time */
public DateTime(a) {
this(TimeZone.getDefault());
}
Copy the code
** timezone.getdefault ()** Java.util; Method returns the default time zone for this host on which the program is running
Is it risky to use the default time zone?
So what’s the default time zone
1, Java. Util. TimeZone getDefault method source code shown in this class, it will eventually be called sun. Util. Calendar. ZoneInfo getTimeZone method of a class. This method returns a String parameter as ID for the desired time area.
Timezone: if the zoneID cannot be read, read the default timezone of the system
zoneID = getSystemTimeZoneID(javaHome);
/** * Gets the platform defined TimeZone ID. **/
private static native String getSystemTimeZoneID(String javaHome);
Copy the code
If you see this native, you’ve got the core. At this point, it’s still not clear how to get the default time zone for your system.
Download the OpenJDK from gitee.com/mirrors/ope…
GMT_ID = “GMT_ID” GMT_ID = “GMT”
Risk avoidance best practices
The timezone is set in the user.timezone variable in the JVM
What is a native
Native is a computer function, and a native Method is a Java interface that calls non-Java code. Methods are implemented in non-Java languages, such as C or C++.
What about native’s source code
** Private Static Native String getSystemTimeZoneID(String javaHome)** as an example
The package java.util.TimeZone where the getSystemTimeZoneID method resides;Copy the code
Find the getSystemTimeZoneID method under timezone.c, as shown in the figure
/* * Gets the platform defined TimeZone ID */
JNIEXPORT jstring JNICALL
Java_java_util_TimeZone_getSystemTimeZoneID(JNIEnv *env, jclass ign, jstring java_home, jstring country)
{
const char *cname;
const char *java_home_dir;
char *javaTZ;
if (java_home == NULL)
return NULL;
java_home_dir = JNU_GetStringPlatformChars(env, java_home, 0);
if (java_home_dir == NULL)
return NULL;
if(country ! =NULL) {
cname = JNU_GetStringPlatformChars(env, country, 0);
/* ignore error cases for cname */
} else {
cname = NULL;
}
/* * Invoke platform dependent mapping function */
javaTZ = findJavaTZ_md(java_home_dir, cname);
free((void *)java_home_dir);
if(cname ! =NULL) {
free((void *)cname);
}
if(javaTZ ! =NULL) {
jstring jstrJavaTZ = JNU_NewStringPlatform(env, javaTZ);
free((void *)javaTZ);
return jstrJavaTZ;
}
return NULL;
}
Copy the code
Important: Call different platform-specific mapping functions
/*
* Invoke platform dependent mapping function
*/
javaTZ = findJavaTZ_md(java_home_dir, cname);
Copy the code
The findJavaTZ_md method exists in both solaris and Windows directories.
Check the difference between the two directories:
Because the OpenJDK, the Java standard library and some of the tools source repo (JDK directory), BSD and Linux platform related source is in the Solaris directory. In the original Sun JDK source code, the platform-related directories start from solaris and Windows. Later, all Unix platform-related code is placed in solaris directory, sharing most of the code. Source: author: RednaxelaFX links: https://www.zhihu.com/question/58982441/answer/170264788 zhihuCopy the code
The simple understanding is:
On Windows, use the JDK code compiled in the Windows directory
On Unix platforms, JDK code compiled in solaris directories is used
Understand findJavaTZ_md method execution under different systems
Windows system
/* * Detects the platform time zone which maps to a Java time zone ID. */
char *findJavaTZ_md(const char *java_home_dir, const char *country)
{
char winZoneName[MAX_ZONE_CHAR];
char winMapID[MAX_MAPID_LENGTH];
char *std_timezone = NULL;
int result;
winMapID[0] = 0;
result = getWinTimeZone(winZoneName, winMapID);
if(result ! = VALUE_UNKNOWN) {if (result == VALUE_GMTOFFSET) {
std_timezone = _strdup(winZoneName);
} else{ std_timezone = matchJavaTZ(java_home_dir, result, winZoneName, winMapID, country); }}return std_timezone;
}
Copy the code
The comment is clearly written to get the current Time zone in the “Time Zones” registry
/* * Gets the current time zone entry in the "Time Zones" registry. */
static int getWinTimeZone(char *winZoneName, char *winMapID)
{... }Copy the code
Setting the time zone:
Where is the value of the select value on the range taken from, as stated above, is in the registry
Open the registry: Regedit–>
Computer \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Copy the code
The Unix platform
The findJavaTz_md() method comments make it clear: map the platform time zone ID to the Java time zone ID
/* * findJavaTZ_md() maps platform time zone ID to Java time zone ID * using
/lib/tzmappings. If the TZ value is not found, it * trys some libc implementation dependent mappings. If it still * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm * form. `country', which can be null, is not used for UNIX platforms. */
/*ARGSUSED1*/
char *
findJavaTZ_md(const char *java_home_dir, const char *country)
{
char *tz;
char *javatz = NULL;
char *freetz = NULL;
tz = getenv("TZ");
#ifdef __linux__
if (tz == NULL) {
#else
#ifdef __solaris__
if (tz == NULL || *tz == '\ 0') {
#endif
#endif
tz = getPlatformTimeZoneID();
freetz = tz;
}
/* * Remove any preceding ':' */
if(tz ! =NULL && *tz == ':') {
tz++;
}
#ifdef __solaris__
if (strcmp(tz, "localtime") = =0) {
tz = getSolarisDefaultZoneID();
freetz = tz;
}
#endif
if(tz ! =NULL) {
#ifdef __linux__
/* * Ignore "posix/" prefix. */
if (strncmp(tz, "posix/".6) = =0) {
tz += 6;
}
#endif
javatz = strdup(tz);
if(freetz ! =NULL) {
free((void*) freetz); }}return javatz;
}
Copy the code
Steps:
< Java home>/lib/tzmappings. If the “TZ” variable is not found, proceed to step 2
Tz = getPlatformTimeZoneID(); Perform a Linux-specific mapping, returning a time zone ID if found, null otherwise
【Linux】Centos7 change the system timezone.
timedatectl
Copy the code
Modify the time zone
timedatectl set-timezone Asia/Shanghai
Copy the code
3, compare /etc/localtime with “/usr/share/zoneinfo “files, if the same, return the time zone ID, if not, go to step 4
4. Return to GMT
Method name: dateutil.datesecond ()
Methods described
The current time is converted to a {@link DateTime} object, ignoring the millisecond part
Source code Analysis 1
/** * current time, convert to {@linkDateTime} object, ignoring the millisecond part * *@returnCurrent time *@since4.6.2 * /
public static DateTime dateSecond(a) {
returnbeginOfSecond(date()); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -public static DateTime beginOfSecond(Date date) {
return newDateTime(beginOfSecond(calendar(date))); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --public static Calendar beginOfSecond(Calendar calendar) {
return truncate(calendar, DateField.SECOND);
}
Copy the code
When we get to this point, we actually go through three method calls
- Calendar (Date) is converted to a Calendar object
- BeginOfSecond (Calendar Calendar) gets the start time of the second level, that is, the millisecond part is ignored
- DateTime(Calendar Calendar) The Calendar object is converted to a DateTime object
/ / CalendarUtil class
/** * gets the start time of the second level, that is, ignoring the millisecond part **@paramThe calendar date {@link Calendar}
* @return {@link Calendar}
* @since4.6.2 * /
public static Calendar beginOfSecond(Calendar calendar) {
returntruncate(calendar, DateField.SECOND); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * Change the date to the start time of a time field **@param calendar {@link Calendar}
* @paramDateField Time field *@returnThe original {@link Calendar}
*/
public static Calendar truncate(Calendar calendar, DateField dateField) {
returnDateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.TRUNCATE); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/ / DateModifier class
/** * Change the date **@param calendar {@link Calendar}
* @paramDateField indicates the dateField to which it is reserved *@paramModifyType Change type, including round off, round off, forward *@returnModified {@link Calendar}
*/
public static Calendar modify(Calendar calendar, int dateField, ModifyType modifyType) {
// AM_PM special processing
if (Calendar.AM_PM == dateField) {
boolean isAM = DateUtil.isAM(calendar);
switch (modifyType) {
case TRUNCATE:
calendar.set(Calendar.HOUR_OF_DAY, isAM ? 0 : 12);
break;
case CEILING:
calendar.set(Calendar.HOUR_OF_DAY, isAM ? 11 : 23);
break;
case ROUND:
int min = isAM ? 0 : 12;
int max = isAM ? 11 : 23;
int href = (max - min) / 2 + 1;
int value = calendar.get(Calendar.HOUR_OF_DAY);
calendar.set(Calendar.HOUR_OF_DAY, (value < href) ? min : max);
break;
}
// Process the next level field
return modify(calendar, dateField + 1, modifyType);
}
// Loop through each level of fields, down to the millisecond field
for (int i = dateField + 1; i <= Calendar.MILLISECOND; i++) {
if (ArrayUtil.contains(IGNORE_FIELDS, i)) {
// Ignore irrelevant fields (WEEK_OF_MONTH)
continue;
}
// The month related fields are ignored when calculating the start and end days of the week.
if (Calendar.WEEK_OF_MONTH == dateField || Calendar.WEEK_OF_YEAR == dateField) {
if (Calendar.DAY_OF_MONTH == i) {
continue; }}else {
// Ignore the week related fields in other cases
if (Calendar.DAY_OF_WEEK == i) {
continue;
}
}
modifyField(calendar, i, modifyType);
}
return calendar;
}
Copy the code
Loop through all levels of fields:
1. // Ignore irrelevant fields (WEEK_OF_MONTH) and never modify them
/** Ignored computed fields */
private static final int[] IGNORE_FIELDS = new int[] { //
Calendar.HOUR_OF_DAY, // The same name as HOUR
Calendar.AM_PM, // This field is handled separately and does not participate in the start and end of the calculation
Calendar.DAY_OF_WEEK_IN_MONTH, // Do not participate in the calculation
Calendar.DAY_OF_YEAR, / / DAY_OF_MONTH embodiment
Calendar.WEEK_OF_MONTH, // Special processing
Calendar.WEEK_OF_YEAR / / WEEK_OF_MONTH embodiment
};
Copy the code
2, // The month fields are ignored when calculating the start and end days of the week.
3, modifyField(Calendar, I, modifyType); I = 14 (meaning Calendar. MILLISECOND)
Calendar. set(field, dateutil. getBeginValue(calendar, field));
/** * get the minimum value of the specified date field, For example, the minimum number of minutes is 0 * * @param calendar {@link calendar} * @param dateField {@link dateField} * @return field minimum * @see Calendar#getActualMinimum(int) * @since 4.5.7 */ public static int getBeginValue(calendarcalendar) int dateField) { if (Calendar.DAY_OF_WEEK == dateField) { return calendar.getFirstDayOfWeek(); } return calendar.getActualMinimum(dateField); }Copy the code
So we get what we want, ignore the milliseconds
calendar.set(field, DateUtil.getBeginValue(calendar, field)); -->calendar.set(field,0); / / field = 14 (meaning Calendar. MILLISECOND)Copy the code
Method name: dateutil.now ()
Methods described
The current time is in the format YYYY-MM-DD HH: MM: SS
Source code Analysis 1
/** * The current time in the format YYYY-MM-DD HH: MM :ss **@returnThe standard form of the current time string */
public static String now(a) {
return formatDateTime(newDateTime()); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * Format date time <br> * Format YYYY-MM-DD HH: MM :ss **@paramDate Formatted date *@returnFormatted date */
public static String formatDateTime(Date date) {
if (null == date) {
return null;
}
return DatePattern.NORM_DATETIME_FORMAT.format(date);
}
Copy the code
//FastDateFormat is a thread-safe implementation.
public class FastDateFormat extends Format implements DateParser.DatePrinter {... }Copy the code
Why is FastDateFormat a thread-safe implementation? Let’s dig a little deeper
/** * Standard date-time format, accurate to seconds {@linkFastDateFormat} : YYYY-MM-DD HH: MM :ss */
public static final FastDateFormat NORM_DATETIME_FORMAT = FastDateFormat.getInstance(NORM_DATETIME_PATTERN);
------------------------------------
public static FastDateFormat getInstance(final String pattern) {
return CACHE.getInstance(pattern, null.null);
}
Copy the code
When you see a CACHE, you’re excited
public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {... F format = cInstanceCache.get(key); . }Copy the code
CInstanceCache uses ConcurrentHashMap, which is known to be thread-safe.
Ok, now admit that FastDateFormat is a thread-safe implementation
ConcurrentMap<Tuple, F> cInstanceCache = new ConcurrentHashMap<>(7);
Copy the code
The key used here is Tuple, read the source code
/** * Immutable tuple type, used for multiple values to return <br> * Multiple values can support different element value types **@author Looly
*
*/
public class Tuple extends CloneSupport<Tuple> implements 可迭代<Object>, Serializable{
private static final long serialVersionUID = -7689304393482182157L;
private final Object[] members;
private int hashCode;
private boolean cacheHash;
/** * construct *@paramMembers Array */
public Tuple(Object... members) {
this.members = members; }... }Copy the code
The traditional way of writing it is one
Date = new Date(); System.out.println(date.toString()); SimpleDateFormat ft = new SimpleDateFormat (" YYYY-MM-DD hh: MM :ss"); System.out.println(" current time: "+ ft.format(date));Copy the code
contrast
Hutool is simplified and does not need to duplicate new DateFormat instance objects. FastDateFormat getInstance is cached.
Method name: dateutil.today ()
Methods described
The current date in the format YYYY-MM-DD
Source code Analysis 1
/** * The current date in the format YYYY-MM-DD **@returnThe standard form string */ for the current date
public static String today(a) {
return formatDate(newDateTime()); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * Format the date part (excluding the time) <br> * Format YYYY-MM-DD **@paramDate Formatted date *@returnThe formatted character string */
public static String formatDate(Date date) {
if (null == date) {
return null;
}
return DatePattern.NORM_DATE_FORMAT.format(date);
}
Copy the code
/ / DatePattern class
/** * Standard date format: YYYY-MM-DD */
public static final String NORM_DATE_PATTERN = "yyyy-MM-dd";
/** * Standard date format {@linkFastDateFormat} : yyyy - MM - dd * /
public static final FastDateFormat NORM_DATE_FORMAT = FastDateFormat.getInstance(NORM_DATE_PATTERN);
Copy the code
From the above DateUtil. Now the source analysis, the * * FastDateFormat getInstance (NORM_DATE_PATTERN) * * is a caching mechanism, please see the above DateUtil. Now, analysis of the source