Appearance
LocalDateTime
Since Java 8, the java.time
package provides a new date and time API, primarily involving the following types:
- Local date and time:
LocalDateTime
,LocalDate
,LocalTime
- Date and time with time zone:
ZonedDateTime
- Instant:
Instant
- Time zone:
ZoneId
,ZoneOffset
- Time interval:
Duration
- A new set of formatting types,
DateTimeFormatter
, to replaceSimpleDateFormat
.
Compared to the old API, the new API strictly distinguishes between instant, local date, local time, and date-time with time zone, making date and time calculations much more convenient.
Additionally, the new API corrects unreasonable constant designs in the old API:
- The range of
Month
is represented as 1 to 12 for January to December. - The range of
Week
is represented as 1 to 7 for Monday to Sunday.
Finally, almost all types in the new API are immutable (similar to String
), so they can be used safely without worrying about modifications.
Let's first look at the most commonly used LocalDateTime
, which represents a local date and time:
java
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDate d = LocalDate.now(); // Current date
LocalTime t = LocalTime.now(); // Current time
LocalDateTime dt = LocalDateTime.now(); // Current date and time
System.out.println(d); // Printed strictly according to ISO 8601 format
System.out.println(t); // Printed strictly according to ISO 8601 format
System.out.println(dt); // Printed strictly according to ISO 8601 format
}
}
The local date and time obtained through now()
is always returned in the current default time zone. Unlike the old API, LocalDateTime
, LocalDate
, and LocalTime
are printed strictly according to the date and time format specified by ISO 8601 by default.
There is a small issue with the above code: when obtaining the three types, a slight delay occurs while executing each line of code. Therefore, the dates and times of the three types may not match (the milliseconds will likely differ). To ensure that the same instant's date and time are obtained, you can rewrite it as follows:
java
LocalDateTime dt = LocalDateTime.now(); // Current date and time
LocalDate d = dt.toLocalDate(); // Convert to current date
LocalTime t = dt.toLocalTime(); // Convert to current time
Conversely, you can create LocalDateTime
by specifying a date and time using the of()
method:
java
// Specify date and time:
LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, note that 11=November
LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
Since it follows the strict ISO 8601 format, you can convert a string to LocalDateTime
by passing a standard format:
java
LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
LocalDate d = LocalDate.parse("2019-11-19");
LocalTime t = LocalTime.parse("15:16:17");
Note that the separator for date and time in ISO 8601 is T
. The standard formats are as follows:
- Date:
yyyy-MM-dd
- Time:
HH:mm:ss
- Time with milliseconds:
HH:mm:ss.SSS
- Date and time:
yyyy-MM-dd'T'HH:mm:ss
- Date and time with milliseconds:
yyyy-MM-dd'T'HH:mm:ss.SSS
DateTimeFormatter
If you need to customize the output format or parse a non-ISO 8601 format string into LocalDateTime
, you can use the new DateTimeFormatter
:
java
import java.time.*;
import java.time.format.*;
public class Main {
public static void main(String[] args) {
// Custom formatting:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dtf.format(LocalDateTime.now()));
// Parse using custom format:
LocalDateTime dt2 = LocalDateTime.parse("2019/11/30 15:16:17", dtf);
System.out.println(dt2);
}
}
LocalDateTime
provides a very simple method for adding or subtracting dates and times using method chaining:
java
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// Add 5 days and subtract 3 hours:
LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
System.out.println(dt2); // 2019-10-31T17:30:59
// Subtract 1 month:
LocalDateTime dt3 = dt2.minusMonths(1);
System.out.println(dt3); // 2019-09-30T17:30:59
}
}
Notice that adjusting the month automatically adjusts the date. For example, subtracting 1 month from 2019-10-31 results in 2019-09-30, as September does not have 31 days.
To adjust the date and time, use the withXxx()
methods, such as withHour(15)
to change 10:11:12 to 15:11:12:
- Adjust year:
withYear()
- Adjust month:
withMonth()
- Adjust day:
withDayOfMonth()
- Adjust hour:
withHour()
- Adjust minute:
withMinute()
- Adjust second:
withSecond()
Example code is as follows:
java
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// Change day to the 31st:
LocalDateTime dt2 = dt.withDayOfMonth(31);
System.out.println(dt2); // 2019-10-31T20:30:59
// Change month to September:
LocalDateTime dt3 = dt2.withMonth(9);
System.out.println(dt3); // 2019-09-30T20:30:59
}
}
It is also important to note that adjusting the month will correspondingly adjust the date, meaning that when adjusting the month of 2019-10-31 to September, the date automatically changes to the 30th.
In fact, LocalDateTime
also has a generic with()
method that allows for more complex calculations. For example:
java
import java.time.*;
import java.time.temporal.*;
public class Main {
public static void main(String[] args) {
// The first day of this month at 00:00:
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
System.out.println(firstDay);
// The last day of this month:
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDay);
// The first day of next month:
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(nextMonthFirstDay);
// The first Monday of this month:
LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstWeekday);
}
}
The new API can easily handle calculations such as finding the first Sunday of a month.
To determine the chronological order of two LocalDateTime
instances, you can use the isBefore()
and isAfter()
methods, which are similar for LocalDate
and LocalTime
:
java
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
System.out.println(now.isBefore(target));
System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19)));
System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00")));
}
}
Note that LocalDateTime
cannot be converted to a timestamp because it has no time zone, which makes it impossible to determine a specific moment. The ZonedDateTime
we will introduce later combines LocalDateTime
with a time zone, allowing it to be converted to a timestamp represented as long
.
Duration and Period
Duration
represents the time interval between two moments, while Period
represents the number of days between two dates:
java
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
Duration d = Duration.between(start, end);
System.out.println(d); // PT1235H10M30S
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
System.out.println(p); // P1M21D
}
}
Note that the difference between two LocalDateTime
instances is represented using Duration
, similar to PT1235H10M30S
, which indicates 1235 hours, 10 minutes, and 30 seconds. The difference between two LocalDate
instances is represented using Period
, such as P1M21D
, indicating 1 month and 21 days.
Both Duration
and Period
formats conform to ISO 8601, using the structure P...T...
, where P...
indicates the date interval and T...
indicates the time interval. If it is in the format PT...
, it signifies only a time interval. You can also create Duration
directly using ofXxx()
or parse()
methods:
java
Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes
You may notice that the Java 8 java.time
API closely resembles the open-source Joda Time. This similarity is due to the excellent design of Joda Time, which led the JDK team to invite Joda Time's author, Stephen Colebourne, to help design the java.time
API.
Summary
Java 8 introduced a new date and time API that consists of immutable classes, which are formatted and parsed by default according to the ISO 8601 standard.
Using LocalDateTime
, you can easily add or subtract dates and times, or adjust them, and it always returns a new object.
Methods like isBefore()
and isAfter()
help determine the chronological order of dates and times.
Duration
and Period
can represent the "interval differences" between two dates and times.