7.3. Date operations (JSR-310 Date and Time API)


7.3.1. Overview

This guideline recommends the use of JSR-310 Date and Time API which offers various date and time calculations,
as against java.util.Date and java.util.Calendar.

Note

Since JSR-310 Date and Time API has been introduced from Java8, using Joda Time is recommended for the environments of versions Java8 and below. For how to use Joda Time, refer Date Operations (Joda Time).

7.3.2. How to use

Date and Time API offers various classes depending on the application such as a class exclusively handling dates and a class exclusively handling time.
In this guideline, the focus is on java.time.LocalDate , java.time.LocalTime and java.time.LocalDateTime, however, since the prefix of all the methods offered by each class is same for primary date and time operations, it should be interpreted by substituting with the appropriate class name.
Classes and methods used primarily are as given below.

Primary class for handling date and time
Class name Description Primary factory methods
Class which handles date and time operation and does not possess information about timezone difference
now Generated by current date and time
of Generated by any date and time
parse Generated from date and time string
from Generated from other objects with date and time information
Class which handles date and time operation considering timezone difference Same as above
Class to handle Japanese calendar operations Same as above

Primary class for handling information about time period
Class name Description Primary factory methods
Class which handles date and time base, time base period
between Generated from the difference between two objects with date and time information
from Generated from another object that contains amount of time
of Generated in any time period

Class for handling format
Class name Description Primary factory method
Class which handles operations related to date and time format
ofPattern Generates a formatter in specified pattern

How to use each class and method is explained below.

Note

For the topics that are not covered in the guideline, refer Javadoc for details.

Note

Date and Time API class is immutable (date and time calculation result is considered as a new object, and changes do not occur in the objects for calculation).

7.3.2.1. Fetch date and time

7.3.2.1.1. Fetch by current date and time

java.time.LocalTime , java.time.LocalDate and java.time.LocalDateTime must be selectively used in accordance with the purpose for which it is to be used. Example is shown below.
  1. Use java.time.LocalTime when only time is to be fetched.
LocalTime localTime =  LocalTime.now();
  1. Use java.time.LocalDate when only date is to be fetched.
LocalDate localDate =  LocalDate.now();
  1. Use java.time.LocalDateTime when both date and time are to be fetched.
LocalDateTime localDateTime = LocalDateTime.now();

7.3.2.1.2. Fetch by specifying year, month, day, hours, minutes and seconds

Specific date and time can be specified by using of method. Example is shown below.
  1. Specify time and fetch java.time.LocalTime.
// 23:30:59
LocalTime localTime =  LocalTime.of(23, 30, 59);
  1. Specify date and fetch java.time.LocalDate.
// 2015/12/25
LocalDate localDate =  LocalDate.of(2015, 12, 25);
  1. Specify date and time and fetch java.time.LocalDateTime.
// 2015/12/25 23:30:59
LocalDateTime localDateTime = LocalDateTime.of(2015, 12, 25, 23, 30, 59);

Also, various dates and times can be fetched by using java.time.temporal.TemporalAdjusters.
// LeapYear(2012/2)
LocalDate localDate1 = LocalDate.of(2012, 2, 1);

// Last day of month(2012/2/29)
LocalDate localDate2 = localDate1.with(TemporalAdjusters.lastDayOfMonth());

// Next monday(2012/2/6)
LocalDate localDate3 = localDate1.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

Note

Unlike the specifications of java.util.Calendar, calendar month starts from 1.

7.3.2.1.3. Fetch date and time when time zone is specified

When an international application is to be created, a design must be adopted considering the time zone.
java.time.OffsetTime , java.time.OffsetDateTime and java.time.ZonedDateTime must be used selectively in Date and Time API in accordance with the purpose for which it is to be used.
Example is given below.
  1. Use java.time.OffsetTime when the time difference between time + UTC is to be fetched.
// Ex, 12:30:11.567+09:00
OffsetTime offsetTime =  OffsetTime.now();
  1. Use java.time.OffsetDateTime when the time difference between date, time + UTC is to be fetched.
// Ex, 2015-12-25T12:30:11.567+09:00
OffsetDateTime offsetDateTime =  OffsetDateTime.now();
  1. Use java.time.ZonedDateTime when the time difference and region for date, time + UTC is to be fetched.
// Ex, 2015-12-25T12:30:11.567+09:00[Asia/Tokyo]
ZonedDateTime zonedDateTime = ZonedDateTime.now();
Further, current date and time considering the time zone can be fetched in all these methods by specifying java.time.ZoneId which indicates time zone in the argument.
java.time.ZoneId example is shown below.
ZoneId zoneIdTokyo = ZoneId.of("Asia/Tokyo");
OffsetTime offsetTime =  OffsetTime.now(zoneIdTokyo);
Note that, java.time.ZoneId consists of a method to be defined by region name/area name format and a method defined by time difference from UTC.
ZoneId.of("Asia/Tokyo");
ZoneId.of("UTC+01:00");

Although purpose of using java.time.OffsetDateTime and java.time.ZonedDateTime is similar, the basic difference is as given below.
An appropriate class should be selected according to the characteristics of the system to be created.
Class name Description
java.time.OffsetDateTime
Since it only consists of quantitative value (only time difference), system does not undergo any change even if there is a change in the concept of time for each area.
java.time.ZonedDateTime
Since it includes concept of region besides time difference, the system undergoes change when a change occurs in the concept of time for each region.(when daylight saving etc is included as a policy)

7.3.2.2. Time period

7.3.2.2.1. Fetch time period

java.time.Period is used while handling date based period and java.time.Duration is used while handling time based period.
Since day represented by java.time.Duration is of exactly 24 hours, expected results may not be obtained if daylight saving changes are not incorporated.
On the contrary, since java.time.Period represents 1 day including daylight saving, an error does not occur even in the system which handles daylight saving.
Example is given below.
LocalDate date1 = LocalDate.of(2010, 01, 15);
LocalDate date2 = LocalDate.of(2011, 03, 18);
LocalTime time1 = LocalTime.of(11, 50, 50);
LocalTime time2 = LocalTime.of(12, 52, 53);

// One year, two months and three days.
Period pd = Period.between(date1, date2);

// One hour, two minutes and three seconds.
Duration dn = Duration.between(time1, time2);

Note

A method can also be employed wherein the period is generated by specifying it by using of method. For details, refer Javadoc of Period, Duration.

7.3.2.3. Type conversion

7.3.2.3.1. Interoperability of each class of Date and Time API

java.time.LocalTime , java.time.LocalDate and java.time.LocalDateTime can easily be mutually converted. Example is given below.
  1. Conversion from java.time.LocalTime to java.time.LocalDateTime.
// Ex. 12:10:30
LocalTime localTime =  LocalTime.now();

// 2015-12-25 12:10:30
LocalDateTime localDateTime = localTime.atDate(LocalDate.of(2015, 12, 25));
  1. Conversion from java.time.LocalDate to java.time.LocalDateTime.
// Ex. 2012-12-25
LocalDate localDate =  LocalDate.now();

// 2015-12-25 12:10:30
LocalDateTime localDateTime = localDate.atTime(LocalTime.of(12, 10, 30));
  1. Conversion from java.time.LocalDateTime to java.time.LocalTime and java.time.LocalDate.
// Ex. 2015-12-25 12:10:30
LocalDateTime localDateTime =  LocalDateTime.now();

// 12:10:30
LocalTime localTime =  localDateTime.toLocalTime();

// 2012-12-25
LocalDate localDate =  localDateTime.toLocalDate();

Similarly, java.time.OffsetTime , java.time.OffsetDateTime and java.time.ZonedDateTime can also be easily mutually converted. Example is given below.
  1. Conversion from java.time.OffsetTime to java.time.OffsetDateTime.
// Ex, 12:30:11.567+09:00
OffsetTime offsetTime =  OffsetTime.now();

// 2015-12-25T12:30:11.567+09:00
OffsetDateTime offsetDateTime = offsetTime.atDate(LocalDate.of(2015, 12, 25));
  1. Conversion from java.time.OffsetDateTime to java.time.ZonedDateTime.
// Ex, 2015-12-25T12:30:11.567+09:00
OffsetDateTime offsetDateTime =  OffsetDateTime.now();

// 2015-12-25T12:30:11.567+09:00[Asia/Tokyo]
ZonedDateTime zonedDateTime = offsetDateTime.atZoneSameInstant(ZoneId.of("Asia/Tokyo"));
  1. Conversion from java.time.ZonedDateTime to java.time.OffsetDateTime and java.time.OffsetTime.
// Ex, 2015-12-25T12:30:11.567+09:00[Asia/Tokyo]
ZonedDateTime zonedDateTime =  ZonedDateTime.now();

// 2015-12-25T12:30:11.567+09:00
OffsetDateTime offsetDateTime =  zonedDateTime.toOffsetDateTime();

// 12:30:11.567+09:00
OffsetTime offsetTime =  zonedDateTime.toOffsetDateTime().toOffsetTime();

Also, java.time.LocalTime can be converted to java.time.OffsetTime by adding time difference information.
// Ex, 12:30:11.567
LocalTime localTime =  LocalTime.now();

// 12:30:11.567+09:00
OffsetTime offsetTime = localTime.atOffset(ZoneOffset.ofHours(9));

Besides, conversion to another class is also possible by adding missing information (date information is not adequate in case of conversion from LocalTime to LocalDateTime).
Conversion method begins with the prefix at or to. For details, refer Javadoc of each class .

7.3.2.3.2. Interoperability with java.util.Date

A method which directly converts java.time.LocalDate class to java.util.Date is not provided.

However, since a method which converts java.time.Instant offered by Date and Time API is added to java.util.Date from Java8 and subsequent versions, a conversion can be carried out through java.time.Instant.
Example is given below.
  1. Conversion from java.time.LocalDateTime to java.util.Date.
LocalDateTime localDateTime = LocalDateTime.now();
Instant instant = localDateTime.toInstant(ZoneOffset.ofHours(9));
Date date = Date.from(instant);
  1. Conversion from java.util.Date to java.time.LocalDateTime.
Date date = new Date();
Instant instant = date.toInstant();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

Note

Since java.time.LocalTime and java.time.LocalDate do not contain Instant values, it is necessary to convert once to java.time.LocalDateTime.

7.3.2.3.3. Interoperability with java.sqlpackage

An upgrade is added to java.sql package from Java8 version and a method for mutual conversion with java.time package is defined.
Example is given below.
  1. Conversion from java.sql.Date to java.time.LocalDate.
java.sql.Date date =  new java.sql.Date(System.currentTimeMillis());
LocalDate localDate = date.toLocalDate();
  1. Conversion from java.time.LocalDate to java.sql.Date.
LocalDate localDate = LocalDate.now();
java.sql.Date date =  java.sql.Date.valueOf(localDate);
  1. Conversion from java.sql.Time to java.time.LocalTime.
java.sql.Time time =  new java.sql.Time(System.currentTimeMillis());
LocalTime localTime = time.toLocalTime();
  1. Conversion from java.time.LocalTime to java.sql.Time.
LocalTime localTime = LocalTime.now();
java.sql.Time time =  java.sql.Time.valueOf(localTime);
  1. Conversion from java.sql.Timestamp to java.time.LocalDateTime.
java.sql.Timestamp timestamp =  new java.sql.Timestamp(System.currentTimeMillis());
LocalDateTime localDateTime = timestamp.toLocalDateTime();
  1. Conversion from java.time.LocalDateTime to java.sql.Timestamp.
LocalDateTime localDateTime = LocalDateTime.now();
java.sql.Timestamp timestamp =  java.sql.Timestamp.valueOf(localDateTime);

7.3.2.3.4. How to use org.terasoluna.gfw.common.date package

Currently, Date Factory for Date and Time API is not provided in the common library. (Refer: System Date)
However, java.time.LocalDate can be generated by using org.terasoluna.gfw.common.date.ClassicDateFactory and java.sql.Date as an interim measure.
It can be generated by converting from java.time.LocalDate even for the java.time.LocalTime and java.time.LocalDateTime classes.
Example is given below.

bean definition file ([projectname]-env.xml)

<bean id="dateFactory" class="org.terasoluna.gfw.common.date.DefaultClassicDateFactory" />

Java class

@Inject
ClassicDateFactory dateFactory;

public DateFactorySample getSystemDate() {

 java.sql.Date date = dateFactory.newSqlDate();
 LocalDate localDate = date.toLocalDate();

 // omitted
}

Note

Date Factory corresponding to Date and Time API will be added later.

7.3.2.3.5. Format for the string

A method which uses toString method and a method which uses java.time.fomat.DateTimeFormatter can be used for converting the object containing date and time information to string.
When outputting any date and time string, conversion to various date and time strings can be done by using java.time.fomat.DateTimeFormatter.

java.time.fomat.DateTimeFormatter consists of a method which uses a formatter of predefined ISO pattern and a method which is used by defining a format of any pattern.
DateTimeFormatter formatter1 = DateTimeFormatter.BASIC_ISO_DATE;

DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("G uuuu/MM/dd E")
                                          .withLocale(Locale.JAPANESE)
                                          .withResolverStyle(ResolverStyle.STRICT);
At that time, Locale and ResolverStyle (strict) can be defined besides string format.
Since default value of Locale changes depending on the system, it should be set at the time of initialization.
Also, when ResolverStyle (strict) uses ofPattern method, ResolverStyle.SMART is set as default, however, in this guideline, it is recommended to specify ResolverStyle.STRICT for strict interpretation of date to avoid occurrence of unexpected behaviour.(When ISO pattern formatter is to be used, ResolverStyle.STRICT is specified.)

Also, since format yyyy in Date and Time API represent year in the calendar, interpretation will be different according to the calendar. (Year will be 2015 according to Western calendar but will be 0027 according to Japanese calendar).
When western calendar is to be indicated, it is recommended to use uuuu format instead of yyyy format.For the defined format list, refer DateTimeFormatter .

Example is given below.
DateTimeFormatter formatter1 = DateTimeFormatter.BASIC_ISO_DATE;

DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("G uuuu/MM/dd E")
                                          .withLocale(Locale.JAPANESE)
                                          .withResolverStyle(ResolverStyle.STRICT);

LocalDate localDate1 = LocalDate.of(2015, 12, 25);

// "2015-12-25"
System.out.println(localDate1.toString());
// "20151225"
System.out.println(formatter1.format(localDate1));
// "Western calendar 2015/12/25 Friday"
System.out.println(formatter2.format(localDate1));

Also, when the strings are to be displayed on the screen,
dedicated JSP tags do not exist in Date and Time API unlike Joda Time.
Since fmt:formatDate tag of JSTL handles only java.util.Date and java.util.TimeZone objects,
formatted string is passed and displayed when the date and time information held by object of Date and Time API on JSP is to be displayed.

Controller class

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = {RequestMethod.GET, RequestMethod.POST})
    public String home(Model model, Locale locale) {

        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu/MM/dd")
                                           .withLocale(locale)
                                           .withResolverStyle(ResolverStyle.STRICT);

        LocalDate localDate1 = LocalDate.now();

        model.addAttribute("currentDate", localDate1.toString());
        model.addAttribute("formattedCurrentDateString", dateFormatter.format(localDate1));

    // omitted

    }
}

jsp file

<p>currentDate =  ${f:h(currentDate)}.</p>
<p>formattedCurrentDateString =  ${f:h(formattedCurrentDateString)}.</p>

Output results example (html)

<p>currentDate =  2015-12-25.</p>
<p>formattedCurrentDateString =  2015/12/25.</p>

7.3.2.3.6. Path from the string

Similar to conversion to string, various date strings can be converted to Date and Time API class by using java.time.fomat.DateTimeFormatter.
Example is as shown below.
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("uuuu/MM/dd")
                                           .withLocale(Locale.JAPANESE)
                                           .withResolverStyle(ResolverStyle.STRICT);

DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("HH:mm:ss")
                                           .withLocale(Locale.JAPANESE)
                                           .withResolverStyle(ResolverStyle.STRICT);

LocalDate localDate = LocalDate.parse("2015/12/25", formatter1);
LocalTime localTime = LocalDate.parse("14:09:20", formatter2);

7.3.2.4. Date operation

Date and time can be easily calculated and compared in Date and Time API.
Example is as shown below.

7.3.2.4.1. Date and time calculation

plus method and minus method are provided for calculating date and time.
  1. Example for calculating time.
LocalTime localTime =  LocalTime.of(20, 30, 50);
LocalTime plusHoursTime = localTime.plusHours(2);
LocalTime plusMinutesTime = localTime.plusMinutes(10);
LocalTime minusSecondsTime = localTime.minusSeconds(15);
  1. Example for calculating date.
LocalDate localDate =  LocalDate.of(2015, 12, 25);
LocalDate plusYearsDate = localDate.plusYears(10);
LocalDate minusMonthsTime = localDate.minusMonths(1);
LocalDate plusDaysTime = localDate.plusDays(3);

Note

If a negative number is passed in the plus method, results similar to the results at the time of using minus method can be obtained. Same for minus method.

7.3.2.4.2. Date and time comparison

Time series for past, future and current period can be compared in Date and Time API.
Example is as shown below.
  1. Example for comparison of time.
LocalTime morning =  LocalTime.of(7, 30, 00);
LocalTime daytime =  LocalTime.of(12, 00, 00);
LocalTime evening =  LocalTime.of(17, 30, 00);

daytime.isBefore(morning); // false
morning.isAfter(evening); // true
evening.equals(LocalTime.of(17, 30, 00)); // true

daytime.isBefore(daytime); // false
morning.isAfter(morning); // false
  1. Example for comparison of date.
LocalDate may =  LocalDate.of(2015, 6, 1);
LocalDate june =  LocalDate.of(2015, 7, 1);
LocalDate july =  LocalDate.of(2015, 8, 1);

may.isBefore(june); // true
june.isAfter(july); // false
july.equals(may); // false

may.isBefore(may); // false
june.isAfter(june); // false

Note that, the class applicable to Interval of Joda Time currently does not exist in Date and Time API.

7.3.2.4.3. Determination of date and time

Example for determining date and time is shown below.
  1. When a valid date and time string is to be determined, it can be determined based on the occurrence and non-occurrence of java.time.format.DateTimeParseException.
String strDateTime = "aabbcc";
DateTimeFormatter timeFormatter  = DateTimeFormatter.ofPattern("HHmmss")
                              .withLocale(Locale.JAPANESE)
                              .withResolverStyle(ResolverStyle.STRICT);;

DateTimeFormatter dateFormatter  = DateTimeFormatter.ofPattern("uuMMdd")
                              .withLocale(Locale.JAPANESE)
                              .withResolverStyle(ResolverStyle.STRICT);;

try {
    // DateTimeParseException
    LocalTime localTime = LocalTime.parse(strDateTime, timeFormatter);
}
catch (DateTimeParseException e) {
    System.out.println("Invalid time string !!");
}

try {
    // DateTimeParseException
    LocalDate localDate = LocalDate.parse(strDateTime, dateFormatter);
}
catch (DateTimeParseException e) {
    System.out.println("Invalid date string !!");
}
  1. When a leap year is to be determined, isLeapYear method of java.time.LocalDate can be used.
LocalDate date1 = LocalDate.of(2012, 1, 1);
LocalDate date2 = LocalDate.of(2015, 1, 1);

date1.isLeapYear(); // true
date2.isLeapYear(); // false

7.3.2.4.4. Fetching year, month, day, hours, minutes, seconds

When the respective year, month, day, hours, minutes and seconds are to be fetched, use get method.
An example to fetch information related to date is shown below.
LocalDate localDate = LocalDate.of(2015, 2, 1);

// 2015
int year = localDate.getYear();

// 2
int month = localDate.getMonthValue();

// 1
int dayOfMonth = localDate.getDayOfMonth();

// 32 ( day of year )
int dayOfYear = localDate.getDayOfYear();

7.3.2.5. Japanese calendar (JapaneseDate)

A class called java.time.chrono.JapaneseDate is provided in Date and Time API to handle Japanese calendar.

Note

java.time.chrono.JapaneseDate cannot be used before Meiji 6 (1873 of Western calendar) when the Gregorian calendar was introduced.

7.3.2.5.1. Fetching Japanese calendar

Similar to java.time.LocalDate, Japanese calendar can be fetched by using now method and of method.
Further, Japanese calendar can also be fetched by using java.time.chrono.JapaneseEra class.
Example is shown below.
JapaneseDate japaneseDate1 = JapaneseDate.now();
JapaneseDate japaneseDate2 = JapaneseDate.of(2015, 12, 25);
JapaneseDate japaneseDate3 = JapaneseDate.of(JapaneseEra.HEISEI, 27, 12, 25);
An exception occurs when a value prior to Meiji 6 is specified in the argument.
// DateTimeException
JapaneseDate japaneseDate = JapaneseDate.of(1500, 1, 1);

7.3.2.5.2. Format for the string

It is possible to convert to Japanese calendar date by using java.time.fomat.DateTimeFormatter. While using, calendar is set to java.time.chrono.JapaneseChronology by using DateTimeFormatter#withChronology method.
Since various formats can be handled in Japanese calendar dates as well, output can be obtained corresponding to the requirements like zero-filling or space-filling etc.
An example wherein Japanese calendar is displayed using space filling is shown below.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("GppyYearppMMonthppdDay")
                                 .withLocale(Locale.JAPANESE)
                                 .withResolverStyle(ResolverStyle.STRICT)
                                 .withChronology(JapaneseChronology.INSTANCE);

JapaneseDate japaneseDate = JapaneseDate.of(1992, 1, 1);

// "Heisei YY4 MM1 DD1"
System.out.println(formatter.format(japaneseDate));

7.3.2.5.3. Path from the string

Japanese string can be converted to java.time.chrono.JapaneseDate by using java.time.fomat.DateTimeFormatter.
Example is shown below.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("GyYearMMMonthddDate")
                                .withLocale(Locale.JAPANESE)
                                .withResolverStyle(ResolverStyle.STRICT)
                                .withChronology(JapaneseChronology.INSTANCE);

JapaneseDate japaneseDate1 = JapaneseDate.from(formatter.parse("Heisei 27YY12MM25DD"));
JapaneseDate japaneseDate2 = JapaneseDate.from(formatter.parse("Meiji YY6MM01DD01"));

7.3.2.5.4. Conversion of Western and Japanese calendar

Conversion between Western and Japanese calendars can be easily carried out from java.time.LocalDate by using from method.
LocalDate localDate = LocalDate.of(2015, 12, 25);
JapaneseDate jpDate = JapaneseDate.from(localDate);