Java’s date time API can be quite confusing when you’re new to it. Java introduced another new set of date time API to replace the legacy API in version 8. In this article, we will be looking into the new Java 8 date time API.
Introduction
The legacy date time API has been there for decades, and one of the biggest feature in Java 8 is the new date time API. You might be asking, since the old API has been established for so long, why Java still create another set of date time API?
Here’s why
- Legacy date time API are mutable and not thread safe.
- Java date time classed is not standardized , e.g.
Date
class in bothjava.util
andjava.sql packages
, formatting and parsing classes are placed injava.text
package. - The poor API design that lack of day to day operation utilities method.
- Does not cater for time zone use case, developer need write additional logic to handle it.
Date Time API Classes
In the following example, we will be looking into the creation of different date time objects available in Java 8 date time API. You will notice that all of these classes have consistent API methods like now()
, parse()
, of()
.
The now()
method of these classes has an overloaded method where we can specify ZoneId for getting dates in a specific time zone.
Get all the available list :
1
2
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
System.out.println("available zone id = \n" + availableZoneIds);
LocalDate
LocalDate
is a class that represents a date with a default format of yyyy-MM-dd
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static void localDateExample() {
LocalDate now = LocalDate.now();
System.out.println("Current Date = " + now);
LocalDate secondDayInMarch2020 = LocalDate.parse("2020-03-02");
System.out.println("second day in march 2020");
LocalDate thirdDayInMarch2020 = LocalDate.of(2020, 3, 3);
System.out.println("third day in march 2020 = " + thirdDayInMarch2020);
LocalDate hundredDayIn2020 = LocalDate.ofYearDay(2020, 100);
System.out.println("100th day in 2020 = " + hundredDayIn2020);
LocalDate klDateNow = LocalDate.now(ZoneId.of("Asia/Kuala_Lumpur"));
System.out.println("Current Date in Kuala Lumpur = " + klDateNow);
LocalDate tokyoDateNow = LocalDate.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current Date in Tokyo = " + tokyoDateNow);
LocalDate chicagoDateNow = LocalDate.now(ZoneId.of("America/Chicago"));
System.out.println("Current Date in Chicago = " + chicagoDateNow);
}
Output:
1
2
3
4
5
6
7
Current Date = 2021-07-19
second day in march 2020
third day in march 2020 = 2020-03-03
100th day in 2020 = 2020-04-09
Current Date in Kuala Lumpur = 2021-07-19
Current Date in Tokyo = 2021-07-19
Current Date in Chicago = 2021-07-19
LocalTime
LocalTime
is a class that represents a time in human readable format with format hh:mm:ss.zzz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void localTimeExample() {
LocalTime now = LocalTime.now();
System.out.println("Current Time = " + now);
LocalTime customTime = LocalTime.of(5, 30, 0,0);
System.out.println("My custom time = " + customTime);
LocalTime customTime2 = LocalTime.parse("05:45:00.000");
System.out.println("My custom time2 = " + customTime2);
LocalTime klTimeNow = LocalTime.now(ZoneId.of("Asia/Kuala_Lumpur"));
System.out.println("Current Time in Kuala Lumpur = " + klTimeNow);
LocalTime tokyoTimeNow = LocalTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current Time in Tokyo = " + tokyoTimeNow);
LocalTime chicagoTimeNow = LocalTime.now(ZoneId.of("America/Chicago"));
System.out.println("Current Time in Chicago = " + chicagoTimeNow);
}
Output:
1
2
3
4
5
6
Current Time = 15:50:09.394
My custom time = 05:30
My custom time2 = 05:45
Current Time in Kuala Lumpur = 15:50:09.394
Current Time in Tokyo = 16:50:09.394
Current Time in Chicago = 02:50:09.394
LocalDateTime
LocalTime
is a class that represents a combination of date and time with format yyyy-MM-dd-HH-mm-ss.zzz
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static void localDateTimeExample() {
LocalDateTime now = LocalDateTime.now();
System.out.println("Current Date Time = " + now);
LocalDateTime customDateTime = LocalDateTime.of(2020, 3,19,5, 30);
System.out.println("My custom Date Time = " + customDateTime);
LocalDateTime customDateTime2 = LocalDateTime.parse("2020-03-19T06:30:00");
System.out.println("My custom Date Time = " + customDateTime2);
LocalDateTime klDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Kuala_Lumpur"));
System.out.println("Current Date Time in Kuala Lumpur = " + klDateTimeNow);
LocalDateTime tokyoDateTimeNow = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current Date Time in Tokyo = " + tokyoDateTimeNow);
LocalDateTime chicagoDateTimeNow = LocalDateTime.now(ZoneId.of("America/Chicago"));
System.out.println("Current Date Time in Chicago = " + chicagoDateTimeNow);
}
Output:
1
2
3
4
5
6
Current Date Time = 2021-07-19T16:29:28.399
My custom Date Time = 2020-03-19T05:30
My custom Date Time = 2020-03-19T06:30
Current Date Time in Kuala Lumpur = 2021-07-19T16:29:28.400
Current Date Time in Tokyo = 2021-07-19T17:29:28.400
Current Date Time in Chicago = 2021-07-19T03:29:28.400
OffsetDateTime
OffsetDateTime
class represents a date time with an offset from UTC/Greenwich in the ISO-8601 calendar system.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static void offsetDateTimeExample() {
OffsetDateTime now = OffsetDateTime.now();
System.out.println("Current Offset Date Time = " + now);
OffsetDateTime customDateTime = OffsetDateTime.of(2020, 3,19,5, 30,0,0, ZoneOffset.of("+08:00"));
System.out.println("My custom Offset Date Time = " + customDateTime);
OffsetDateTime customDateTime2 = OffsetDateTime.parse("2020-03-19T06:30:00+08:00");
System.out.println("My custom Offset Date Time = " + customDateTime2);
OffsetDateTime klDateTimeNow = OffsetDateTime.now(ZoneId.of("Asia/Kuala_Lumpur"));
System.out.println("Current Offset Date Time in Kuala Lumpur = " + klDateTimeNow);
OffsetDateTime tokyoDateTimeNow = OffsetDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Current Offset Date Time in Tokyo = " + tokyoDateTimeNow);
OffsetDateTime chicagoDateTimeNow = OffsetDateTime.now(ZoneId.of("America/Chicago"));
System.out.println("Current Offset Date Time in Chicago = " + chicagoDateTimeNow);
}
Output:
1
2
3
4
5
6
Current Offset Date Time = 2021-07-19T16:56:00.466+08:00
My custom Offset Date Time = 2020-03-19T05:30+08:00
My custom Offset Date Time = 2020-03-19T06:30+08:00
Current Offset Date Time in Kuala Lumpur = 2021-07-19T16:56:00.482+08:00
Current Offset Date Time in Tokyo = 2021-07-19T17:56:00.482+09:00
Current Offset Date Time in Chicago = 2021-07-19T03:56:00.484-05:00
Instant
Instant
is a class represents a date time with machine readable time format, Unix timestamp.
1
2
3
4
5
6
7
8
private static void instantExample() {
Instant now = Instant.now();
System.out.println("Current timestamp = " + now);
Instant currentInstant = Instant.ofEpochMilli(System.currentTimeMillis());.
System.out.println(" = " + currentInstant);
}
1
2
Current timestamp = 2021-07-19T09:28:22.691Z
Current timestamp2 2021-07-19T09:28:22.758Z
Date Time Utilities API
The utilities API is the thing I most appreciate after migrating to Java 8 date time API. It provides lots of utility functions that cover most of the day to day operation scenario.
Date
For class that cover date such as LocalDate
and LocalDateTime
does provide some useful utility method that ease our job.
Utility methods to :
- plus / minus day, month, year
- compare two dates object
- check if date is leap year
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void dateUtilityMethods() {
LocalDateTime now1 = LocalDateTime.now();
System.out.println("now = " + now1);
LocalDateTime twoDaysLater = now1.plusDays(2);
System.out.println("two days later = " + twoDaysLater);
LocalDate now = LocalDate.now();
System.out.println("now = " + now);
LocalDate tomorrow = now.plusDays(1);
System.out.println("Tomorrow = " + tomorrow);
LocalDate twoWeekAfter = now.plusWeeks(2);
System.out.println("2 week after today = " + tomorrow);
LocalDate nextMonth = now.plus(1, ChronoUnit.MONTHS);
System.out.println("1 month after today = " + tomorrow);
DayOfWeek dayOfWeek = now.getDayOfWeek();
System.out.println("Day of week = " + dayOfWeek.name());
int dayOfYear = now.getDayOfYear();
System.out.println("Day of year = " + dayOfYear);
boolean isLeapYear = now.isLeapYear();
System.out.println("Is Leap Year = " + isLeapYear);
boolean notBefore = now.isBefore(tomorrow);
System.out.println(now + "is before " + tomorrow + " = " + notBefore);
boolean isAfter = nextMonth.isAfter(now);
System.out.println(nextMonth + "is after " + now + " = " + isAfter);
}
Output:
1
2
3
4
5
6
7
8
9
10
11
now = 2021-07-19T19:15:32.087
Truncate until days = 2021-07-19T00:00
now = 2021-07-19
Tomorrow = 2021-07-20
2 week after today = 2021-07-20
1 month after today = 2021-07-20
Day of week = MONDAY
Day of year = 200
Is Leap Year = false
2021-07-19is before 2021-07-20 = true
2021-08-19is after 2021-07-19 = true
In Java 8 package also include a utility class, Period
that:
-
serve as a date unit for add / minus operation
-
compare two dates object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private static void periodUtilityClass() {
LocalDate now = LocalDate.now();
System.out.println("now = " + now);
Period fiveDay = Period.ofDays(5);
LocalDate fiveDaysLater = now.plus(fiveDay);
System.out.println("5 days later = " + fiveDaysLater);
Period twoMonth = Period.ofMonths(2);
LocalDate twoMonthBefore = now.minus(twoMonth);
System.out.println("2 months before = " + twoMonthBefore);
Period oneYear = Period.ofYears(1);
LocalDate oneYearAfter = now.plus(oneYear);
System.out.println("1 year after = " + oneYearAfter);
int daysBetween = Period.between(now, fiveDaysLater).getDays();
System.out.println("Days difference between " + now + " and " + fiveDaysLater + " = " + daysBetween);
int daysDiff1 = Period.between(now, oneYearAfter).getDays();
System.out.println("Days difference between " + now + " and " + oneYearAfter + " = " + daysDiff1);
long daysDiff2 = ChronoUnit.DAYS.between(now, oneYearAfter);
System.out.println("Days difference between " + now + " and " + oneYearAfter + " = " + daysDiff2);
}
** Note: betwee()
only calculates specific date unit that we request. e.g. if the date difference is exactly 1 year, the returned result will be 0. (see daysDiff1
and dayDiff2
in the example)
Output:
1
2
3
4
5
6
7
now = 2021-07-19
5 days later = 2021-07-24
2 months before = 2021-05-19
1 year after = 2022-07-19
Days difference between 2021-07-19 and 2021-07-24 = 5
Days difference between 2021-07-19 and 2022-07-19 = 0
Days difference between 2021-07-19 and 2022-07-19 = 365
Time
For class that cover time such as LocalTime
and LocalDateTime
also provide some useful utilities method that ease our job.
Utilities methods to :
- plus / minus hour, minutes, second, nanosecond
- compare two times object
- min / max value. This will be useful when constructing database queries to find records within a given span of time.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private static void timeUtilityMethods() {
LocalDateTime now1 = LocalDateTime.now();
System.out.println("now = " + now1);
LocalDateTime oneHourBefore = now1.plusHours(1);
System.out.println("one hour before" + oneHourBefore);
LocalDateTime twoDaysLater = now1.plusDays(2);
System.out.println("two days later = " + twoDaysLater);
LocalDateTime truncatedToHours = now1.truncatedTo(ChronoUnit.HOURS);
System.out.println("Truncate until hours = " + truncatedToHours);
LocalTime now = LocalTime.now();
System.out.println("now = " + now);
LocalTime twoHoursLater = now.plusHours(2);
System.out.println("2 hours later = " + twoHoursLater);
LocalTime thirtyBefore = now.minusMinutes(30);
System.out.println("30 minutes before = " + thirtyBefore);
LocalTime fourHoursBefore = now.minus(4, ChronoUnit.HOURS);
System.out.println("4 hours before = " + fourHoursBefore);
boolean isBefore = now.isBefore(twoHoursLater);
System.out.println(now + " is before " + twoHoursLater + " = " + isBefore);
boolean isAfter = twoHoursLater.isAfter(now);
System.out.println(twoHoursLater + " is after " + now + " = " + isAfter);
LocalTime maxTime = LocalTime.MAX;
System.out.println("Max Time = " + maxTime);
LocalTime minTime = LocalTime.MIN;
System.out.println("Min Time = " + minTime);
}
Output:
1
2
3
4
5
6
7
8
9
10
11
12
now = 2021-07-19T19:25:25.453
one hour before2021-07-19T20:25:25.453
two days later = 2021-07-21T19:25:25.453
Truncate until hours = 2021-07-19T19:00
now = 19:25:25.453
2 hours later = 21:25:25.453
30 minutes before = 18:55:25.453
4 hours before = 15:25:25.453
19:25:25.453 is before 21:25:25.453 = true
21:25:25.453 is after 19:25:25.453 = true
Max Time = 23:59:59.999999999
Min Time = 00:00
In Java 8 package also include a utility class, Duration
that:
- serve as a time unit
- compare two time object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static void durationUtilityClass() {
LocalTime now = LocalTime.now();
System.out.println("now = " + now);
Duration twoHours = Duration.ofHours(2);
LocalTime twoHoursLater = now.plus(twoHours);
System.out.println("2 hours later = " + twoHoursLater);
Duration fortyMinutes = Duration.ofMinutes(40);
LocalTime fortyMinutesBefore = now.minus(fortyMinutes);
System.out.println("40 minutes before = " + fortyMinutesBefore);
Duration thirtySeconds = Duration.ofSeconds(30);
LocalTime thirtySecondsLater = now.plus(thirtySeconds);
System.out.println("30 seconds later = " + thirtySecondsLater);
long hoursDiff = Duration.between(now, twoHoursLater).toHours();
System.out.println("Hours difference between " + now + " and " + twoHoursLater + " = " + hoursDiff);
long secondsDiff1 = Duration.between(now, fortyMinutesBefore).getSeconds();
System.out.println("Second difference between " + now + " and " + fortyMinutesBefore + " = " + secondsDiff1);
}
Output:
1
2
3
4
5
6
now = 19:31:34.322
2 hours later = 21:31:34.322
40 minutes before = 18:51:34.322
30 seconds later = 19:32:04.322
Hours difference between 19:31:34.322 and 21:31:34.322 = 2
Second difference between 19:31:34.322 and 18:51:34.322 = -2400
Parsing and Formatting
Java 8 provide a formatter class, DateTimeFormatter
that can be used to format all the Java 8 date time API.
Parsing
1
2
3
4
5
6
7
8
9
10
11
12
private static void parseExample(){
LocalDate localDate = LocalDate.parse("2020/01/19", DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(localDate);
LocalTime localTime = LocalTime.parse("08-55", DateTimeFormatter.ofPattern("HH-mm"));
System.out.println(localTime);
LocalDateTime localDateTime = LocalDateTime.parse("27::05::2020 21::39::48",
DateTimeFormatter.ofPattern("d::MM::uuuu HH::mm::ss"));
System.out.println(localDateTime);
}
Output:
1
2
3
2020-05-27T21:39:48
2020-01-19
08:55
Formatting
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static void formatExample() {
System.out.println("Local Date");
LocalDate localDate = LocalDate.now();
System.out.println(localDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
System.out.println(localDate.format(DateTimeFormatter.ofPattern("dd-MMM-yyyy")));
System.out.println(localDate.format(DateTimeFormatter.ISO_DATE));
System.out.println("\nLocal Time");
LocalTime localTime = LocalTime.now();
System.out.println(localTime.format(DateTimeFormatter.ofPattern("hh:mm:ss")));
System.out.println(localTime.format(DateTimeFormatter.ofPattern("hh:mm")));
System.out.println(localTime.format(DateTimeFormatter.ISO_TIME));
System.out.println("\nLocal Date Time");
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
System.out.println(localDateTime.format(DateTimeFormatter.ISO_DATE_TIME));
}
Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
Local Date
19/07/2021
19-Jul-2021
2021-07-19
Local Time
09:09:12
09:09
21:09:12.845
Local Date Time
19::Jul::2021 21::09::12
2021-07-19T21:09:12.847
Legacy API Support
Adopting Java 8 date time API is pretty straightforward if you are creating your application from scratch. Often time, migrate existing application to use new API is a nightmare. Fortunately, Java 8 release included some method in the legacy API to assist developer perform the migration.
In java.util.Date
there is a method toInstant()
that will convert it to Instant
object.
1
2
3
4
5
6
7
8
private static void legacySupport() {
Instant instant = new Date().toInstant();
LocalDateTime now = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
System.out.println("Date from legacy API = " + now);
Date now2 = Date.from(Instant.now());
System.out.println("Date from new API = " + now2);
}
Output:
1
2
Date from legacy API = 2021-07-19T21:21:29.524
Date from new API = Mon Jul 19 21:21:29 SGT 2021
Conclusion
The new Java 8 API is definitely a thumbs up feature that ease the development. I will definitely recommend you to start using the new API, if you haven’t done so.
Check out the source code here.