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
LocalDatewith placeholder values for missing fields. - ❌ Forgetting leap year rules with
MonthDay.of(FEBRUARY, 29). - ❌ Comparing
YearMonthdirectly toLocalDatewithout 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, andMonthDayin thejava.timeAPI. - 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
LocalDatewith placeholder values. - ✅ Use
Yearfor year-only contexts. - ✅ Use
YearMonthfor month + year values like credit card expiry. - ✅ Use
MonthDayfor recurring dates without year. - ✅ Always validate
MonthDayagainst 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).