Back to Blog
Java

Java Period: Date-Based Amounts in java.time

java period: Learn how to use java.time.Period to represent date-based amounts, perform arithmetic, and avoid common pitfalls when working with years, months, and days.

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 period requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When working with dates in Java, you often need to represent a span of time in terms of years, months, and days. The java.time.Period class models exactly that: a date-based amount of time. Unlike Duration, which measures time in seconds and nanoseconds, Period is calendar-based, so it respects concepts like month lengths and leap years. This makes it the right tool for operations like "add one month" or "calculate age in years and months."

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 that converts weeks to days: Period.ofWeeks(2) is equivalent to Period.ofDays(14). This is important because Period does not have a separate week component; weeks are always expressed as days.

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

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 the ISO-8601 format: P followed by years, months, and days. A period of zero is P0D.

Adding and Subtracting Periods

Period integrates with LocalDate and other date types through the plus and minus methods. This is where date-based arithmetic becomes practical.

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

Notice that plus on LocalDate handles month-end adjustments. If you add one month to January 31, you get February 28 (or 29 in a leap year), not a nonexistent date. The Period itself does not know about the calendar; it simply carries the amount. The date arithmetic is performed by the date class.

You can also add a Period to a LocalDateTime, but be aware that the time part remains unchanged. For ZonedDateTime, adding a Period adjusts the date and preserves the zone, but may shift the offset if the date change crosses a DST boundary.

Reading Period Components

The getYears, getMonths, and getDays methods return the individual components. These are not normalized across units; a period of 14 months returns getMonths() == 2 and getYears() == 1 only if you constructed it that way. Period.ofMonths(14) returns getMonths() == 14 and getYears() == 0.

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

If you need a normalized form, use normalized(), which converts months to years when possible. For Period.ofMonths(14), normalized() returns P1Y2M. Note that days are never normalized because a day count does not map cleanly to months without a reference date.

Period vs Duration

Choosing between Period and Duration depends on the nature of the amount. Duration is precise to nanoseconds and is meant for machine time, while Period is calendar-based and meant for human-readable time.

AspectPeriodDuration
UnitsYears, months, daysHours, minutes, seconds, ns
Based onCalendarClock time
Example"1 month""30 days"
DST handlingDate arithmetic adjustsFixed length, may not align
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 month can have 28, 29, 30, or 31 days. If you need calendar semantics, Period is the correct choice.

Negative Periods and Normalization

Period can be negative. For example, Period.between(end, start) where end is after start returns a negative period. You can also create one explicitly 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

When adding a negative period to a date, the arithmetic works as expected: later.plus(negative) returns earlier. Be careful when comparing periods. Period.ofMonths(1) is not equal to Period.ofDays(30), even though they may represent the same duration for a specific date range. Equality is based on the exact components, not the calendar effect.

Normalization only applies to months and years. Period.ofMonths(15).normalized() returns P1Y3M. However, Period.ofDays(365) stays as P365D because the number of days in a year varies. If you need a total number of days, you must use a date reference and calculate the difference in days.

Edge Cases and Production Considerations

One subtle issue arises when adding a Period to a date near the end of a month. For example, adding one month to January 31 gives February 28 (or 29). This is defined by the plus method in LocalDate, which uses the TemporalAdjuster logic. If you need to preserve the last day of the month, you must handle that manually.

Another concern is performance. Period instances are immutable and thread-safe, so they can be cached and shared. Creating a Period is cheap, but Period.between requires two LocalDate objects and performs a calendar calculation. In high-throughput code, avoid creating unnecessary Period instances in loops; reuse them when the amount is constant.

When using Period in a distributed system or persistence layer, remember that the ISO-8601 string format is stable and can be parsed with Period.parse. This is useful for configuration values or API payloads.

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

Finally, be aware that Period does not support weeks as a separate unit. If you need to represent weeks, convert to days explicitly. Also, Period does not handle hours, minutes, or seconds; those belong to Duration.

java period: Practical Usage and Code Examples | RYUSLOG DEV