Java 8 Date and Time API
Java 8 introduced the new Date and Time API. This new API is inspired by the popular JodaTime library. It was added to overcome the drawbacks of the existing Date API.
In this tutorial, we will discuss the important classes of the new Date and Time API.
Drawbacks of Existing API(Java 7)
The existing date and time API had a few major drawbacks. Let's discuss some of these drawbacks.
- Thread Safety - The existing Date and Calendar classes are not thread-safe and require additional efforts to handle concurrency. The new LocalDate, LocalTime, and LocalDateTime classes solved this issue. These classes are immutable and thread-safe.
- Time zones - The old APIs did not provide any support for time zones. The new ZonedDateTime class supports time zones as well.
- Other Improvements - The new API meets the ISO standards and is easier to understand. It provides a lot of methods to manipulate date and time. The existing API is a bit difficult to use and understand as compared to the new API.
LocalDate Class
The LocalDate class is used to represent a date in the standard ISO format(yyyy-mm-dd). It does not include time and does not support timezones.
Creating LocalDate Instances
We can create an instance of the LocalDate class by using the now() method. It will capture the current date according to the system's clock.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
System.out.print("Current Date is: " + currDate);
}
}
Current Date is: 2021-08-13
To create an instance using some other date, we can use the of() method. This method takes three integer arguments, the year, month, and day.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate date = LocalDate.of(2021, 8, 13);
System.out.print("Date: " + date);
}
}
Date: 2021-08-13
Use the parse() method if you have a string that contains a date. Make sure that the date is in the correct ISO format.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate date = LocalDate.parse("2021-08-13");
System.out.print("Date: " + date);
}
}
Date: 2021-08-13
Plus and Minus Methods
We can add or subtract days, months, or years from a date. Let's use the plusDays() or minusDays() method to fetch yesterday's date and tomorrow's date. We also have similar methods to alter month and year.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
LocalDate yesterday = currDate.minusDays(1);
LocalDate tomorrow = currDate.plusDays(1);
System.out.println("Current Date: " + currDate);
System.out.println("tomorrow's Date: " + tomorrow);
System.out.println("Yesterday's Date: " + yesterday);
}
}
Current Date: 2021-08-13
tomorrow's Date: 2021-08-14
Yesterday's Date: 2021-08-12
We can also use the simpler plus() or minus() methods to manipulate dates. These methods will use the ChronoUnit enum. Remember that LocalDate is immutable, and we are not changing the existing date. All these methods return a new instance with the modifications.
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
LocalDate date1 = currDate.plus(5, ChronoUnit.MONTHS);
LocalDate date2 = currDate.minus(5, ChronoUnit.DAYS);
System.out.println("Current Date: " + currDate);
System.out.println("Current Date plus five months: " + date1);
System.out.println("Current Date minus five days: " + date2);
}
}
Current Date: 2021-08-13
Current Date plus five months: 2022-01-13
Current Date minus five days: 2021-08-08
Getter Methods
The LocalDate class contains several getter methods to fetch different information from the LocalDate such as get day of week or day of month, etc. The following code demonstrates this.
import java.time.DayOfWeek;
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
DayOfWeek dayOfWeek = currDate.getDayOfWeek();//getDayOfWeek() returns a DayOfWeek instance and not a String
int dayOfMonth = currDate.getDayOfMonth();
int dayOfYear = currDate.getDayOfYear();
System.out.println("Date: " + currDate);
System.out.println("Day of Week: " + dayOfWeek);
System.out.println("Day of Month: " + dayOfMonth);
System.out.println("Day of Year: " + dayOfYear);
}
}
Date: 2021-08-13
Day of Week: FRIDAY
Day of Month: 13
Day of Year: 225
Comparing Dates
We can compare dates by using the isBefore() and isAfter() methods to check which is greater. See the example below.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
LocalDate tomorrow = currDate.plusDays(1);
LocalDate yesterday = currDate.minusDays(1);
System.out.println("Current date is after yesterday's date: " + currDate.isAfter(yesterday));
System.out.println("Current date is before tomorrow's date: " + currDate.isBefore(tomorrow));
}
}
Current date is after yesterday's date: true
Current date is before tomorrow's date: true
Other Utility Methods
This class also provides other utility methods. For example, we have the isLeapYear() method that returns a boolean value indicating whether the year is a leap year or not.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
System.out.println(currDate + " is a leap year? " + currDate.isLeapYear());
}
}
2021-08-13 is a leap year? false
We also have the lengthOfMonth() and lengthOfYear() methods that return the length of month and year.
import java.time.LocalDate;
public class Demo
{
public static void main(String[] args)
{
LocalDate currDate = LocalDate.now();
System.out.println("Current Date " + currDate);
System.out.println("Length of Month: " + currDate.lengthOfMonth());
System.out.println("Length of Year: " + currDate.lengthOfYear());
}
}
Current Date 2021-08-14
Length of Month: 31
Length of Year: 365
There are a lot of other methods available. Check out the official Javadoc page to view them.
LocalTime
The LocalTime class is used to represent the time.
Creating LocalTime Instances
To create a LocalTime instance, we can use the now(), of(), or parse() method. The now() method will use the system's clock to fetch the current time. The of() and parse() methods can create a LocalTime according to the parameters passed.
import java.time.LocalTime;
public class Demo
{
public static void main(String[] args)
{
LocalTime currTime = LocalTime.now();
LocalTime t1 = LocalTime.of(5, 32, 44);
LocalTime t2 = LocalTime.parse("05:32:44");
System.out.println(currTime);
System.out.println(t1);
System.out.println(t2);
}
}
16:26:36.285807700
05:32:44
05:32:44
Plus and Minus Methods
The LocalTime class also gets several plus and minus methods.
import java.time.LocalTime;
public class Demo
{
public static void main(String[] args)
{
LocalTime t1 = LocalTime.of(5, 32, 44);
LocalTime t2 = t1.plusHours(2);
LocalTime t3 = t1.minusMinutes(10);
System.out.println(t1);
System.out.println(t2);
System.out.println(t3);
}
}
05:32:44
07:32:44
05:22:44
We can also use the simpler plus() and minus() methods with a ChronoUnit parameter.
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class Demo
{
public static void main(String[] args)
{
LocalTime t1 = LocalTime.of(5, 32, 44);
LocalTime t2 = t1.plus(2, ChronoUnit.HOURS);
LocalTime t3 = t1.plus(10, ChronoUnit.MINUTES);
System.out.println(t1);
System.out.println(t2);
System.out.println(t3);
}
}
05:32:44
07:32:44
05:42:44
Getter Methods
This class also contains several getter methods to fetch different information like the hours, minutes, or seconds. The following code demonstrates this.
import java.time.LocalTime;
public class Demo
{
public static void main(String[] args)
{
LocalTime t = LocalTime.of(5, 32, 44);
int hours = t.getHour();
int minutes = t.getMinute();
int seconds = t.getSecond();
System.out.println("Time: " + t);
System.out.println("Hours: " + hours);
System.out.println("Minutes: " + minutes);
System.out.println("Seconds: " + seconds);
}
}
Time: 05:32:44
Hours: 5
Minutes: 32
Seconds: 44
Comparing LocalTime Instances
Like the LocalDate class, we can compare instances of LocalTime by using the isBefore() and isAfter() methods.
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class Demo
{
public static void main(String[] args)
{
LocalTime t1 = LocalTime.of(5, 32, 44);
LocalTime t2 = t1.plus(1, ChronoUnit.HOURS);
boolean b = t1.isAfter(t2); //False
}
}
LocalDateTime Class
The LocalDateTime class is a combination of the LocalDate and LocalTime classes. A LocalDateTime instance contains a date component and a time component.
Similar to the previous classes, we have the now(), of() and parse() methods to construct an object of the LocalDateTime class.
import java.time.LocalDateTime;
public class Demo
{
public static void main(String[] args)
{
LocalDateTime currDateTime = LocalDateTime.now();
LocalDateTime dt1 = LocalDateTime.of(2020, 8, 13, 5, 32);
LocalDateTime dt2 = LocalDateTime.parse("2020-08-13T05:32");
System.out.println(currDateTime);
System.out.println(dt1);
System.out.println(dt2);
}
}
2021-08-13T16:44:11.684424
2020-08-13T05:32
2020-08-13T05:32
We also have similar plus and minus methods for the LocalDateTime class.
import java.time.LocalDateTime;
public class Demo
{
public static void main(String[] args)
{
LocalDateTime currDateTime = LocalDateTime.now();
LocalDateTime dt1 = currDateTime.plusDays(10);
LocalDateTime dt2 = currDateTime.minusHours(5);
System.out.println(currDateTime);
System.out.println(dt1);
System.out.println(dt2);
}
}
2021-08-13T16:46:12.603985700
2021-08-23T16:46:12.603985700
2021-08-13T11:46:12.603985700
To fetch different components of the LocalDateTime instance, we can use the getters. The following code demonstrates the use of a few getter methods.
import java.time.LocalDateTime;
import java.time.Month;
public class Demo
{
public static void main(String[] args)
{
LocalDateTime currDateTime = LocalDateTime.now();
int hour = currDateTime.getHour();
int dayOfMonth = currDateTime.getDayOfMonth();
Month month = currDateTime.getMonth();
System.out.println(currDateTime);
System.out.println("Hour: " + hour);
System.out.println("Day of Month: " + dayOfMonth);
System.out.println("Month: " + month);
}
}
2021-08-13T16:49:07.087638100
Hour: 16
Day of Month: 13
Month: AUGUST
ZonedDateTime Class
The classes discussed above do not support time zones. The ZonedDateTime class provides this functionality. It is used with the ZoneId class that helps to identify different time zones.
Use the now() method to create an instance of this class. The relevant time zone information will be added to the current date and time.
import java.time.ZonedDateTime;
public class Demo
{
public static void main(String[] args)
{
ZonedDateTime zdt = ZonedDateTime.now();
System.out.print(zdt);
}
}
2021-08-13T17:31:18.046976800+05:30[Asia/Calcutta]
We can also provide a Zone Id to the now() method. The zone id is used to identify different time zones. Use the of() method of the ZoneId class to do this. Make sure you provide a valid time zone.
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Demo
{
public static void main(String[] args)
{
ZoneId zone = ZoneId.of("Asia/Tokyo");
ZonedDateTime zdt = ZonedDateTime.now(zone);
System.out.print(zdt);
}
}
2021-08-13T21:02:03.657050700+09:00[Asia/Tokyo]
We can view all the available time zones by using the getAvailableZoneIds() method of the ZoneId class.
import java.time.ZoneId;
import java.util.Set;
public class Demo
{
public static void main(String[] args)
{
Set<String> zones = ZoneId.getAvailableZoneIds();
for(String zone : zones)
System.out.println(zone);
}
}
A few timezones printed by the above code are shown below.
Asia/Colombo
Australia/West
Indian/Antananarivo
Australia/Brisbane
Indian/Mayotte
US/Indiana-Starke
We can also construct a ZonedDateTime instance by using the parse() method of this class.
import java.time.ZonedDateTime;
public class Demo
{
public static void main(String[] args)
{
ZonedDateTime zdt = ZonedDateTime.parse("2021-08-13T17:35:08.044680100+09:00[Asia/Tokyo]");
System.out.print(zdt);
}
}
2021-08-13T17:35:08.044680100+09:00[Asia/Tokyo]
ZoneOffset Class
Another way of working with timezones is to use the ZoneOffset. The following code demonstrates this.
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
public class Demo
{
public static void main(String[] args)
{
LocalDateTime dateTime = LocalDateTime.now();
ZoneOffset offset = ZoneOffset.of("+09:00");
OffsetDateTime offsetDateTime = OffsetDateTime.of(dateTime, offset);
System.out.print(offsetDateTime);
}
}
2021-08-13T17:38:41.485118900+09:00
Period and Duration
The Period and Duration classes are used to find the difference between two dates or times. The period is used for LocalDates, and the Duration is used for LocalTimes.
Let's use the Period class to fetch the difference between two dates. We will use the getDays(), getMonths(), and getYears() methods to fetch the difference between each component of the two dates.
import java.time.LocalDate;
import java.time.Period;
public class Demo
{
public static void main(String[] args)
{
LocalDate d1 = LocalDate.now();
LocalDate d2 = d1.plusDays(10).plusMonths(5).plusYears(1);
int days = Period.between(d1, d2).getDays();
int months = Period.between(d1, d2).getMonths();
int years = Period.between(d1, d2).getYears();
System.out.println("Initial Date: " + d1 + " Final Date " + d2);
System.out.println("Difference in Days: " + days);
System.out.println("Difference in Months: " + months);
System.out.println("Difference in Years: " + years);
}
}
Initial Date: 2021-08-13 Final Date 2023-01-23
Difference in Days: 10
Difference in Months: 5
Difference in Years: 1
Let's use the Duration class to fetch the difference between two LocalTimes. We can use the getSeconds() method of this class to fetch the difference between the two times in seconds.
import java.time.Duration;
import java.time.LocalTime;
public class Demo
{
public static void main(String[] args)
{
LocalTime t1 = LocalTime.now();
LocalTime t2 = t1.plusHours(10).plusMinutes(5).plusSeconds(15);
long secs = Duration.between(t1, t2).getSeconds();
System.out.println("Initial Time: " + t1 + " Final Time " + t2);
System.out.println("Difference in Seconds: " + secs);
}
}
Initial Time: 17:55:56.774818600 Final Time 04:01:11.774818600
Difference in Seconds: -50085
We can also use the between() method of the ChronoUnit class for this purpose. The following code demonstrates its use.
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class Demo
{
public static void main(String[] args)
{
LocalTime t1 = LocalTime.now();
LocalTime t2 = t1.plusHours(10).plusMinutes(5).plusSeconds(15);
long hours = ChronoUnit.HOURS.between(t2, t1);
long mins = ChronoUnit.MINUTES.between(t2, t1);
long secs = ChronoUnit.SECONDS.between(t2, t1);
System.out.println("Initial Time: " + t1 + " Final Time " + t2);
System.out.println("Difference in Hours: " + hours);
System.out.println("Difference in Minutes: " + mins);
System.out.println("Difference in Seconds: " + secs);
}
}
Initial Time: 17:57:29.308126300 Final Time 04:02:44.308126300
Difference in Hours: 13
Difference in Minutes: 834
Difference in Seconds: 50085
Conversion Between Old and New Classes
Java 8 also provides support to convert the old API classes to the new ones. We will use the ofInstant() method of the LocalDateTime class and the toInstant() method on the Date and Calendar class instances to perform the conversion. The code below demonstrates this.
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
public class Demo
{
public static void main(String[] args)
{
Date d = new Date();
Calendar c = Calendar.getInstance();
LocalDateTime dateTime1 = LocalDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault());
LocalDateTime dateTime2 = LocalDateTime.ofInstant(c.toInstant(), ZoneId.systemDefault());
System.out.println(dateTime1);
System.out.println(dateTime2);
}
}
2021-08-13T18:06:18.030
2021-08-13T18:06:18.055
Formatting Dates
Java 8 provides a format() function to format a date to a string using a particular pattern. The new DateTimeFormatter class allows us to use predefined patterns. In the code below, we are using a few predefined date formats.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Demo
{
public static void main(String[] args)
{
LocalDateTime dateTime = LocalDateTime.now();
String dateStr1 = dateTime.format(DateTimeFormatter.ISO_WEEK_DATE);
String dateStr2 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE);
String dateStr3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(dateStr1);
System.out.println(dateStr2);
System.out.print(dateStr3);
}
}
2021-W32-6
20210814
2021-08-14T09:02:32.5346662
The DateTimeFormatter class also allows us to define custom date formats. Let's separate each component of the date by two asterisks.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Demo
{
public static void main(String[] args)
{
LocalDateTime dateTime = LocalDateTime.now();
String dateStr1= dateTime.format(DateTimeFormatter.ofPattern("dd ** MM ** YYYY"));
System.out.print(dateStr1);
}
}
14 ** 08 ** 2021
Summary
The new Date and Time API aims to resolve the problems of the existing API. The new date and time classes are immutable and thread-safe. The LocalDate, LocalTime, and LocalDateTime classes are the most commonly used classes of the new API. These new classes are present in the java.time package. It also provides a ZonedDateTime class to work with time zones. This feature was also lacking in the existing API.