Back to Blog
Java

Java Period: Date-Based Amounts in java.time

Use java.time.Period to represent date-based amounts in years, months, and days, including creation, arithmetic, normalization, and differences from Duration.

java.timePerioddate arithmetictemporal APIJava 8
Illustration of a calendar with a period arrow between two dates, representing the Java Period class for date-based amounts.

java.time.Period represents a date-based amount of time in years, months, and days. It is useful for calendar-aware operations such as "add one month" or "calculate the difference in years and months." Unlike Duration, which stores seconds and nanoseconds, Period stores calendar units. When you add a Period to a date, the date class performs the resolution, so month lengths and leap years are handled correctly.

Creating a Period

The simplest way to create a Period is with the static factory methods of, ofYears, ofMonths, ofWeeks, and ofDays. Each returns an immutable instance.

Period oneYear = Period.ofYears(1); Period twoMonths = Period.ofMonths(2); Period threeDays = Period.ofDays(3); Period mixed = Period.of(1, 2, 3); // 1 year, 2 months, 3 days

ofWeeks is a convenience method that converts weeks to days: Period.ofWeeks(2) is equivalent to Period.ofDays(14). Period has no separate week component; weeks are always represented as days.

You can also create a Period from a pair of LocalDate instances using between. This is the most common way to measure the calendar difference between two dates.

LocalDate start = LocalDate.of(2023, 5, 10); LocalDate end = LocalDate.of(2024, 8, 15); Period period = Period.between(start, end); System.out.println(period); // P1Y3M5D

The toString representation follows ISO-8601: P followed by the years, months, and days components. A zero-length period is P0D.

Adding and Subtracting Periods

Period can be added to date-based temporals with their plus methods. The following example adds one month to a LocalDate:

LocalDate today = LocalDate.of(2025, 2, 28); Period oneMonth = Period.ofMonths(1); LocalDate nextMonth = today.plus(oneMonth); System.out.println(nextMonth); // 2025-03-28

The date class performs the calendar arithmetic. LocalDate.plusMonths adds the month count first, then adjusts the day if it is beyond the last valid day of the resulting month. Adding one month to January 31 therefore gives February 28, or February 29 in a leap year. The Period itself only carries the amount; it does not resolve dates.

You can add a Period to a LocalDateTime as well; the time part remains unchanged. For ZonedDateTime, adding a Period adjusts the date and keeps the same zone, but the offset can change if the adjustment crosses a DST boundary.

Reading Period Components

The getYears, getMonths, and getDays methods return each component as stored. Components are not normalized automatically: Period.ofMonths(14) has getMonths() == 14 and getYears() == 0.

Period p = Period.of(0, 14, 0); System.out.println(p.getYears()); // 0 System.out.println(p.getMonths()); // 14

Use normalized() when you want years and months consolidated. Period.ofMonths(14).normalized() returns P1Y2M. Normalization never converts days to months, because a day count does not map to a fixed number of months without a reference date.

Period vs Duration

Use Period for calendar-based amounts such as age, billing cycles, or a calendar month. Use Duration for fixed machine time such as timeouts or elapsed time.

AspectPeriodDuration
UnitsYears, months, daysSeconds and nanoseconds; days are 24-hour units
MeaningCalendar-basedTime-based
ExamplePeriod.ofMonths(1)Duration.ofDays(30)
Calendar effectsHandled by date classes when addedFixed length; no calendar-month concept
Typical useAge, billing cyclesTimeouts, elapsed time

A common mistake is using Duration to represent a month. Duration.ofDays(30) is not the same as Period.ofMonths(1), because a calendar month can have 28, 29, 30, or 31 days. If you need calendar semantics, use Period.

Negative Periods and Normalization

Period can be negative. For example, Period.between(end, start) when end is after start returns a negative period. You can also create one directly with Period.of(-1, 0, 0).

LocalDate earlier = LocalDate.of(2024, 1, 1); LocalDate later = LocalDate.of(2024, 2, 1); Period negative = Period.between(later, earlier); System.out.println(negative); // P-1M

Adding a negative period to a date works as expected: later.plus(negative) returns earlier. When comparing periods, remember that equality is based on the exact stored components, not on the effect on a particular date. Period.ofMonths(1) is not equal to Period.ofDays(30).

Normalization applies only to years and months. Period.ofMonths(15).normalized() returns P1Y3M, but Period.ofDays(365) stays P365D because the number of days in a year varies. To get a total number of days, use a reference date and calculate the day difference directly.

Edge Cases and Production Considerations

One subtle issue is adding a Period to a date near the end of a month. As described above, LocalDate.plusMonths resolves the result to the last valid day. If you need to preserve the original day of month, such as always landing on the 31st, you must implement that rule separately.

Period instances are immutable and thread-safe, so they can be safely shared and cached. If the amount is constant, reuse the same Period instead of creating a new one in a loop.

The ISO-8601 string form is stable and can be parsed with Period.parse, which is useful for configuration values or API payloads.

Period fromConfig = Period.parse("P1Y2M");

Keep in mind that Period has no separate week unit; convert weeks to days explicitly. It also does not represent hours, minutes, or seconds; use Duration for those.

Using java.time.Period: Date-Based Amounts in Java | RYUSLOG DEV