Working with Year, YearMonth, and MonthDay in Java Date-Time API

Illustration for Working with Year, YearMonth, and MonthDay in Java Date-Time API
By Last updated:

In many business applications, you don’t always need the full LocalDate (year, month, and day). Sometimes you only care about the year of a financial statement, the month of a recurring subscription, or a birthday without a year. Java’s java.time API introduces Year, YearMonth, and MonthDay for these exact scenarios.

A common pain point: developers misuse LocalDate and put dummy values (e.g., year 1970 for birthdays), leading to confusing bugs and unnecessary data. This tutorial explains how to properly model partial date information with Year, YearMonth, and MonthDay.


1. The Year Class

Represents a year in the ISO-8601 calendar system.

Year year = Year.of(2025);
System.out.println(year.isLeap()); // true or false
System.out.println(year.length()); // days in the year

Use Cases

  • Academic or fiscal year records.
  • Yearly reports or summaries.

2. The YearMonth Class

Represents a particular year and month, without a day.

YearMonth ym = YearMonth.of(2025, Month.FEBRUARY);
System.out.println(ym.lengthOfMonth()); // 28 or 29 depending on leap year

LocalDate firstDay = ym.atDay(1);
LocalDate lastDay = ym.atEndOfMonth();
System.out.println(firstDay + " to " + lastDay);

Use Cases

  • Credit card expiration dates.
  • Monthly billing cycles.
  • Financial planning.

3. The MonthDay Class

Represents a month and day in the ISO calendar, without a year.

MonthDay md = MonthDay.of(Month.FEBRUARY, 29);
System.out.println(md.isValidYear(2024)); // true
System.out.println(md.isValidYear(2025)); // false

LocalDate birthday = md.atYear(2028);
System.out.println(birthday); // 2028-02-29

Use Cases

  • Birthdays.
  • Anniversaries.
  • Recurring reminders without year context.

4. Converting Between Year, YearMonth, MonthDay, and LocalDate

YearMonth ym = YearMonth.now();
LocalDate date = ym.atDay(15); // Convert to LocalDate

MonthDay md = MonthDay.now();
LocalDate resolved = md.atYear(2026); // Attach year to resolve full date

5. Pitfalls and Anti-Patterns

  • ❌ Using LocalDate with placeholder values for missing fields.
  • ❌ Forgetting leap year rules with MonthDay.of(FEBRUARY, 29).
  • ❌ Comparing YearMonth directly to LocalDate without conversion.

6. Business Case Study: Subscription Billing

  • Subscription expires in March 2025 → Store as YearMonth.of(2025, 3).
  • Birthday reminder → Store as MonthDay.of(5, 10) (May 10).
  • Annual report → Store as Year.of(2025).

This ensures correct modeling without forcing dummy values.


📌 What's New in Java Versions?

  • Java 8: Introduced Year, YearMonth, and MonthDay in the java.time API.
  • Java 11: Minor performance improvements.
  • Java 17: No API changes.
  • Java 21: Still stable—these classes haven’t changed since Java 8.

✅ These types are mature and safe to use across all modern Java versions.


Real-World Analogy

Think of these classes like calendar fragments:

  • Year → A whole calendar year (e.g., 2025).
  • YearMonth → A specific month (like a magazine issue: March 2025 edition).
  • MonthDay → A recurring event (like a birthday every April 14).

Conclusion + Key Takeaways

  • ❌ Don’t misuse LocalDate with placeholder values.
  • ✅ Use Year for year-only contexts.
  • ✅ Use YearMonth for month + year values like credit card expiry.
  • ✅ Use MonthDay for recurring dates without year.
  • ✅ Always validate MonthDay against leap years.

Modeling dates correctly makes your applications cleaner, more accurate, and domain-driven.


FAQ: Expert-Level Q&A

1. Can Year handle negative years (BC)?
Yes, ISO-8601 supports proleptic years, including negatives.

2. Does YearMonth consider leap years?
Yes, lengthOfMonth() accounts for leap years automatically.

3. What happens if MonthDay is Feb 29 in a non-leap year?
isValidYear(year) returns false; attaching a year throws DateTimeException.

4. Can I compare YearMonth with LocalDate?
Yes, but convert LocalDate to YearMonth using YearMonth.from(date) first.

5. Is Year immutable?
Yes, like all java.time classes.

6. Can I use YearMonth for payroll systems?
Yes, it’s ideal for monthly payroll cycles.

7. Should birthdays be stored as LocalDate or MonthDay?
Use MonthDay if the year is irrelevant, LocalDate if the year matters (e.g., age checks).

8. Can Year be used for historical calendars (Julian)?
Not directly—ISO-8601 only. Use java.time.chrono for alternate calendars.

9. How do I format YearMonth?
Use DateTimeFormatter.ofPattern("MM/yyyy") or similar.

10. Can YearMonth be persisted in databases?
Yes—map to VARCHAR or split into two numeric columns (year, month).