JAVA8 time class library and JodaTime

Reading guide

This article will first briefly explain some problems in the date time API in the old version of JDK, then introduce joda time, an excellent date time class library, and finally introduce the new time class library in JAVA8. If you already know one of them, you can choose to read.

1, Time class library in historical JDK version

1. Defects and deficiencies of original time class library

I believe that most experienced people will feel unhappy when dealing with date time when using the class libraries before Java 8, including but not limited to the following slot points.    

stay Java 1.0 In the version, the operation of time and date completely depends on java.util.Date Class, which can only represent time in milliseconds, but cannot represent date.

There are big defects in ease of use: the starting choice of year is 1900, and the month starts from 0.

toString Method return value is not intuitive(With time zone, but represents Date Class supports time zones in any way)

In Java version 1.1, many methods in many Date classes are discarded, and java.util.Calendar is added. However, like date, the calendar class also has similar problems and design defects, which makes the code written with these classes as error prone.

The month is still calculated from 0

Daily date and time operations need to be used at the same time Date,Calendar,SimpleDateFormat,Quite cumbersome

Some properties exist only in one class (To parse and format a date or time DateFormat Method exists only in Date In class)

DateFormat Not thread safe if two threads try to use the same formatter Parsing the date may result in unexpected results.

Date and Calendar Are variable

2. Briefly describe the reasons why SimpleDateFormat threads are unsafe

Because the shared variable calendar used by the parse method is not thread safe. The calendar is assigned in the format (subFormat) method and the value is processed in parse. Therefore, in the case of concurrency, the calendar cleaning is not timely and the value is overwritten.

 /**
     * The {@link Calendar} instance used for calculating the date-time fields
     * and the instant of time. This field is used for both formatting and
     * parsing.
     *
     * <p>Subclasses should initialize this field to a {@link Calendar}
     * appropriate for the {@link Locale} associated with this
     * <code>DateFormat</code>.
     * @serial
     */
    protected Calendar calendar;

 

@Override
    public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        pos.beginIndex = pos.endIndex = 0;
        return format(date, toAppendTo, pos.getFieldDelegate());
    }

    // Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);
// At this point the fields of Calendar have been set.  Calendar
// will fill in default values for missing fields when the time
// is computed.

pos.index = start;

Date parsedDate;
try {
    parsedDate = calb.establish(calendar).getTime();
    // If the year value is ambiguous,
    // then the two-digit year == the default start year
    if (ambiguousYear[0]) {
        if (parsedDate.before(defaultCenturyStart)) {
            parsedDate = calb.addYear(100).establish(calendar).getTime();
        }
    }
}

3. How to solve the above thread unsafe problem?

use ThreadLocal Create a thread exclusive for each thread SimpleDateFormat variable
 Create local variables as needed
 use org.apache.commons.lang3.time.DateFormatUtils
 Use what will be mentioned below Joda-Time

2, About joda time

1. Introduction

Joda time is a high-quality date and time development library provided by joda that follows the JDK of Apache 2.0 open source protocol.

    Joda projects other than joda time include joda money, joda beans, joda convert and joda collect

(1) Why joda time

        Easy to use: Calendar is difficult to access "normal" dates and lacks simple methods. Joda time has simple field access, such as getYear() for the year and getDayOfWeek() for the day of the week.
        Easy to extend: JDK supports multiple calendar systems by using subclasses, but it is very cumbersome, and it is difficult to write another calendar system in the implementation. Joda time supports multiple pluggable calendar systems based on Chronology class.
        Comprehensive functions: joda time provides all the necessary functions for date and time calculation. It provides the feature of ready to use.
        Latest time zone calculation: the implementation of time zone is based on the public time zone information database, which is updated several times a year. The new version of joda time includes all changes to the database. Necessary updates should be made as soon as possible. It is easy to manually update the regional data.
        Calendar support: 8 calendar systems are provided.
        Interoperability: internally expressed in milliseconds, which is consistent with JDK or other public time representations.
        Good performance: support minimum calculation for the accessed domain.
        Good test coverage: there are all-round testers to ensure the quality of the library.
        Complete documentation: there is a complete user guide that provides an overview covering common usage scenarios. javadoc is very detailed, covering the rest of the API.
        Development: it has developed actively since 2002. It is a mature and reliable code base, and some related projects are also available at present.
        Open source: released according to Apache 2.0 open source protocol.

(2) Key benefits of joda time

        LocalDate: contains only dates
        LocalTime: contains only time
        Instant: point in time on the timeline
        DateTime: the full date and time in the time zone
        DateTimeZone: better time zone
        Duration and Period: duration
        Interval: the time between two time points
        Comprehensive and flexible time formatting and conversion

Because joda time has so many advantages compared with the time class library before Java 8, joda time has become the de facto standard Java date and time library.

2. Characteristic interpretation

(1) Interoperability between joda time and JDK

Interoperability means that the Joda class can generate an instance of java.util.Date (and Calendar), which allows us to retain the existing dependency on JDK and use Joda to handle complex date / time calculations.

Date To Joda-Time: 

Date date = new Date();
DateTime dateTime = new DateTime(date);

Calendar To Joda-Time: 

Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(calendar);

Joda-Time To Date: 
Date date = new Date();  
DateTime dateTime = new DateTime(date);                       
Date date2 = dateTime.toDate();

Joda-Time To Calendar: 
Calendar calendar = Calendar.getInstance();  
dateTime = new DateTime(calendar);  
Calendar calendar2 = dateTime.toCalendar(Locale.CHINA);

(2) Interpretation of joda's key date / time concept

Joda uses the following concepts so that they can be applied to any date / time library:
Immutability

Joda time is similar to java.lang.String. Their instances cannot be modified (because any operation to change their value will generate new objects), which also means that they are thread safe.
Instant aneous

As indicated in the interface org.joda.time.ReadableInstant, Instant represents an exact point in time, which is the number of milliseconds calculated from epoch: 1970-01-01T00:00:00Z. This design also makes its subclasses compatible with JDK Date and Calendar classes.

/**
 * Defines an instant in the datetime continuum.
 * This interface expresses the datetime as milliseconds from 1970-01-01T00:00:00Z.
 * <p>
 * The implementation of this interface may be mutable or immutable.
 * This interface only gives access to retrieve data, never to change it.
 * <p>
 * Methods in your application should be defined using <code>ReadableInstant</code>
 * as a parameter if the method only wants to read the instant without needing to know
 * the specific datetime fields.
 * <p>
 * The {@code compareTo} method is no longer defined in this class in version 2.0.
 * Instead, the definition is simply inherited from the {@code Comparable} interface.
 * This approach is necessary to preserve binary compatibility.
 * The definition of the comparison is ascending order by millisecond instant.
 * Implementors are recommended to extend {@code AbstractInstant} instead of this interface.
 *
 * @author Stephen Colebourne
 * @since 1.0
 */
public interface ReadableInstant extends Comparable<ReadableInstant> {

    /**
     * Get the value as the number of milliseconds since
     * the epoch, 1970-01-01T00:00:00Z.
     *
     * @return the value as milliseconds
     */
    long getMillis();
                                                                                      ······
}

Partial

Instantaneity represents an exact time in time relative to epoch, while a local time refers to a part of a time segment, which can change the time through some methods (in essence, a new class is generated), so it can be used in multiple places as a point in the repetition cycle.


Chronology

The design core of joda time is chronology (org.joda.time.Chronology). Fundamentally, chronology is a calendar system, a special way to calculate time, and a framework in which calendar algorithm is executed. The eight chronologies supported by joda time are as follows:

ISO(default) - org.joda.time.chrono.ISOChronology

​ GJ - org.joda.time.chrono.GJChronology

​ Gregorian - org.joda.time.chrono.GregorianChronology

​ Julian - org.joda.time.chrono.JulianChronology

​ Coptic - org.joda.time.chrono.CopticChronology

​ Buddhist - org.joda.time.chrono.BuddhistChronology

​ Ethiopic - org.joda.time.chrono.EthiopicChronology

​ Islamic - org.joda.time.chrono.IslamicChronology

Each of the above chronologies can be used as the computing engine of a specific calendar system and is a pluggable implementation.
Time zone

See the encyclopedia explanation for specific definitions. In the actual coding process, any strict time calculation must involve the time zone (or relative to GMT). The corresponding core class in joda time is org.joda.time.DateTimeZone. Although the operation of time zone is not involved in the daily use process, it is worth noting how DateTimeZone affects DateTime, It will not be repeated here.

3. Method of use

After introducing some concepts of joda time, let's demonstrate the method:

(1) Create joda time object

Transient ReadableInstant

// 1. System usage time
DateTime dateTime1 = new DateTime();
// 2. Use date in jdk
Date jdkDate1 = new Date();
DateTime dateTime2 = new DateTime(jdkDate1);
// 3. Specify in milliseconds
Date jdkDate2 = new Date();
long millis = jdkDate.getTime();
DateTime dateTime3 = new DateTime(millis);
// 4. Use Calendar
Calendar calendar = Calendar.getInstance();
DateTime dateTime4 = new DateTime(calendar);
// 5. Use multiple fields to specify an instant (local time segment)
// year  month day hour(midnight is zero) minute second milliseconds
DateTime dateTime5 = new DateTime(2000, 1, 1, 0, 0, 0, 0);
// 6. Generate another DateTime from one DateTime
DateTime dateTime6 = new DateTime(dateTime1);
// 7. Generate DateTime with time string
String timeString = "2019-01-01T00:00:00-06:00";
DateTime dateTime7 = DateTime.parse(timeString);

Locality - ReadablePartial

When the date and time processed in the program do not need to be a complete time, you can create a local time. For example, you only want to focus on the year / month / day, or the time of the day, or a day of the week. In joda time, the org.joda.time.ReadablePartial interface represents these times. Its two classes, LocalDate and LocalTime, are used to represent the year / month / day and a certain time of the day respectively.

// Each field contained is provided explicitly
LocalDate localDate = new LocalDate(2019, 1, 1);
// 6:30:06 PM
LocalTime localTime = new LocalTime(18, 30, 6, 0);

LocalDate replaces org.joda.time.YearMonthDay used in earlier versions of joda time, and LocalTime replaces org.joda.time.TimeOfDay used in earlier versions. (all have been marked as obsolete).
time span

Joda time provides three classes to represent time spans (which can be useful in some business requirements).

​ Duration

This class represents the absolute precision in milliseconds, provides the method of standard mathematical conversion, and converts the time span to standard units.

​ Period

This class is expressed in mm / DD / yy units.

​ Interval

This class represents a specific time span and uses a clear time to define the scope of this time span. Interval is a semi open interval, so the time span encapsulated by it includes the start time of this period, but does not include the end time.

(2) Processing time using joda time method

DateTime today = new DateTime();
// Gets the time before 777 seconds
DateTime dateTime1 = today.minus(777 * 1000);
// Get tomorrow's time
DateTime tomorrow = today.plusDays(1);
// Gets the date of the first day of the month
DateTime dateTime2 = today.withDayOfMonth(1);
// Gets the last day of the month three months after the current time
DateTime dateTime3 = today.plusMonths(3).dayOfMonth().withMaximumValue();

Some DateTime methods are listed below:
       

plus/minus Methods at the beginning (for example: plusDay, minusMonths): Used to return in DateTime Instances after adding or decreasing for a period of time

        plus(long duration) Increases the specified number of milliseconds and returns

        plusYears(int years) Add the specified year and return

        plusMonths(int months) Add the specified month and return

        plusWeeks(int weeks) Add specified week and return

        plusDays(int days) Increase the specified number of days and return

        plusHours(int hours) Increase the specified hour and return

        plusMinutes(int minutes) Increase the specified minutes and return

        plusSeconds(int seconds) Increases the specified number of seconds and returns

        plusMillis(int millis) Increase the specified milliseconds and return

    On the contrary minus Prefixed plus Is to increase minus Is to reduce

        with Method at the beginning: used to return DateTime Instance updates the instance after the specified date unit

        withCenturyOfEra(int centuryOfEra) Update time century units and return

        withYearOfCentury(int yearOfCentury)Update century year and return

        withYear(int year) Update time year and return

        withWeekyear(int weekyear) Update time weeks and return

        withMonthOfYear(int monthOfYear)Update time month and return

        withDayOfYear(int dayOfYear) Update time days and return

        withDayOfMonth(int dayOfMonth) Update time days and return

        withDayOfWeek(int dayOfWeek) Update time days and return

        withHourOfDay(int hour) Update time hours and return

        withMinuteOfHour(int minute) Update time minutes and return

        withSecondOfMinute(int second) Update time seconds and return

        withMillisOfSecond(int millis) Update time MS and return

        withMillisOfDay(int millis) Update time MS and return

        withTimeAtStartOfDay() Get the earliest time of the day

    judge DateTime Some operation methods of object size state

        compareTo(DateTime d) Compare the two time sizes. If the time is greater than the specified time, return 1 if the time is less than the specified time-1 Equal return 0

        equals(DateTime d) Compare whether the two times are equal

        isAfter(long instant) Judge whether the time is greater than the specified time

        isAfterNow() Judge whether the time is greater than the current time

        isBefore(long instant) Judge whether the time is less than the specified time

        isBeforeNow() Judge whether the time is less than the current time

        isEqual(long instant) Judge whether the time is equal to the specified time

        isEqualNow() Judge whether the time is equal to the current time

(3) Format the time as joda time

// The incoming format template only needs a format string compatible with JDK SimpleDateFormat
public static String convert(Date date,String dateFormat){
    return new DateTime(date).toString(dateFormat);
}
// Convert Date in JDK to DateTime in UTC time zone
DateTime dateTime = new DateTime(new Date(), DateTimeZone.UTC);
// Convert String to DateTime
public static Date convertUTC2Date(String utcDate){
    DateTime dateTime =DateTime.parse(utcDate, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
    return dateTime.toDate();
 }

Please refer to the official documentation for more usage.

3, New time class library in JAVA 8

1. Introduction

Due to the defects and poor use experience of the class library of the previous version of JDK, and the influence of joda time, which has become the de facto standard, Oracle decided to provide high-quality date and time support in the JAVA API, which is the new time class library of JDK 8 integrating most joda time features. (the author of joda time actually participated in the development and implemented all the contents of JSR310. The new API is located under java.time. The common classes are as follows: LocalDate, LocalTime, Instant, Duration and Period.)

Because the new time class library of JDK 8 draws heavily on the design idea and even naming of joda time, if you are a user of joda time, you can use the new API without learning cost (of course, there are some differences between them that need to be noted).

2. Method of use

(1) Use LocalDate and LocalTime

The first is LocalDate. The instance of this class is an immutable object. It only provides a simple date and does not contain the time information of the current day. In addition, it does not attach any time zone related information.

// Creates a LocalDate using the specified date
LocalDate date = LocalDate.of(2019, 1, 1);
// Get current date
LocalDate today = LocalDate.now();
// Get today's properties
int year = date.getYear();
Month month = date.getMonth();
int day = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek();
int len = date.lengthOfMonth();
boolean leap = date.isLeapYear();
// Obtain the required attribute field through the enumeration value of ChronoField
int year = date.get(ChronoField.YEAR);

Then comes the local time, which represents a time of day.

LocalTime time = LocalTime.of(18, 18, 18);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();

Both LocalDate and LocalTime can be created by parsing strings using the static method parse.

LocalDate date = LocalDate.parse("2019-01-01");

LocalTime time = LocalTime.parse("18:18:18");

You can also pass a DateTimeFormatter to the parse method. The instance of this class defines how to format a date or time object. It is actually a substitute for the old java.util.DateFormat.

(2) LocalDateTime

// Create LocalDateTime directly
LocalDateTime dt1 = LocalDateTime.of(2019, Month.JANUARY, 1, 18, 18, 18);
// Merge date and time
LocalDate date = LocalDate.parse("2019-01-01");
LocalTime time = LocalTime.parse("18:18:18");
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(18, 18, 18);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
// Extract LocalDate or LocalTime from LocalDateTime
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();

(3) Instant

The Instant class is designed to facilitate computer understanding. It represents a single large integer number at a point in a duration period. In fact, it is calculated based on the number of seconds experienced from the Unix first year time (traditionally set as midnight on January 1, 1970, UTC time zone) (the minimum calculation unit is nanoseconds).

// Pass a second to create an instance of the class
Instant.ofEpochSecond(3);
// Pass a second + nanosecond, 2 seconds, plus 1 million nanoseconds (1 second)
Instant.ofEpochSecond(2, 1_000_000_000);

(4) Duration and Period

​ Duration Is used to compare two LocalTime Object or two Instant Time difference between.

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);

Period is used to compare multiple times by month, year and day.

Period tenDays = Period.between(LocalDate.of(2019, 1, 1), lcalDate.of(2019, 2, 2));

Of course, both Duration and Period classes provide many very convenient factory classes to directly create corresponding instances.

Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

(5) Operation, parsing, and formatting dates

// Modify directly using the withAttribute method
LocalDate date1 = LocalDate.of(2019, 1, 1);
LocalDate date2 = date1.withYear(2019);
LocalDate date3 = date2.withDayOfMonth(1);
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 1);

All classes that declare the temporary interface, such as LocalDate, LocalTime, LocalDateTime and Instant, use get and with methods to distinguish the reading and modification of object values. If unsupported fields are used to access fields, an unsupported temporary typeexception will be thrown. Similarly, both the plus method and the minus method are declared on the temporary interface. By adding or subtracting a number from the TemporalUnit object through these methods, we can easily backtrack or rollback the TemporalUnit object to a certain time period. Through chrononunit enumeration, we can easily implement the TemporalUnit interface.

(6) More customized processing time

Passing a customized TemporalAdjuster object to the overloaded with method gives you more flexibility in dealing with dates. The time and date API already provides a large number of predefined temporaladjusters that can be accessed through the static factory methods of the TemporalAdjuster class. The names of these methods are very intuitive, and the method name is the problem description. In some cases, if you need to define your own TemporalAdjuster, you only need to declare the TemporalAdjuster interface and implement the corresponding methods yourself.

LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.with(TemporalAdjuster.nextOrSame(DayOfWeek.SUNDAY));
LocalDate date3 = date2.with(TemporalAdjuster.lastDayOfMonth());

(7) Resolve date time object

In daily work, formatting and parsing date time objects is another very important function, and the new java.time.format package is specially designed for this purpose. The most important class is DateTimeFormatter. All DateTimeFormatter instances can be used to create a string representing a specific date or time in a certain format. (compared with the old java.util.DateFormat, all DateTimeFormatter instances are thread safe)

// Generate strings using different formatter
LocalDate date = LocalDate.of(2019, 1, 1);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
// Generate LocalDate object
LocalDate date1 = LocalDate.parse("20190101", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2019-01-01", DateTimeFormatter.ISO_LOCAL_DATE);

// Creates a formatter using a specific schema
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2019, 1, 1);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);

(8) Handling different time zones and calendar systems

In the new date time class library, a new java.time.ZoneId class (like other date and time classes, ZoneId class cannot be modified) is used to replace the old java.util.TimeZone in order to minimize the tedious and complex processing of time zones. Time zone is to divide the region into intervals with the same standard time according to certain rules. The class ZoneRules contains 40 such instances. You can simply call getRules() of ZoneId to get the rules of the specified time zone. Each specific ZoneId object is identified by a region ID in the format of "{region} / {City}". For example:

ZoneId romeZone = ZoneId.of("Asia/Shanghai");

In Java 8, a new method toZoneId is added to the original TimeZone to convert an old time zone object into ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();

After obtaining the ZoneId object, you can integrate it with LocalDate, LocalDateTime or Instant objects to construct a ZonedDateTime instance, which represents the time point relative to the specified time zone:

LocalDate date = LocalDate.of(2019, Month.JANUARY, 1);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2019, Month.JANUARY, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);

With ZoneId, you can also convert LocalDateTime to Instant:

LocalDateTime dateTime = LocalDateTime.of(2019, Month.JANUARY, 18, 13, 45);
Instant instantFromDateTime = dateTime.toInstant(romeZone);

The LocalDateTime object can also be obtained in the reverse way:

Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);

Different from joda time, the date time class library in Java 8 provides four other calendar systems. Each of these calendar systems has a corresponding log class, namely Thai buddhistdate, MinguoDate, Japanese date and HijrahDate. All these classes and LocalDate implement the ChronoLocalDate interface, which can model the date of the Gregorian calendar. Using the LocalDate object, you can create instances of these classes. Similarly, using the static factory methods they provide, you can create an instance of any temporary object.

LocalDate date = LocalDate.of(2019, Month.JANUARY, 1);
JapaneseDate japaneseDate = JapaneseDate.from(date);

For the use and localization of specific calendar system, please refer to the official documents, which will not be repeated here.

The above is the full content of this blog. Due to the author's level or space limitation, please refer to the official documents or other webfriends' articles for details. Please leave a message in the comment area to point out the errors or questions about the content of the article. Thank you very much.

(if the references in the text are suspected of infringement, please contact the author to delete them.)

Tags: Java Back-end java8

Posted on Mon, 29 Nov 2021 23:00:40 -0500 by kerplunk