☕ [Java deep series] "technical blind spot" let's fully understand the API guide related to time and date

Technical introduction

Date processing in java has always been a problem. There is no good way to handle it, so there is a third-party framework, such as joda. This article mainly explains the java date processing in detail. Joda can be used in 1.8.

Time concept

First, let's introduce some basic concepts, in which GMT and UTC can be expressed as the same time size.

UT time

UT reflects the average speed of the earth's rotation. It is measured by observing stars.


UTC uses the atomic clock time as a reference, but maintains the time with UT1 in 0.9 seconds, that is, timing adjustment.

The current time standard is Universal Time Coordinated (UTC). If the computer is not connected to the network, it is inaccurate even if it is accurate, because UTC will adjust, and the general travel time is also inaccurate.


Now the network time protocol NTP (Network Time Protocol) commonly used by computers is a standard internet protocol for time synchronization in the Internet. The purpose is to synchronize the computer's time to some time standards.


GMT is the time that fully conforms to the earth's rotation, also known as UT1. Greenwich mean time is used as British civil time, or UTC. GMT is called "UT1", which directly corresponds to the rotation of the earth and is affected by the slight irregularity of the rotation. It is the difference between UT1 and UTC that keeps > below 0.9 seconds by applying leap seconds.

ISO 8601

An international format for time exchange. When some interface calls represent UTC/GMT time, they are displayed in "yyyy MM DD't'hh: mm: ss'z '" format. With millisecond format "yyyy MM DD 't'hh: mm: SS. SSS' Z'".

The implementation in joda is as follows
// Alternate ISO 8601 format without fractional seconds
private static final String ALTERNATIVE_ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  private static DateFormat getAlternativeIso8601DateFormat() {
        SimpleDateFormat df = new SimpleDateFormat(ALTERNATIVE_ISO8601_DATE_FORMAT, Locale.US);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df;

RFC 822


ARPA network is actually the predecessor of the Internet.

In some places, the time format in RFC 822 will be used. The format is as follows

 date-time = [ day "," ] date time ; dd mm yy
                                                     ; hh:mm:ss zzz
The second is equivalent to the current format
"EEE, dd MMM yyyy HH:mm:ss z"

Some header settings use this format.

The implementation in joda is as follows

// RFC 822 Date Format
private static final String RFC822_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
private static DateFormat getRfc822DateFormat() {
        SimpleDateFormat rfc822DateFormat =
                new SimpleDateFormat(RFC822_DATE_FORMAT, Locale.US);
        rfc822DateFormat.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return rfc822DateFormat;

Create Locale.US of SimpleDateFormat to determine which language is used to replace some characters of the format string, such as EEE.

SimpleDateFormat df1=new SimpleDateFormat("GGGG yyyy/MMMM/dd HH:mm:ss EEE aaa zzzz",Locale.CHINA);
SimpleDateFormat df2=new SimpleDateFormat("GGGG yyyy/MMMM/dd HH:mm:ss EEE aaa zzzz",Locale.US);
//Ad 2016 / March / 27 23:32:10 Sunday afternoon China standard time
//AD 2016/March/27 23:32:10 Sun PM China Standard Time

gregorian Calendar, julian Calendar: These are two calendars. We generally use the general gregorian Calendar.

Before jdk1.8

The main classes are Date for recording time stamps, Calendar for converting time and Date, and DateFormat for formatting and parsing time strings


Pay attention to the rules of time representation before use.

In addition, many expiration methods of this class are not recommended, and many have been replaced by Calendar.

Construction method

This class represents the millisecond value at a certain time. Since it is a millisecond value, it needs a reference value.

In all class date methods that accept or return year, month, date, hour, minute, and second values, use the following representation:

The year y is represented by the integer y-1900. A month is represented by an integer from 0 to 11; 0 is January, 1 is February, and so on; Therefore, November is December. The date (a day of the month) is usually represented by an integer between 1 and 31. Hours are represented by an integer between 0 and 23. Therefore, the time from midnight to 1 a.m. is 0 hours, and the time from noon to 1 p.m. is 12 hours. A minute is usually represented by an integer between 0 and 59. The second is represented by an integer between 0 and 61; The values 60 and 61 only appear within leap seconds, even in Java implementations that actually track leap seconds correctly. Due to the current introduction of leap seconds, the possibility of two leap seconds in the same minute is very low, but this specification follows the date and time convention of ISO C.

When we create a Date, which millisecond value is obtained?

public Date() {
 public Date(long date) {
      fastTime = date;

System.currentTimeMillis() is a local method, the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.

This may be inaccurate due to the time of the operating system. Some operating systems are not necessarily expressed in milliseconds. This time is in UTC and is not related to the time zone. This irrelevant means that the values obtained in each time zone at the same time should be consistent. You can simply verify the obtained time expression with a program.

long time = System.currentTimeMillis();
System.out.println("Seconds:"+ time%60);

It can be understood as the difference from UTC's zero on January 1, 1970. fastTime is the variable that the Date class holds this time.

Member variable

The Date object prints out the local time, while the construction method does not reflect the time zone. So where does it reflect the time zone?

Here are the member variables of Date


The following objects are obtained. There are no custom fields. It can be said that it is just a subclass of gregorian time factory to obtain CalendarDate.


Used in the following methods

private static final BaseCalendar getCalendarSystem(BaseCalendar.Date cdate) {
        if (jcal == null) {
            return gcal;
        if (cdate.getEra() != null) {
            return jcal;
        return gcal;
    synchronized private static final BaseCalendar getJulianCalendar() {
        if (jcal == null) {
            jcal = (BaseCalendar) CalendarSystem.forName("julian");
        return jcal;

The Julian calendar is used when the timestamp is used in the following cases, and the Julian calendar will be automatically set when it is used, so this parameter is not available in clone. So this can be ignored.

 private static final BaseCalendar getCalendarSystem(int year) {
        if (year >= 1582) {
            return gcal;
        return getJulianCalendar();
    private static final BaseCalendar getCalendarSystem(long utc) {
        // Quickly check if the time stamp given by `utc' is the Epoch
        // or later. If it's before 1970, we convert the cutover to
        // local time to compare.
        if (utc >= 0
            || utc >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER
                        - TimeZone.getDefaultRef().getOffset(utc)) {
            return gcal;
        return getJulianCalendar();


A timestamp is saved to represent the time. The most important parameter. Creating Date is the assignment of this value.


Save time related content, including time zone, language, etc

    public static final int FIELD_UNDEFINED = -2147483648;
    public static final long TIME_UNDEFINED = -9223372036854775808L;
    private Era era;
    private int year;
    private int month;
    private int dayOfMonth;
    private int dayOfWeek;
    private boolean leapYear;
    private int hours;
    private int minutes;
    private int seconds;
    private int millis;
    private long fraction;
    private boolean normalized;
    private TimeZone zoneinfo;
    private int zoneOffset;
    private int daylightSaving;
    private boolean forceStandardTime;
    private Locale locale;

This value can be ignored and is used in the expiration method.

    public static long parse(String s) {
   ... ...
            // Parse 2-digit years within the correct default century.
            if (year < 100) {
                synchronized (Date.class) {
                    if (defaultCenturyStart == 0) {
                        defaultCenturyStart = gcal.getCalendarDate().getYear() - 80;
                year += (defaultCenturyStart / 100) * 100;
                if (year < defaultCenturyStart) year += 100;
            ... ...


UID to verify version consistency


Save the value used for toString formatting


Save the value used for toString formatting

Main methods


It is mainly the time field of the millisecond value saved in it. The following is our common method, using the default time zone and regional language:

public static Calendar getInstance() {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));

Gregorian calendar is the default for domestic environment, but some pits such as BuddhistCalendar are used for TH-th:

set(int,int,int,int,int,int) method

Method cannot set the MILLISECOND value, so when getInstance is used, the same value is set, and the last MILLISECOND value is inconsistent. Therefore, if necessary, clear MILLISECOND.


The set method does not calculate the time immediately, which means that the corresponding member variables are modified and can only be adjusted when get(), getTime(), getTimeInMillis(), add() or roll()

        Calendar cal1 = Calendar.getInstance();
        cal1.set(2000, 7, 31, 0, 0 , 0);
        //It should be 2000-9-31, that is, 2000-10-1
        cal1.set(Calendar.MONTH, Calendar.SEPTEMBER);
        //If Calendar is converted to 2000-10-1, the current result should be 2000-10-30
        cal1.set(Calendar.DAY_OF_MONTH, 30);
        //The output is 2000-9-30, indicating that the Calendar does not refresh its internal records immediately

In other words, if there is a time to be adjusted during multiple settings, it will not be adjusted. Therefore, try not to make other adjustments after the setting that cannot be determined, so as to prevent the final actual value from being inaccurate with the normal value.

The add method will change the time immediately

Roll is similar to add, but roll does not modify the value of larger fields.


Create and set the pattern string, which can be expressed in the following format:

The date format is out of sync. It is recommended to create separate format instances for each thread. If multiple threads access a format at the same time, it must be externally synchronized.

SimpleDateFormat is a thread unsafe class. Its parent class maintains a Calendar. Calling relevant methods may modify the Calendar. Generally, do not define it as a static variable. If it is defined as static, it must be locked, or use the DateUtils tool class. Positive example: pay attention to thread safety and use DateUtils. org.apache.commons.lang.time.DateUtils. The following processing is also recommended:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { 
        protected DateFormat initialValue() { 
                return new SimpleDateFormat("yyyy-MM-dd"); 


These classes all inherit java.util.Date.

It is equivalent to representing java.util.Date separately. Date refers to information such as year, month, day, etc. Time indicates information such as hours, minutes and seconds. Timestamp maintains nanoseconds, which can represent nanoseconds.

For JDK8 applications, you can use Instant instead of Date, LocalDateTime instead of Calendar, and DateTimeFormatter instead of SimpleDateFormat. The official explanation is: simple beautiful strong immutable thread safe.

Time class of jdk1.8

1.8 added a new date time package, following JSR310. The core code is mainly placed under the Java. Time package. The default calendar system uses ISO-8601 (based on the Gregorian calendar). The main contents under java.time include:

java.time - mainly includes date, time, date, time, time, period, and clock related classes.

  • Java.time.chrono - other non-ISO standard calendar systems can use java.time.chrono, which has defined some chronologies, and you can also customize them.
  • java.time.format - a class that formats and parses dates and times
  • Java.time.temporary - extension API, which is mainly provided to people who write frameworks and libraries, and allows date and time to interoperate, access, and adjust. Fields and units are defined under this package.
  • java.time.zone - defines the time zone, offset relative to the time zone, time zone rules, etc.

The API of the package provides a large number of related methods, which generally have consistent method prefixes:

  • of: static factory method.
  • parse: static factory method, focusing on parsing.
  • Get: get the value of something.
  • Is: check whether something is true.
  • with: immutable setter equivalent.
  • plus: add some amount to an object.
  • minus: subtract some amount from an object.
  • To: convert to another type.
  • at: combine this object with another object, for example: date.atTime(time).

Mutual transformation and Instant

You can see that the old time and date classes have instant conversion. Instant can be said to be a transit station for the transformation between old and new. Instant mainly maintains the second and nanosecond fields, which can represent the nanosecond range. Of course, if it is not supported, an exception will be thrown. It is mainly to convert java.util.Date into a new time class.


It provides a method to access the current time, and you can also get the current Instant. Clock holds the time zone or time zone offset. If you only get the current timestamp, it is recommended to use System.currentTimeMillis()


zone id mainly includes two aspects: one is that the fixed offset relative to UTC/Greenwich is equivalent to a large time zone, and the other is the region with special offset relative to UTC/Greenwich in the time zone. Generally, the fixed offset part can be represented by ZoneOffset, and normalized() can be used to determine whether it can be represented by ZoneOffset. The judgment mainly uses the time zone rule ZoneRules. The real rule of time zone is defined in ZoneRules, which defines when and how many offsets. This method is used because the ID is fixed, but the rules are defined by the government and change frequently.


LocalDateTIme/LocalTime/LocalDate have no concept of time zone. This sentence does not mean that time cannot be obtained according to the time zone, but because these classes do not hold variables representing the time zone. ZoneDateTime holds time zone and offset variables.

These classes can modify the time. In fact, they all generate new objects. Therefore, the time classes here naturally support multithreading.

In these time classes, you can obtain time objects, modify time, obtain new time objects, format time, etc.


The atZone of LocaDateTime is to adjust the time zone of local time. It doesn't change the time. To use the LocalDateTime.now that needs to be obtained at other times, you need to pass in the time zone variable.


The time object needs to be formatted, and the DateTimeFormatter needs to be used to format and parse the date and time.

Expansion and thinking

When formatting with SimpleDateFormat, do not use the 12 hour system, that is hh, because it is easy to cause no minute in the morning and afternoon. For example, "2017-01-01 00:00:00" may become "2017-01-01 12:00:00":: symbol

Method of LocalDateTime
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
    Objects.requireNonNull(formatter, "formatter");
    return formatter.parse(text, LocalDateTime::from);
The method called by parse is
public <T> T parse(CharSequence text, TemporalQuery<T> query) {
   ... ...
The method called by LocalDateTime::from is
public static LocalDateTime from(TemporalAccessor temporal) {
    .... ...     

Where temporary is the interface of LocalDateTime

In fact, everyone has a question about what LocalDateTime::from means.

//Same as the following representation
x ->  LocalDateTime.from(x)
//amount to
new TemporalQuery<LocalDateTime>(){
        public LocalDateTime queryFrom(TemporalAccessor temporal) {
             return LocalDateTime.from(temporal);

Posted on Mon, 29 Nov 2021 00:24:57 -0500 by erikhillis