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

  1. The current time
  2. Gets the first day of the week. Default is Monday
  3. 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

  1. Calendar (Date) is converted to a Calendar object
  2. BeginOfSecond (Calendar Calendar) gets the start time of the second level, that is, the millisecond part is ignored
  3. 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