Manipulate date, time, duration, period, instant and time-zone objects including daylight saving time using Date-Time API.
Java 8 introduced a new Date/Time API in the java.time
package. The classes that belong to this API are immutable and thread-safe.
In this section, we’ll review the following classes:
LocalDate: Represents a date with the year, month, and day of the month information. For example, 2025-08-25
.
LocalTime: Represents a time with hour, minute, second, and nanosecond information. For example, 13:21:05.123456789
.
LocalDateTime: A combination of the above. For example, 2025-08-25T13:21:05.123456789
.
Instant: Represents a single point in time with seconds and nanoseconds. For example, 1970-01-01T00:00:00Z (seconds since epoch: 923456789, nanoseconds: 186,054,812)
.
Period: Represents an amount of time in terms of years, months, and days. For example, 5 years, 2 months, and 9 days
.
Duration: Represents an amount of time in terms of seconds and nanoseconds. For example, 12.87656 seconds
.
With the exception of Instant
, these classes don’t store or represent a time zone.
Also, LocalDate
, LocalTime
, LocalDateTime
, and Instant
implement the interface java.time.temporal.Temporal
, so they all have similar methods. Period
and Duration
implement the interface java.time.temporal.TemporalAmount
, which also makes them very similar.
Here’s a diagram to help you visualize the classes of the Date/Time API:
┌─────────────────────────────────────────────────────────┐
│ Java Date/Time API │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ LocalDate │ │ LocalTime │ │LocalDateTime│ │
│ │ (Date only) │ │ (Time only) │ │(Date + Time)│ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ Instant │ │
│ │ (Time-stamp)│ │
│ └──────┬──────┘ │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ │ │ │
│ ┌─────┴─────┐ ┌─────┴─────┐ │
│ │ Period │ │ Duration │ │
│ │(Date-based│ │(Time-based│ │
│ │ amount) │ │ amount) │ │
│ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
LocalDate
ClassThe key to learning how to use this class is to understand that it holds the year, month, day, and derived information of a date. All of its methods use this information or have a version to work with each of these components.
The following are the most important methods of this class.
To create an instance, we can use the static method of
:
// With year (-999999999 to 999999999), month (1 to 12), day of the month (1 - 31)
LocalDate newYear2001 = LocalDate.of(2001, 1, 1);
// This version uses the enum java.time.Month
LocalDate newYear2002 = LocalDate.of(2002, Month.JANUARY, 1);
Notice that, unlike java.util.Date
, months start from one. If you try to create a date with invalid values (like February 29 on a non-leap year), an exception will be thrown. For today’s date, use now()
:
LocalDate today = LocalDate.now();
Once we have an instance of LocalDate
, we can get the year, the month, and the day with methods like the following:
int year = today.getYear();
int month = today.getMonthValue();
Month monthAsEnum = today.getMonth(); // As an enum: JANUARY, FEBRUARY, etc.
int dayYear = today.getDayOfYear();
int dayMonth = today.getDayOfMonth();
DayOfWeek dayWeekEnum = today.getDayOfWeek(); // As an enum: MONDAY, TUESDAY, etc.
We can also use the get
method:
int get(java.time.temporal.TemporalField field); // value as int
long getLong(java.time.temporal.TemporalField field); // value as long
This method takes an implementation of the interface java.time.temporal.TemporalField
to access a specific field of a date. java.time.temporal.ChronoField
is an enumeration that implements this interface, so we can have, for example:
int year2 = today.get(ChronoField.YEAR);
int month2 = today.get(ChronoField.MONTH_OF_YEAR);
int dayYear2 = today.get(ChronoField.DAY_OF_YEAR);
int dayMonth2 = today.get(ChronoField.DAY_OF_MONTH);
int dayWeek = today.get(ChronoField.DAY_OF_WEEK);
long dayEpoch = today.getLong(ChronoField.EPOCH_DAY);
The supported values for ChronoField
are:
DAY_OF_WEEK
ALIGNED_DAY_OF_WEEK_IN_MONTH
ALIGNED_DAY_OF_WEEK_IN_YEAR
DAY_OF_MONTH
DAY_OF_YEAR
EPOCH_DAY
ALIGNED_WEEK_OF_MONTH
ALIGNED_WEEK_OF_YEAR
MONTH_OF_YEAR
PROLEPTIC_MONTH
YEAR_OF_ERA
YEAR
ERA
Using an unsupported value will throw an exception. The same is true when getting a value that doesn’t fit into an int
with get(TemporalField)
.
To compare a LocalDate
against another instance, we have three methods and another one for leap years:
boolean after = newYear2001.isAfter(newYear2002); // false
boolean before = newYear2001.isBefore(newYear2002); // true
boolean equal = newYear2001.equals(newYear2002); // false
boolean leapYear = newYear2001.isLeapYear(); // false
Once an instance of this class is created, it cannot be modified, but we can create another instance from an existing one.
One way is by using the with()
method and its variations:
LocalDate newYear2003 = newYear2001.with(ChronoField.YEAR, 2003);
LocalDate newYear2004 = newYear2001.withYear(2004);
LocalDate december2001 = newYear2001.withMonth(12);
LocalDate february2001 = newYear2001.withDayOfYear(32);
// Since these methods return a new instance, we can chain them!
LocalDate xmas2001 = newYear2001.withMonth(12).withDayOfMonth(25);
Another way is by adding or subtracting years, months, days, or even weeks:
// Adding
LocalDate newYear2005 = newYear2001.plusYears(4);
LocalDate march2001 = newYear2001.plusMonths(2);
LocalDate january15_2001 = newYear2001.plusDays(14);
LocalDate lastWeekJanuary2001 = newYear2001.plusWeeks(3);
LocalDate newYear2006 = newYear2001.plus(5, ChronoUnit.YEARS);
// Subtracting
LocalDate newYear2000 = newYear2001.minusYears(1);
LocalDate nov2000 = newYear2001.minusMonths(2);
LocalDate dec30_2000 = newYear2001.minusDays(2);
LocalDate lastWeekDec2000 = newYear2001.minusWeeks(1);
LocalDate newYear1999 = newYear2001.minus(2, ChronoUnit.YEARS);
Notice that the plus and minus methods take a java.time.temporal.ChronoUnit
enumeration, which is different from java.time.temporal.ChronoField
. The supported values are:
DAYS
WEEKS
MONTHS
YEARS
DECADES
CENTURIES
MILLENNIA
ERAS
Finally, the method toString()
returns the date in the format uuuu-MM-dd
:
System.out.println(newYear2001.toString()); // Prints 2001-01-01
LocalTime
ClassThe key to learning how to use this class is to keep in mind that it holds the hour, minutes, seconds, and nanoseconds. All of its methods use this information or have a version to work with each of them.
The following are the most important methods of this class. As you can see, they are the same (or very similar) methods as LocalDate
, adapted to work with time (hours, minutes, seconds) instead of date (days, months, years).
To create an instance, we can use the static
method of
:
// With hour (0-23) and minutes (0-59)
LocalTime fiveThirty = LocalTime.of(5, 30);
// With hour, minutes, and seconds (0-59)
LocalTime noon = LocalTime.of(12, 0, 0);
// With hour, minutes, seconds, and nanoseconds (0-999_999_999)
LocalTime almostMidnight = LocalTime.of(23, 59, 59, 999_999_999);
If you try to create a time with an invalid value (like LocalTime.of(24, 0)
), an exception will be thrown. To get the current time, use now()
:
LocalTime now = LocalTime.now();
Once we have an instance of LocalTime
, we can get the hour, the minutes, and other information with methods like the following:
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nanosecond = now.getNano();
We can also use the get()
method:
int value = now.get(java.time.temporal.TemporalField field); // value as int
long valueLong = now.getLong(java.time.temporal.TemporalField field); // value as long
Just like in the case of LocalDate
, we can have, for example:
int hourAMPM = now.get(ChronoField.HOUR_OF_AMPM); // 0 - 11
int hourDay = now.get(ChronoField.HOUR_OF_DAY); // 0 - 23
int minuteDay = now.get(ChronoField.MINUTE_OF_DAY); // 0 - 1,439
int minuteHour = now.get(ChronoField.MINUTE_OF_HOUR); // 0 - 59
int secondDay = now.get(ChronoField.SECOND_OF_DAY); // 0 - 86,399
int secondMinute = now.get(ChronoField.SECOND_OF_MINUTE); // 0 - 59
long nanoDay = now.getLong(ChronoField.NANO_OF_DAY); // 0-86_399_999_999
int nanoSecond = now.get(ChronoField.NANO_OF_SECOND); // 0-999_999_999
The supported values for ChronoField
are:
NANO_OF_SECOND
NANO_OF_DAY
MICRO_OF_SECOND
MICRO_OF_DAY
MILLI_OF_SECOND
MILLI_OF_DAY
SECOND_OF_MINUTE
SECOND_OF_DAY
MINUTE_OF_HOUR
MINUTE_OF_DAY
HOUR_OF_AMPM
CLOCK_HOUR_OF_AMPM
HOUR_OF_DAY
CLOCK_HOUR_OF_DAY
AMPM_OF_DAY
Using a different value will throw an exception. The same is true when getting a value that doesn’t fit into an int
using get(TemporalField)
.
To check a time object against another one, we have three methods:
boolean after = fiveThirty.isAfter(noon); // false
boolean before = fiveThirty.isBefore(noon); // true
boolean equal = noon.equals(almostMidnight); // false
Like LocalDate
, once an instance of LocalTime
is created we cannot modify it, but we can create another instance from an existing one.
One way is through the with
method and its versions:
LocalTime ten = noon.with(ChronoField.HOUR_OF_DAY, 10);
LocalTime eight = noon.withHour(8);
LocalTime twelveThirty = noon.withMinute(30);
LocalTime thirtyTwoSeconds = noon.withSecond(32);
// Since these methods return a new instance, we can chain them!
LocalTime secondsNano = noon.withSecond(20).withNano(999_999);
Of course, another way is by adding or subtracting hours, minutes, seconds, or nanoseconds:
// Adding
LocalTime sixThirty = fiveThirty.plusHours(1);
LocalTime fiveForty = fiveThirty.plusMinutes(10);
LocalTime plusSeconds = fiveThirty.plusSeconds(14);
LocalTime plusNanos = fiveThirty.plusNanos(99_999_999);
LocalTime sevenThirty = fiveThirty.plus(2, ChronoUnit.HOURS);
// Subtracting
LocalTime fourThirty = fiveThirty.minusHours(1);
LocalTime fiveTen = fiveThirty.minusMinutes(20);
LocalTime minusSeconds = fiveThirty.minusSeconds(2);
LocalTime minusNanos = fiveThirty.minusNanos(1);
LocalTime fiveTwenty = fiveThirty.minus(10, ChronoUnit.MINUTES);
Notice that the plus
and minus
versions take a java.time.temporal.ChronoUnit
enumeration, which is different from java.time.temporal.ChronoField
. The supported values are:
NANOS
MICROS
MILLIS
SECONDS
MINUTES
HOURS
HALF_DAYS
Finally, the method toString()
returns the time in the format HH:mm:ss.SSSSSSSSS
, omitting the parts with value zero (for example, just returning HH:mm
if it has zero seconds/nanoseconds):
System.out.println(fiveThirty.toString()); // Prints 05:30
This should cover all the necessary corrections and improvements for the provided text.
LocalDateTime
ClassThe key to learning how to use this class is to remember that it combines LocalDate
and LocalTime
classes.
It represents both a date and a time, with information like year, month, day, hours, minutes, seconds, and nanoseconds. Other fields, such as day of the year, day of the week, and week of year can also be accessed.
To create an instance, we can use either the static method of()
or from a LocalDate
or LocalTime
instance:
// Setting seconds and nanoseconds to zero
LocalDateTime dt1 = LocalDateTime.of(2024, 9, 19, 14, 5);
// Setting nanoseconds to zero
LocalDateTime dt2 = LocalDateTime.of(2024, 9, 19, 14, 5, 20);
// Setting all fields
LocalDateTime dt3 = LocalDateTime.of(2024, 9, 19, 14, 5, 20, 9);
// Assuming this date
LocalDate date = LocalDate.now();
// And this time
LocalTime time = LocalTime.now();
// Combine the above date with the given time like this
LocalDateTime dt4 = date.atTime(14, 30, 59, 999999);
// Or this
LocalDateTime dt5 = date.atTime(time);
// Combine this time with the given date. Notice that LocalTime
// only has this method to be combined with a LocalDate
LocalDateTime dt6 = time.atDate(date);
If you try to create an instance with an invalid value or date, an exception will be thrown. To get the current date/time use now()
:
LocalDateTime now = LocalDateTime.now();
Once we have an instance of LocalDateTime
, we can get the information with the methods we know from LocalDate
and LocalTime
, such as:
int year = now.getYear();
int dayYear = now.getDayOfYear();
int hour = now.getHour();
int minute = now.getMinute();
We can also use the get()
method:
int get(java.time.temporal.TemporalField field)
long getLong(java.time.temporal.TemporalField field)
For example:
int month = now.get(ChronoField.MONTH_OF_YEAR);
int minuteHour = now.get(ChronoField.MINUTE_OF_HOUR);
The supported values for ChronoField
are:
NANO_OF_SECOND
NANO_OF_DAY
MICRO_OF_SECOND
MICRO_OF_DAY
MILLI_OF_SECOND
MILLI_OF_DAY
SECOND_OF_MINUTE
SECOND_OF_DAY
MINUTE_OF_HOUR
MINUTE_OF_DAY
HOUR_OF_AMPM
CLOCK_HOUR_OF_AMPM
HOUR_OF_DAY
CLOCK_HOUR_OF_DAY
AMPM_OF_DAY
DAY_OF_WEEK
ALIGNED_DAY_OF_WEEK_IN_MONTH
ALIGNED_DAY_OF_WEEK_IN_YEAR
DAY_OF_MONTH
DAY_OF_YEAR
EPOCH_DAY
ALIGNED_WEEK_OF_MONTH
ALIGNED_WEEK_OF_YEAR
MONTH_OF_YEAR
PROLEPTIC_MONTH
YEAR_OF_ERA
YEAR
ERA
Using a different value will throw an exception. The same is true when getting a value that doesn’t fit into an int
with get(TemporalField)
.
To check a LocalDateTime
object against another one, we have three methods:
boolean after = now.isAfter(dt1); // true
boolean before = now.isBefore(dt1); // false
boolean equal = now.equals(dt1); // false
Once an instance of LocalTime
is created, we cannot modify it, but we can create another instance from an existing one.
One way is through the with method and its versions:
LocalDateTime dt7 = now.with(ChronoField.HOUR_OF_DAY, 10);
LocalDateTime dt8 = now.withMonth(8);
// Since these methods return a new instance, we can chain them!
LocalDateTime dt9 = now.withYear(2013).withMinute(0);
Another way is by adding or subtracting years, months, days, weeks, hours, minutes, seconds, or nanoseconds:
// Adding
LocalDateTime dt10 = now.plusYears(4);
LocalDateTime dt11 = now.plusWeeks(3);
LocalDateTime dt12 = now.plus(2, ChronoUnit.HOURS);
// Subtracting
LocalDateTime dt13 = now.minusMonths(2);
LocalDateTime dt14 = now.minusNanos(1);
LocalDateTime dt15 = now.minus(10, ChronoUnit.SECONDS);
In this case, the supported values for ChronoUnit
are:
NANOS
MICROS
MILLIS
SECONDS
MINUTES
HOURS
HALF_DAYS
DAYS
WEEKS
MONTHS
YEARS
DECADES
CENTURIES
MILLENNIA
ERAS
Finally, the method toString()
returns the date-time in the format uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS
, omitting the parts with value zero, for example:
System.out.println(dt1.toString()); // Prints 2024-09-19T14:05
Instant
ClassAlthough in practical terms, a LocalDateTime
instance represents an instant in the timeline, there is another class that may be more appropriate.
The java.time.Instant
class represents an instant in the number of seconds that have passed since the epoch, a convention used in UNIX/POSIX systems and set at midnight of January 1, 1970 UTC time.
From that date, time is measured in 86,400 seconds per day. This information is stored as a long
. The class also supports nanosecond precision, stored as an int
.
You can create an instance of this class with the following methods:
// Setting seconds
Instant fiveSecondsAfterEpoch = Instant.ofEpochSecond(5);
// Setting seconds and nanoseconds (can be negative)
Instant sixSecTwoNanBeforeEpoch = Instant.ofEpochSecond(-6, -2);
// Setting milliseconds after (can be before also) epoch
Instant fiftyMilliSecondsAfterEpoch = Instant.ofEpochMilli(50);
For the current instance of the system clock use:
Instant now = Instant.now();
Once we have an instance of Instant
, we can get the information with the following methods:
long seconds = now.getEpochSecond(); // Gets the seconds
int nanos1 = now.getNano(); // Gets the nanoseconds
// Gets the value as an int
int millis = now.get(ChronoField.MILLI_OF_SECOND);
// Gets the value as a long
long nanos2 = now.getLong(ChronoField.NANO_OF_SECOND);
The supported ChronoField
values are:
NANO_OF_SECOND
MICRO_OF_SECOND
MILLI_OF_SECOND
INSTANT_SECONDS
Using any other value will throw an exception. The same is true when getting a value that doesn’t fit into an int
using get(TemporalField)
.
To check an Instant
object against another one, we have three methods:
boolean after = now.isAfter(fiveSecondsAfterEpoch); // true
boolean before = now.isBefore(fiveSecondsAfterEpoch); // false
boolean equal = now.equals(fiveSecondsAfterEpoch); // false
Once an instance of this object is created, we cannot modify it, but we can create another instance from an existing one.
One way is by using the with
method:
Instant i1 = now.with(ChronoField.NANO_OF_SECOND, 10);
Another way is by adding or subtracting seconds, milliseconds, or nanoseconds:
// Adding
Instant i10 = now.plusSeconds(400);
Instant i11 = now.plusMillis(98622200);
Instant i12 = now.plusNanos(300013890);
Instant i13 = now.plus(2, ChronoUnit.MINUTES);
// Subtracting
Instant i14 = now.minusSeconds(2);
Instant i15 = now.minusMillis(1);
Instant i16 = now.minusNanos(1);
Instant i17 = now.minus(10, ChronoUnit.SECONDS);
The supported ChronoUnit
values are:
NANOS
MICROS
MILLIS
SECONDS
MINUTES
HOURS
HALF_DAYS
DAYS
Finally, the method toString()
returns the instance in the format uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS
, for example:
// Prints 1970-01-01T00:00:00.050Z
System.out.println(fiftyMilliSecondsAfterEpoch.toString());
Notice that it contains zone time information (Z
). This is because Instant
represents a point in time from the epoch of 1970-01-01Z
in the UTC zone time.
Period
ClassThe java.time.Period
class represents an amount of time in terms of years, months, and days.
You can create an instance of this class with the following methods:
// Setting years, months, days (can be negative)
Period period5y4m3d = Period.of(5, 4, 3);
// Setting days (can be negative), years and months will be zero
Period period2d = Period.ofDays(2);
// Setting months (can be negative), years and days will be zero
Period period2m = Period.ofMonths(2);
// Setting weeks (can be negative). The resulting period will
// be in days (1 week = 7 days). Years and months will be zero
Period period14d = Period.ofWeeks(2);
// Setting years (can be negative), days and months will be zero
Period period2y = Period.ofYears(2);
A Period
can also be thought of as the difference between two LocalDates
. Luckily, there’s a method that supports this concept:
LocalDate march2003 = LocalDate.of(2003, 3, 1);
LocalDate may2003 = LocalDate.of(2003, 5, 1);
Period dif = Period.between(march2003, may2003); // 2 months
The start date is included, but not the end date.
Be careful about how the date is calculated.
First, complete months are counted, and then the remaining number of days is calculated. The number of months is then split into years (1 year equals 12 months). A month is considered if the end day of the month is greater than or equal to the start day of the month.
The result of this method can be a negative period if the end is before the start (year, month, and day will have a negative sign).
Here are some examples:
// dif1 will be 1 year 2 months 2 days
Period dif1 = Period.between(LocalDate.of(2000, 2, 10), LocalDate.of(2001, 4, 12));
// dif2 will be 25 days
Period dif2 = Period.between(LocalDate.of(2013, 5, 9), LocalDate.of(2013, 6, 3));
// dif3 will be -2 years -3 days
Period dif3 = Period.between(LocalDate.of(2014, 11, 3), LocalDate.of(2012, 10, 31));
Once we have an instance of Period
, we can get the information with the following methods:
int days = period5y4m3d.getDays();
int months = period5y4m3d.getMonths();
int year = period5y4m3d.getYears();
long days2 = period5y4m3d.get(ChronoUnit.DAYS);
Notice that the get
method returns a long
type.
Also, the supported ChronoUnit
values are:
DAYS
MONTHS
YEARS
Using any other value will throw an exception.
Once an instance of Period
is created, we cannot modify it, but we can create another instance based on an existing one.
One way is by using the with
method and its versions:
Period period8d = period2d.withDays(8);
// Since these methods return a new instance, we can chain them!
Period period2y1m2d = period2d.withYears(2).withMonths(1);
Another way is by adding or subtracting years, months, or days:
// Adding
Period period9y4m3d = period5y4m3d.plusYears(4);
Period period5y7m3d = period5y4m3d.plusMonths(3);
Period period5y4m6d = period5y4m3d.plusDays(3);
Period period7y4m3d = period5y4m3d.plus(period2y);
// Subtracting
Period period5y4m1d = period5y4m3d.minusYears(2);
Period period5y3m3d = period5y4m3d.minusMonths(1);
Period period5y4m2d = period5y4m3d.minusDays(1);
Period period3y4m3d = period5y4m3d.minus(period2y);
The methods plus
and minus
take an implementation of the interface java.time.temporal.TemporalAmount
(another instance of Period
or an instance of Duration
).
Finally, the toString()
method returns the period in the format P<years>Y<months>M<days>D
, for example:
System.out.println(period5y4m3d.toString()); // Prints P5Y4M3D
A zero period will be represented as zero days, P0D
.
Duration
ClassThe java.time.Duration
class is similar to the Period
class, the only thing is that it represents an amount of time in terms of seconds and nanoseconds.
You can create an instance of this class with the following methods:
Duration oneDay = Duration.ofDays(1); // 1 day = 86400 seconds
Duration oneHour = Duration.ofHours(1); // 1 hour = 3600 seconds
Duration oneMin = Duration.ofMinutes(1); // 1 minute = 60 seconds
Duration tenSeconds = Duration.ofSeconds(10);
// Set seconds and nanoseconds (if they are outside the range
// 0 to 999,999,999, the seconds will be altered, like below)
Duration twoSeconds = Duration.ofSeconds(1, 1000000000);
// Seconds and nanoseconds are extracted from the passed millisecs
Duration oneSecondFromMillis = Duration.ofMillis(2);
// Seconds and nanoseconds are extracted from the passed nanos
Duration oneSecondFromNanos = Duration.ofNanos(1000000000);
Duration oneSecond = Duration.of(1, ChronoUnit.SECONDS);
Valid values of ChronoUnit
for the method Duration.of(long amount, TemporalUnit unit)
are:
NANOS
MICROS
MILLIS
SECONDS
MINUTES
HOURS
HALF_DAYS
DAYS
A Duration
can also be created as the difference between two implementations of the interface java.time.temporal.Temporal
, as long as they support seconds (and for more accuracy, nanoseconds), like LocalTime
, LocalDateTime
, and Instant
. So we can have something like this:
Duration diff = Duration.between(Instant.ofEpochSecond(123456789), Instant.ofEpochSecond(99999));
The result can be negative if the end is before the start. A negative duration is indicated by a negative sign in the seconds part. For example, a duration of -100 nanoseconds is stored as -1 second plus 999,999,900 nanoseconds.
If the objects are of different types, then the duration is calculated based on the type of the first object. This only works if the first argument is a LocalTime
and the second is a LocalDateTime
(because it can be converted to LocalTime
). Otherwise, an exception is thrown.
Once we have an instance of Duration
, we can get the information with the following methods:
// The nanoseconds part the duration, from 0 to 999,999,999
int nanos = oneSecond.getNano();
// The seconds part of the duration, positive or negative
long seconds = oneSecond.getSeconds();
// It supports SECONDS and NANOS. Other units throw an exception
long oneSec = oneSecond.get(ChronoUnit.SECONDS);
Note that the methods getSeconds()
and get(TemporalUnit)
return a long
type. Additionally, the latter only supports SECONDS
and NANOS
as arguments.
Once an instance of Duration
is created, we cannot modify it, but we can create another instance from an existing one. One way is to use the with
method and its versions:
Duration duration1sec8nan = oneSecond.withNanos(8);
Duration duration2sec1nan = oneSecond.withSeconds(2).withNanos(1);
Another way is by adding or subtracting days, hours, minutes, seconds, milliseconds, or nanoseconds:
// Adding
Duration plus4Days = oneSecond.plusDays(4);
Duration plus3Hours = oneSecond.plusHours(3);
Duration plus3Minutes = oneSecond.plusMinutes(3);
Duration plus3Seconds = oneSecond.plusSeconds(3);
Duration plus3Millis = oneSecond.plusMillis(3);
Duration plus3Nanos = oneSecond.plusNanos(3);
Duration plusAnotherDuration = oneSecond.plus(twoSeconds);
Duration plusChronoUnits = oneSecond.plus(1, ChronoUnit.DAYS);
// Subtracting
Duration minus4Days = oneSecond.minusDays(4);
Duration minus3Hours = oneSecond.minusHours(3);
Duration minus3Minutes = oneSecond.minusMinutes(3);
Duration minus3Seconds = oneSecond.minusSeconds(3);
Duration minus3Millis = oneSecond.minusMillis(3);
Duration minus3Nanos = oneSecond.minusNanos(3);
Duration minusAnotherDuration = oneSecond.minus(twoSeconds);
Duration minusChronoUnits = oneSecond.minus(1, ChronoUnit.DAYS);
Methods plus
and minus
take either another Duration
or a valid ChronoUnit
value (the same values used to create an instance).
Finally, the method toString()
returns the duration with the format PTnHnMnS
. Any fractional seconds are placed after a decimal point in the seconds section. If a section has a zero value, it’s omitted. For example:
2 days 4 minutes PT48H4M
45 seconds 99 milliseconds PT45.099S
If you want to work with time zone information, the Date/Time API has the following classes:
ZoneId: Represents the ID of time zone. For example, Asia/Tokyo
.
ZoneOffset: Represents a time zone offset. It’s a subclass of ZoneId
. For example, -06:00
.
ZonedDateTime: Represents a date/time with time zone information. For example, 2025-08-30T20:05:12.463-05:00[America/Mexico_City]
.
OffsetDateTime: Represents a date/time with an offset from UTC/Greenwich. For example, 2025-08-30T20:05:12.463-05:00
.
OffsetTime: Represents a time with an offset from UTC/Greenwich. For example, 20:05:12.463-05:00
.
Just like the classes in the previous section, these are located in the java.time
package and are immutable.
Here’s a diagram to visualize the zone-aware classes:
┌────────────────────────────────────────────────────────┐
│ Java Time Zone-Aware Classes │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ ZoneId │ │ ZoneOffset │ │
│ │ (Time zone) │ │(UTC offset) │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ └──────────────────┼────────────────┐ │
│ │ │ │
│ ┌──────┴──────┐ ┌──────┴───────┐ │
│ │ZonedDateTime│ │OffsetDateTime│ │
│ │(Date + Time │ │ (Date + Time │ │
│ │ + Time Zone)│ │ + UTC offset)│ │
│ └─────────────┘ └──────┬───────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │OffsetTime │ │
│ │ (Time + │ │
│ │UTC offset)│ │
│ └───────────┘ │
│ │
└────────────────────────────────────────────────────────┘
Key Points:
- ZoneId represents a time zone (like "America/New_York")
- ZoneOffset represents a fixed offset from UTC (like "+05:00")
- ZonedDateTime combines LocalDateTime with a ZoneId
- OffsetDateTime combines LocalDateTime with a ZoneOffset
- OffsetTime combines LocalTime with a ZoneOffset
ZoneId
and ZoneOffset
ClassesThe world is divided into time zones in which the same standard time is kept. By convention, a time zone is expressed as the number of hours different from the Coordinated Universal Time (UTC). Since the Greenwich Mean Time (GMT) and the Zulu time (Z), used in the military, have no offset from UTC, they’re often used as synonyms.
Java uses the Internet Assigned Numbers Authority (IANA) database of time zones, which keeps a record of all known time zones around the world and is updated many times per year.
Each time zone has an ID, represented by the class java.time.ZoneId
. There are three types of ID:
The first type just states the offset from UTC/GMT time. They are represented by the class ZoneOffset
and they consist of digits starting with +
or -
, for example, +02:00
.
The second type also states the offset from UTC/GMT time, but with one of the following prefixes: UTC, GMT, and UT, for example, UTC+11:00
. They are also represented by the class ZoneOffset
.
The third type is region-based. These IDs have the format area/city, for example, Europe/London
.
You can get all the available zone IDs with the static
method:
Set<String> getAvailableZoneIds()
For example, to print them to the console:
ZoneId.getAvailableZoneIds().stream().forEach(System.out::println);
To get the zone ID of your system, use the static
method:
ZoneId.systemDefault()
Under the hood, it uses java.util.TimeZone.getDefault()
to find the default time zone and converts it to a ZoneId
.
If you want to create a specific ZoneId
object use the method of()
:
ZoneId singaporeZoneId = ZoneId.of("Asia/Singapore");
This method parses the ID producing a ZoneOffset
or a ZoneRegion
(both extend from ZoneId
).
In fact, the above line produces a ZoneRegion
. A ZoneOffset
is returned if, for example, ID is Z
, or starts with +
or -
. For example:
ZoneId zoneId = ZoneId.of("Z"); // Z represents the zone ID for UTC
ZoneId zoneId2 = ZoneId.of("-2"); // -02:00
The rules for this method are:
Z
, the result is ZoneOffset.UTC
. Any other letter will throw an exception.+
or -
, the ID is parsed as a ZoneOffset
using ZoneOffset.of(String)
.GMT
, UTC
or UT
then the result is a ZoneId
with the same ID and rules equivalent to ZoneOffset.UTC
.UTC+
, UTC-
, GMT+
, GMT-
, UT+
or UT-
then the ID is split in two, with a two or three letter prefix and a suffix starting with the sign. The suffix is parsed as a ZoneOffset
. The result will be a ZoneId
with the specified prefix and the normalized offset ID.[A-Za-z][A-Za-z0-9~/._+-]+)
or is not found, an exception is thrown.Remember that a ZoneOffset
represents an offset, generally from UTC. This class has a lot more constructors than ZoneId
:
// The offset must be in the range of -18 to +18
ZoneOffset offsetHours = ZoneOffset.ofHours(1);
// The range is -18 to +18 for hours and 0 to ± 59 for minutes
// If the hours are negative, the minutes must be negative or zero
ZoneOffset offsetHrMin = ZoneOffset.ofHoursMinutes(1, 30);
// The range is -18 to +18 for hours and 0 to ± 59 for mins and secs
// If the hours are negative, mins and secs must be negative or zero
ZoneOffset offsetHrMinSe = ZoneOffset.ofHoursMinutesSeconds(1, 30, 0);
// The offset must be in the range -18:00 to +18:00
// Which corresponds to -64800 to +64800
ZoneOffset offsetTotalSeconds = ZoneOffset.ofTotalSeconds(3600);
// The range must be from +18:00 to -18:00
ZoneOffset offset = ZoneOffset.of("+01:30:00");
The formats accepted by the of()
method are:
If you pass an invalid format or an out-of-range value to any of these methods, an exception is thrown.
To get the value of the offset, you can use:
// Gets the offset as int
int offsetInt = offset.get(ChronoField.OFFSET_SECONDS);
// Gets the offset as long
long offsetLong= offset.getLong(ChronoField.OFFSET_SECONDS);
// Gets the offset in seconds
int offsetSeconds = offset.getTotalSeconds();
ChronoField.OFFSET_SECONDS
is the only accepted value of ChronoField
, so the three statements above return the same result. Other values throw an exception.
Anyway, once you have a ZoneId
object, you can use it to create a ZonedDateTime
instance.
ZonedDateTime
ClassA java.time.ZonedDateTime
object represents a point in time relative to a time zone.
A ZonedDateTime
object has three parts:
This means that it stores all date and time fields to a precision of nanoseconds, and a time zone with a zone offset.
Here’s an example:
2025-08-31 T08:45:20.000 +02:00[Africa/Cairo]
Where the parts are as follows:
Date | Time | Offset | Time zone |
---|---|---|---|
2025-08-31 |
T08:45:20.000 |
+02:00 |
[Africa/Cairo] |
Once you have a ZoneId
object, you can combine it with a LocalDate
, a LocalDateTime
, or an Instant
to transform it into ZonedDateTime
:
ZoneId australiaZone = ZoneId.of("Australia/Victoria");
LocalDate date = LocalDate.of(2020, 7, 3);
ZonedDateTime zonedDate = date.atStartOfDay(australiaZone);
LocalDateTime dateTime = LocalDateTime.of(2020, 7, 3, 9, 0);
ZonedDateTime zonedDateTime = dateTime.atZone(australiaZone);
Instant instant = Instant.now();
ZonedDateTime zonedInstant = instant.atZone(australiaZone);
Or using the of
method:
ZonedDateTime zonedDateTime2 =
ZonedDateTime.of(LocalDate.now(), LocalTime.now(), australiaZone);
ZonedDateTime zonedDateTime3 =
ZonedDateTime.of(LocalDateTime.now(), australiaZone);
ZonedDateTime zonedDateTime4 =
ZonedDateTime.ofInstant(Instant.now(), australiaZone);
// year, month, day, hours, minutes, seconds, nanoseconds, zoneId
ZonedDateTime zonedDateTime5 =
ZonedDateTime.of(2025, 1, 30, 13, 59, 59, 999, australiaZone);
You can also get the current date/time from the system clock in the default time zone with:
ZonedDateTime now = ZonedDateTime.now();
From a ZonedDateTime
you can get LocalDate
, LocalTime
, or a LocalDateTime
(without the time zone part) with:
LocalDate currentDate = now.toLocalDate();
LocalTime currentTime = now.toLocalTime();
LocalDateTime currentDateTime = now.toLocalDateTime();
ZonedDateTime
also has most of the methods of LocalDateTime
that we reviewed in the previous section:
// To get the value of a specified field
int day = now.getDayOfMonth();
int dayYear = now.getDayOfYear();
int nanos = now.getNano();
Month monthEnum = now.getMonth();
int year = now.get(ChronoField.YEAR);
long micro = now.getLong(ChronoField.MICRO_OF_DAY);
// This is new, gets the zone offset such as "-03:00"
ZoneOffset offset = now.getOffset();
// To create another instance
ZonedDateTime zdt1 = now.with(ChronoField.HOUR_OF_DAY, 10);
ZonedDateTime zdt2 = now.withSecond(49);
// Since these methods return a new instance, we can chain them!
ZonedDateTime zdt3 = now.withYear(2023).withMonth(12);
// The following two methods are specific to ZonedDateTime
// Returns a copy of the date/time with a different zone, retaining the instant
ZonedDateTime zdt4 = now.withZoneSameInstant(australiaZone);
// Returns a copy of this date/time with a different time zone,
// retaining the local date/time if it's valid for the new time zone
ZonedDateTime zdt5 = now.withZoneSameLocal(australiaZone);
// Adding
ZonedDateTime zdt6 = now.plusDays(4);
ZonedDateTime zdt7 = now.plusWeeks(3);
ZonedDateTime zdt8 = now.plus(2, ChronoUnit.HOURS);
// Subtracting
ZonedDateTime zdt9 = now.minusMinutes(20);
ZonedDateTime zdt10 = now.minusNanos(99999);
ZonedDateTime zdt11 = now.minus(10, ChronoUnit.SECONDS);
The toString()
method returns the date/time in the format of a LocalDateTime
followed by a ZoneOffset
, optionally, a ZoneId
if it is not the same as the offset, and omitting the parts with value zero:
// Prints 2024-09-19T00:30Z
System.out.println(
ZonedDateTime.of(2024, 9, 19, 0, 30, 0, 0, ZoneId.of("Z")));
// Prints, for example, 2024-06-17T19:48:39.113332-04:00[America/Montreal]
System.out.println(
ZonedDateTime.now(ZoneId.of("America/Montreal")));
Many countries in the world adopt what is called Daylight Saving Time (DST), the practice of advancing the clock by an hour in the summer (though not exactly in the summer in all countries but let’s bear with this) when the daylight savings time starts.
When daylight saving time ends, clocks are set back by an hour. This is done to make better use of natural daylight.
ZonedDateTime
is fully aware of DST.
For example, let’s take a country where DST is fully observed, like Italy (UTC/GMT +2).
In 2023, DST started in Italy on March 26th and ended on October 29th. This means that on:
March 26, 2023 at 2:00:00 A.M. clocks were turned forward 1 hour to
March 26, 2023 at 3:00:00 A.M. local daylight time instead
(So a time like March 26, 2023 2:30:00 A.M. didn’t actually exist!)
October 29, 2023 at 3:00:00 A.M. clocks were turned backward 1 hour to
October 29, 2023 at 2:00:00 A.M. local daylight time instead
(So a time like October 29, 2023 2:30:00 A.M. actually existed twice!)
If we create an instance of LocalDateTime
with this date/time and print it:
LocalDateTime ldt = LocalDateTime.of(2023, 3, 26, 2, 30);
System.out.println(ldt);
The result will be:
2023-03-26T02:30 // Wrong
But if we create an instance of ZonedDateTime
for Italy (notice that the format uses a city, not a country) and print it:
ZonedDateTime zdt = ZonedDateTime.of(
2023, 3, 26, 2, 30, 0, 0, ZoneId.of("Europe/Rome"));
System.out.println(zdt);
The result will be just like in the real world when using DST:
2023-03-26T03:30+02:00[Europe/Rome] // Correct
But be careful. We have to use a regional ZoneId
, a ZoneOffset
won’t do the trick because this class doesn’t have the zone rules information to account for DST:
ZonedDateTime zdt1 = ZonedDateTime.of(
2023, 3, 26, 2, 30, 0, 0, ZoneOffset.ofHours(2));
System.out.println(zdt1);
ZonedDateTime zdt2 = ZonedDateTime.of(
2023, 3, 26, 2, 30, 0, 0, ZoneId.of("UTC+2"));
System.out.println(zdt2);
The result will be:
2023-03-26T02:30+02:00 // Wrong
2023-03-26T02:30+02:00[UTC+02:00] // Wrong
When we create an instance of ZonedDateTime
for Italy, we have to add an hour to see the effect:
ZonedDateTime zdt3 = ZonedDateTime.of(
2023, 10, 29, 2, 30, 0, 0, ZoneId.of("Europe/Rome"));
System.out.println(zdt3);
ZonedDateTime zdt4 = zdt3.plusHours(1);
System.out.println(zdt4);
The result will be:
2023-10-29T02:30+02:00[Europe/Rome]
2023-10-29T02:30+01:00[Europe/Rome]
Otherwise we will be creating the ZonedDateTime
at 3:00 of the new time:
ZonedDateTime zdt5 = ZonedDateTime.of(
2023, 10, 29, 3, 30, 0, 0, ZoneId.of("Europe/Rome"));
System.out.println(zdt5); // Prints 2023-10-29T03:30+01:00[Europe/Rome]
We also need to be careful when adjusting the time across the DST boundary using the plus()
and minus()
methods that take a TemporalAmount
implementation, in other words, a Period
or a Duration
. This is because both differ in their treatment of daylight savings time.
Consider one hour before the start of DST in Italy:
ZonedDateTime zdt6 = ZonedDateTime.of(
2023, 3, 26, 1, 0, 0, 0, ZoneId.of("Europe/Rome"));
When we add a Duration
of one day:
System.out.println(zdt6.plus(Duration.ofDays(1)));
The result is:
2023-03-27T02:00+02:00[Europe/Rome]
When we add a Period
of one day:
System.out.println(zdt6.plus(Period.ofDays(1)));
The result is:
2023-03-27T01:00+02:00[Europe/Rome]
The reason is that Period
adds a conceptual date, while Duration
adds exactly one day (24 hours or 86,400 seconds) and when it crosses the DST boundary, one hour is added, and the final time is 02:00 instead of 01:00.
OffsetDateTime
and OffsetTime
ClassesOffsetDateTime
represents an object with date/time information and an offset from UTC, for example, 2025-01-01T11:30-06:00
.
You may think Instant
, OffsetDateTime
, and ZonedDateTime
are very much alike, after all, they all store the date and time to a nanosecond precision. However, there are subtle but important differences:
Instant
represents a point in time in the UTC time zone.OffsetDateTime
represents a point in time with an offset (any offset).ZonedDateTime
represents a point in time in a time zone (any time zone), adding full time zone rules like daylight saving time adjustments.OffsetTime
represents a time with an offset from UTC, for example, 11:30-06:00
. The common way to create an instance of these classes is:
OffsetDateTime odt = OffsetDateTime.of(
LocalDateTime.now(), ZoneOffset.of("+03:00"));
OffsetTime ot = OffsetTime.of(
LocalTime.now(), ZoneOffset.of("-08:00"));
System.out.println(odt);
System.out.println(ot);
If you run the above example, the output would be similar to this:
2024-06-17T19:19:32.645941+03:00
19:19:32.648413-08:00
Both classes have practically the same methods as their LocalDateTime
, ZonedDateTime
, and LocalTime
counterparts. With an offset from UTC and without time zone variations, they always represent an exact instant in time, which may be more suitable for certain types of applications (the Java documentation recommends OffsetDateTime
when communicating with a database or in a network protocol).
java.time.format.DateTimeFormatter
is the class used for parsing and formatting dates. It can be used in two ways:
The date/time classes LocalDate
, LocalTime
, LocalDateTime
, ZonedDateTime
, and OffsetDateTime
all have the following three methods:
// Formats the date/time object using the specified formatter
String format(DateTimeFormatter formatter)
// Obtains an instance of a date/time object (of type T) from a string with a default format
static T parse(CharSequence text)
// Obtains an instance of a date/time object (of type T) from a string using a specific formatter
static T parse(CharSequence text, DateTimeFormatter formatter)
DateTimeFormatter
has the following two methods:
// Formats a date/time object using the formatter instance
String format(TemporalAccessor temporal)
// Parses the text producing a temporal object
TemporalAccessor parse(CharSequence text)
All format methods throw the runtime exception java.time.DateTimeException
.
All parse methods throw the runtime exception java.time.format.DateTimeParseException
.
DateTimeFormatter
provides three ways to format date/time objects:
Predefined Formatters
Formatter | Description | Example |
---|---|---|
BASIC_ISO_DATE |
Date fields without separators | 20250803 |
ISO_LOCAL_DATE ISO_LOCAL_TIME ISO_LOCAL_DATE_TIME |
Date fields with separators | 2025-08-03 13:40:10 2025-08-03T13:40:10 |
ISO_OFFSET_DATE ISO_OFFSET_TIME ISO_OFFSET_DATE_TIME |
Date fields with separators and zone offset | 2025-08-03+07:00 13:40:10+07:00 2025-08-03T13:40:10+07:00 |
ISO_ZONED_DATE_TIME |
A zoned date and time | 2025-08-03T13:40:10+07:00[Asia/Bangkok] |
ISO_DATE ISO_TIME ISO_DATE_TIME |
Date or Time with or without offset DateTime with ZoneId |
2025-08-03+07:00 13:40:10 2025-08-03T13:40:10+07:00[Asia/Bangkok] |
ISO_INSTANT |
Date and Time of an Instant | 2025-08-03T13:40:10Z |
ISO_ORDINAL_DATE |
Year and day of the year | 2025-200 |
ISO_WEEK_DATE |
Year, week and day of the week | 2025-W34-2 |
RFC_1123_DATE_TIME |
RFC 1123 / RFC 822 date format | Sun, 3 Aug 2025 13:40:10 GMT |
Locale-specific Formatters
Style | Date | Time |
---|---|---|
SHORT |
8/3/15 | 1:40 PM |
MEDIUM |
Aug 03, 2025 | 1:40:00 PM |
LONG |
August 03, 2025 | 1:40:00 PM PDT |
FULL |
Monday, August 03, 2025 | 1:40:00 PM PDT |
Custom Patterns
Symbol | Meaning | Examples |
---|---|---|
G |
Era | AD; Anno Domini; A |
u |
Year | 2025; 15 |
y |
Year of Era | 2025; 15 |
D |
Day of Year | 150 |
M / L |
Month of Year | 7; 07; Jul; July; J |
d |
Day of Month | 20 |
Q / q |
Quarter of year | 2; 02; Q2; 2nd quarter |
Y |
Week-based Year | 2025; 15 |
w |
Week of Week-based Year | 30 |
W |
Week of Month | 2 |
E |
Day of Week | Tue; Tuesday; T |
e / c |
Localized Day of Week | 2; 02; Tue; Tuesday; T |
F |
Week of Month | 2 |
a |
AM/PM of Day | AM |
h |
Hour (1-12) | 10 |
K |
Hour (0-11) | 1 |
k |
Hour (1-24) | 20 |
H |
Hour (0-23) | 23 |
m |
Minute | 10 |
s |
Second | 11 |
S |
Fraction of Second | 999 |
A |
Milli of Day | 2345 |
n |
Nano of Second | 865437987 |
N |
Nano of Day | 12986497300 |
V |
Time Zone ID | Asia/Manila; Z; -06:00 |
z |
Time Zone Name | Pacific Standard Time; PST |
O |
Localized Zone Offset | GMT+4; GMT+04:00; UTC-04:00; |
X |
Zone Offset (‘Z’ for zero) | Z; -08; -0830; -08:30 |
x |
Zone Offset | +0000; -08; -0830; -08:30 |
Z |
Zone Offset | +0000; -0800; -08:00 |
' |
Escape for Text | |
'' |
Single Quote | |
[ ] |
Optional Section Start / End | |
# { } |
Reserved for future use |
Assuming:
LocalDate ldt = LocalDate.of(2025, 1, 20);
These are examples of using a predefined formatter:
System.out.println(DateTimeFormatter.ISO_DATE.format(ldt));
System.out.println(ldt.format(DateTimeFormatter.ISO_DATE));
The output will be:
2025-01-20
2025-01-20
These are examples of using a localized style:
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
// With the current locale
System.out.println(formatter.format(ldt));
System.out.println(ldt.format(formatter));
// With another locale
System.out.println(formatter.withLocale(Locale.GERMAN).format(ldt));
One output can be:
1/20/25
1/20/25
20.01.25
And these are examples of using a custom pattern:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("QQQQ Y");
// With the current locale
System.out.println(formatter.format(ldt));
System.out.println(ldt.format(formatter));
// With another locale
System.out.println(formatter.withLocale(Locale.GERMAN).format(ldt));
One output can be:
1st quarter 2025
1st quarter 2025
1. Quartal 2025
If the formatter uses information that is not available, a DateTimeException
will be thrown. For example, using a DateTimeFormatter.ISO_OFFSET_DATE
with a LocalDate
instance (it doesn’t have offset information).
To parse a date and/or time value from a string, use one of the parse
methods. For example:
// Format according to ISO-8601
String dateTimeStr1 = "2025-06-29T14:45:30";
// Custom format
String dateTimeStr2 = "2025/06/29 14:45:30";
LocalDateTime ldt = LocalDateTime.parse(dateTimeStr1);
// Using DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
// DateTimeFormatter returns a TemporalAccessor instance
TemporalAccessor ta = formatter.parse(dateTimeStr2);
// LocalDateTime returns an instance of the same type
ldt = LocalDateTime.parse(dateTimeStr2, formatter);
The version of parse()
of the date/time objects takes a string in a format according to ISO-8601, which is:
Class | Format | Example |
---|---|---|
LocalDate |
uuuu-MM-dd |
2024-12-03 |
LocalTime |
HH:mm:ss |
10:15 |
LocalDateTime |
uuuu-MM-dd'T'HH:mm:ss |
2024-12-03T10:15:30 |
ZonedDateTime |
uuuu-MM-dd'T'HH:mm:ssXXXXX[VV] |
2023-12-03T10:15:30+01:00[Europe/Paris] |
OffsetDateTime |
uuuu-MM-dd'T'HH:mm:ssXXXXX |
2023-12-03T10:15:30+01:00 |
OffsetTime |
HH:mm:ssXXXXX |
10:15:30+01:00 |
If the formatter uses information that is not available or if the pattern of the format is invalid, a DateTimeParseException
will be thrown.
In the Localization chapter, we’ll revisit the DateTimeFormatter
class, focusing on its application in localization.
LocalDate
, LocalTime
, LocalDateTime
, Instant
, Period
, and Duration
are the core classes of the new Java Date/Time API, located in the package java.time
. They are immutable, thread-safe, and with the exception of Instant
, they don’t store or represent a time-zone.
LocalDate
, LocalTime
, LocalDateTime
, and Instant
implement interface java.time.temporal.Temporal
, so they all have similar methods. While Period
and Duration
implement interface java.time.temporal.TemporalAmount
, which also makes them very similar.
LocalDate
represents a date with the year, month, and day of the month information. You can create an instance using:
LocalDate.of(2025, 8, 1);
Valid ChronoField
values to use with the get()
method are: DAY_OF_WEEK, ALIGNED_DAY_OF_WEEK_IN_MONTH, ALIGNED_DAY_OF_WEEK_IN_YEAR, DAY_OF_MONTH, DAY_OF_YEAR, EPOCH_DAY, ALIGNED_WEEK_OF_MONTH, ALIGNED_WEEK_OF_YEAR, MONTH_OF_YEAR, PROLEPTIC_MONTH, YEAR_OF_ERA, YEAR,
and ERA
.
Valid ChronoUnits
values to use with the plus()
and minus()
methods are: DAYS, WEEKS, MONTHS, YEARS, DECADES, CENTURIES, MILLENNIA,
and ERAS
.
LocalTime
represents a time with hour, minutes, seconds, and nanoseconds information. You can create an instance using:
LocalTime.of(14, 20, 50, 99999);
Valid ChronoField
values to use with the get()
method are: NANO_OF_SECOND, NANO_OF_DAY, MICRO_OF_SECOND, MICRO_OF_DAY, MILLI_OF_SECOND, MILLI_OF_DAY, SECOND_OF_MINUTE, SECOND_OF_DAY, MINUTE_OF_HOUR, MINUTE_OF_DAY, HOUR_OF_AMPM, CLOCK_HOUR_OF_AMPM, HOUR_OF_DAY, CLOCK_HOUR_OF_DAY,
and AMPM_OF_DAY
.
Valid ChronoUnits
values to use with the plus()
and minus()
methods are: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS,
and HALF_DAYS
.
LocalDateTime
is a combination of LocalDate
and LocalTime
. You can create an instance using:
LocalDateTime.of(2025, 8, 1, 14, 20, 50, 99999);
Valid ChronoField
and ChronoUnits
values are a combination of the ones used for LocalDate
and LocalTime
.
Instant
represents a single point in time in seconds and nanoseconds. You can create an instance using:
Instant.ofEpochSecond(134556767, 999999999);
Valid ChronoField
values to use with the get()
method are: NANO_OF_SECOND, MICRO_OF_SECOND, MILLI_OF_SECOND,
and INSTANT_SECONDS
.
Valid ChronoUnit
values to use with the plus()
and minus()
methods are: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS,
and DAYS
.
Period
represents an amount of time in terms of years, months and days. You can create an instance using:
Period.of(3, 12, 30);
Valid ChronoUnits
values to use with the get()
method are: DAYS, MONTHS, YEARS
.
Duration
represents an amount of time in terms of seconds and nanoseconds. You can create an instance using:
Duration.ofSeconds(50, 999999);
Valid ChronoUnits
values to use with the of()
method are: NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS,
and DAYS
. With the get()
method, only NANOS
and SECONDS
are valid.
ZoneId
, ZoneOffset
, ZonedDateTime
, OffsetDateTime
, and OffsetTime
are the classes of the Java Date/Time API that store information about time zones and time offsets. They are located in the java.time
package and are immutable.
Each time zone has an ID, represented by the class ZoneId
. There are three types of ID.
The first type just states the offset from UTC/GMT time. They are represented by the class ZoneOffset
and they consist of digits starting with +
or -
, for example +02:00
.
The second type also states the offset from UTC/GMT time, but with one of the following prefixes: UTC, GMT and UT, for example, UTC+11:00
. They are also represented by the class ZoneOffset
.
The third type is region based. These IDs have the format area/city, for example, Europe/London.
ZoneId
object, use the method of
:
ZoneId.of("Asia/Singapore");
ZoneId.of("+3");
ZoneId.of("Z");
The first method above methods produces an object of type ZoneRegion
. The other two produce an object of type ZoneOffset
.
A java.time.ZonedDateTime
object represents a point in time relative to a time zone.
ZonedDateTime
object has three parts: a date, a time, and a time zone. It can be created with either:
ZoneId australiaZone = ZoneId.of("Australia/Victoria");
ZonedDateTime zonedDateTime5 =
ZonedDateTime.of(2025, 1, 30, 13, 59, 59, 999, australiaZone);
Or by using LocalDate
, LocalTime
, LocalDateTime
, or Instant
plus ZoneId
.
If we create an instance of ZonedDateTime
for a region where a Daylight Saving Time (DST) is observed, the instance will support it, advancing the clock one hour when DST starts, and setting it back when DST ends.
Period
and Duration
differ in their treatment of DST.
Period
adds a conceptual day to a date, while Duration
adds an exact day to a date, without taking into account DST.
OffsetDateTime
represents an object with date/time information and an offset from UTC, for example, 2025-01-01T11:30-06:00
.
OffsetTime
represents a time with an offset from UTC, for example, 11:30-06:00
.
java.time.format.DateTimeFormatter
is a class for parsing and formatting dates. It can be used in two ways:
LocalDate
, LocalTime
, LocalDateTime
, ZonedDateTime
, OffsetDateTime
all have the methods:
String format(DateTimeFormatter formatter)
static T parse(CharSequence text)
static T parse(CharSequence text, DateTimeFormatter formatter)
DateTimeFormatter
has the following two methods:
String format(TemporalAccessor temporal)
TemporalAccessor parse(CharSequence text)
format
methods throw the runtime exception java.time.DateTimeException
, while all parse
methods throw the runtime exception java.time.format.DateTimeParseException
.1. Which of the following are valid ways to create a LocalDate
object?
A. LocalDate.of(2014);
B. LocalDate.with(2014, 1, 30);
C. LocalDate.of(2014, 0, 30);
D. LocalDate.now().plusDays(5);
2. Which of the following options is the result of executing this line?
LocalDate.of(2014, 1, 2).atTime(14, 30, 59, 999999)
A. A LocalDate
instance representing 2014-01-02
B. A LocalTime
instance representing 14:30:59:999999
C. A LocalDateTime
instance representing 2014-01-02 14:30:59:999999
D. An exception is thrown
3. Which of the following are valid ChronoUnit
values for LocalTime
? (Choose all that apply)
A. YEAR
B. NANOS
C. DAY
D. HALF_DAYS
4. Which of the following statements are true? (Choose all that apply)
A. java.time.Period
implements java.time.temporal.Temporal
B. java.time.Instant
implements java.time.temporal.Temporal
C. LocalDate
and LocalTime
are thread-safe
D. LocalDateTime.now()
will return the current time in UTC zone
5. Which of the following options is a valid way to get the nanoseconds part of an Instant
object referenced by i
?
A. int nanos = i.getNano();
B. long nanos = i.get(ChronoField.NANOS);
C. long nanos = i.get(ChronoUnit.NANOS);
D. int nanos = i.getEpochNano();
6. Which of the following options is the result of executing this line?
System.out.println(
Period.between(
LocalDate.of(2025, 3, 20),
LocalDate.of(2025, 2, 20))
);
A. P29D
B. P-29D
C. P1M
D. P-1M
7. Which of the following options is the result of executing this line?
System.out.println(
Duration.between(
LocalDateTime.of(2025, 3, 20, 18, 0),
LocalTime.of(18, 5) )
);
A. PT5M
B. PT-5M
C. PT300S
D. An exception is thrown
8. Which of the following are valid ChronoField
values for LocalDate
?
A. DAY_OF_WEEK
B. HOUR_OF_DAY
C. DAY_OF_MONTH
D. MILLI_OF_SECOND
9. Which of the following are valid ways to create a ZoneId
object?
A. ZoneId.ofHours(2);
B. ZoneId.of("2");
C. ZoneId.of("-1");
D. ZoneId.of("America/Canada");
10. Which of the following options is the result of executing these lines?
ZoneOffset offset = ZoneOffset.of("Z");
System.out.println(
offset.get(ChronoField.HOUR_OF_DAY)
);
A. 0
B. 1
C. 12:00
D. An exception is thrown
11. Assuming a local time zone of +2:00
, which of the following options is the result of executing these lines?
ZonedDateTime zdt =
ZonedDateTime.of(2025, 02, 28, 5, 0, 0, 0,
ZoneId.of("+05:00"));
System.out.println(zdt.toLocalTime());
A. 05:00
B. 17:00
C. 02:00
D. 03:00
12. Assuming that DST starts on October, 4, 2025 at 0:00:00, which of the following is the result of executing the above lines?
ZonedDateTime zdt =
ZonedDateTime.of(2025, 10, 4, 0, 0, 0, 0,
ZoneId.of("America/Asuncion"))
.plus(Duration.ofHours(1));
System.out.println(zdt);
A. 2025-10-04T00:00-03:00[America/Asuncion]
B. 2025-10-04T01:00-03:00[America/Asuncion]
C. 2025-10-04T02:00-03:00[America/Asuncion]
D. 2025-10-03T23:00-03:00[America/Asuncion]
13. Which of the following statements are true? (Choose all that apply)
A. java.time.ZoneOffset
is a subclass of java.time.ZoneId
.
B. java.time.Instant
can be obtained from java.time.ZonedDateTime
.
C. java.time.ZoneOffset
can manage DST.
D. java.time.OffsetDateTime
represents a point in time in the UTC time zone.
14. Which of the following options is the result of executing these lines?
DateTimeFormatter formatter =
DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
System.out.println(
formatter
.withLocale(Locale.ENGLISH)
.format(LocalDateTime.of(2025, 5, 7, 16, 0))
);
A. 5/7/15 4:00 PM
B. 5/7/15
C. 4:00 PM
D. 4:00:00 PM
15. Which of the following statements is true about these lines?
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("HH:mm:ss X");
OffsetDateTime odt =
OffsetDateTime.parse("11:50:20 Z", formatter);
A. The pattern HH:mm:ss X
is invalid.
B. An OffsetDateTime
is created successfully.
C. Z
is an invalid offset.
D. An exception is thrown at runtime.
Do you like what you read? Would you consider?
Do you have a problem or something to say?