Calculating Age, Expiry Dates, and Scheduling with the Java Date & Time API

Illustration for Calculating Age, Expiry Dates, and Scheduling with the Java Date & Time API
By Last updated:

Age calculation, expiry date tracking, and scheduling are foundational requirements in software systems. From insurance policies expiring, subscription renewals, medication reminders, to calculating user ages for eligibility checks, accurate time calculations matter. A common mistake developers make is using manual arithmetic with days or months, which fails in leap years, varying month lengths, or daylight saving transitions.

The java.time API offers robust tools (Period, Duration, ChronoUnit, TemporalAdjusters) to handle these tasks correctly and efficiently.


1. Calculating Age with Period

LocalDate birthDate = LocalDate.of(1990, 5, 15);
LocalDate today = LocalDate.now();

Period age = Period.between(birthDate, today);
System.out.printf("Age: %d years, %d months, %d days%n", age.getYears(), age.getMonths(), age.getDays());

✅ Handles leap years and month differences.
❌ Avoid manual subtraction—will fail on leap years.


2. Checking Expiry Dates

LocalDate issueDate = LocalDate.of(2023, 1, 1);
LocalDate expiryDate = issueDate.plusYears(2);

boolean isExpired = LocalDate.now().isAfter(expiryDate);
System.out.println("Is expired? " + isExpired);

✅ Great for licenses, subscriptions, or product shelf-life.


3. Scheduling with TemporalAdjusters

Next Monday (recurring meetings)

LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println("Next Monday: " + nextMonday);

Last Day of Month (salary processing)

LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println("Last day of month: " + lastDay);

✅ Clean API for recurring schedules.


4. Measuring Time with Duration

LocalDateTime start = LocalDateTime.now();
// simulate task
LocalDateTime end = start.plusHours(5).plusMinutes(30);

Duration duration = Duration.between(start, end);
System.out.printf("Task duration: %d hours %d minutes%n", duration.toHours(), duration.toMinutesPart());

✅ Useful for task runtimes, session timeouts, and tracking SLAs.


5. Combining Period and Duration

LocalDateTime expiryDateTime = LocalDateTime.of(2025, 12, 31, 23, 59);
LocalDateTime now = LocalDateTime.now();

Duration remaining = Duration.between(now, expiryDateTime);
System.out.printf("Remaining: %d days, %d hours%n", remaining.toDays(), remaining.toHoursPart());

✅ Works for precise expiry down to hours and minutes.


6. Pitfalls and Anti-Patterns

  • ❌ Using System.currentTimeMillis() for age/expiry → timezone issues.
  • ❌ Adding fixed days (+365) for a year → fails on leap years.
  • ❌ Not handling DST transitions in scheduling.
  • ❌ Mixing Period (date-based) and Duration (time-based) incorrectly.

7. Best Practices

  • ✅ Use Period for human-readable differences (age, years, months).
  • ✅ Use Duration for machine-precise differences (sessions, timers).
  • ✅ Use TemporalAdjusters for recurring schedules.
  • ✅ Always validate edge cases like leap years and month-end.

📌 What's New in Java Versions?

  • Java 8: Introduced Period, Duration, TemporalAdjusters.
  • Java 11: Minor improvements in Duration formatting.
  • Java 17: Stable API, no major changes.
  • Java 21: Continued stability, regular time zone updates.

✅ Core APIs unchanged since Java 8.


Real-World Analogy

Think of age, expiry, and scheduling like a gym membership:

  • Age → when you qualify for age-based discounts.
  • Expiry → when your membership ends.
  • Scheduling → when the next training session is due.

The API provides accurate ways to calculate each without human error.


Conclusion + Key Takeaways

  • ❌ Avoid manual arithmetic for age, expiry, or schedules.
  • ✅ Use Period for age and expiry, Duration for time spans.
  • ✅ Use TemporalAdjusters for recurring schedules.
  • ✅ Always test edge cases like leap years and DST.

Correct handling ensures accurate eligibility checks, compliance reporting, and user trust.


FAQ: Expert-Level Q&A

1. Should I use Period or Duration for age calculation?
Use Period—it handles years/months properly.

2. How to calculate expiry at a specific time of day?
Use LocalDateTime + Duration for precise expiry.

3. What’s the difference between isAfter() and isBefore()?
They compare dates/times chronologically.

4. Can TemporalAdjusters skip invalid dates (e.g., 31st in February)?
Yes, they adjust to the last valid day automatically.

5. How to check if a subscription expires in the next 7 days?
expiryDate.isBefore(today.plusDays(7)).

6. Should I store expiry dates in UTC?
Yes, store in UTC, convert for user display.

7. How do I handle recurring events in multiple zones?
Store UTC, convert to ZonedDateTime for each user’s zone.

8. Can Period and Duration be combined?
Yes—Period for calendar-based, Duration for clock-based precision.

9. Is there a risk of DST affecting expiry?
Yes, use ZonedDateTime for DST-aware expiry logic.

10. Should I always test with leap years?
Yes—leap years often break naive expiry/age logic.