Date & Time ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-21

Date & Time. Practical Tasks

The pattern MM/dd/YYYY looks harmless, yet for LocalDate.of(2024, 12, 30) it prints 12/30/2025: the capital Y is the week-based year, not the calendar year. The program passes every test you run in March and ships a wrong year in the last days of December — a bug that is much cheaper to catch in a practice problem than in a report a customer has already received.

These Java date and time exercises practise the java.time API added in Java 8: LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration and formatting with DateTimeFormatter. The page has 7 LocalDate and java.time practice problems with hints — from printing facts about your date of birth to two parts of the course practice projects: the employee salary report and the online store receipt. The theory is in the lesson Java Date and Time API: java.time and the Current Date and the lessons of the module that follow it.

LocalDate Methods: Your Date of Birth

  1. Create a LocalDate object with your date of birth using LocalDate.of(year, month, day).
  2. Print the day of the week, the day of the year, the month and the year of this date.
  3. Create a LocalDate object with the current date.
  4. Compare the current date with your date of birth using isAfter(), isBefore() and isEqual(), and print the results.

Example output for the date of birth March 15, 2000:

Day of week: WEDNESDAY
Day of year: 75
Month: MARCH
Year: 2000
Today is after the birthday: true

Hint

getDayOfWeek() and getMonth() return the enums DayOfWeek and Month (that is why the output shows WEDNESDAY and MARCH), while getMonthValue() returns the month number from 1 to 12. The day of the year is getDayOfYear(): in the leap year 2000, March 15 is day 75, not 74.

Holiday Calendar for the Current Year

  1. Get the current year with Year.now() or LocalDate.now().getYear().
  2. Make a list of 5–7 holidays with a fixed date (for example, New Year’s Day, Valentine’s Day, Independence Day, Halloween, Christmas Day, or the holidays of your country): a Map<LocalDate, String> or a list of «date + name» objects.
  3. Print the holiday schedule in date order with DateTimeFormatter: the day of the week, the month name and the day — in English, whatever the default locale of the JVM is.

Example output line for 2026:

Saturday, July 4 — Independence Day

Where to start

The pattern EEEE, MMMM d with Locale.US gives Saturday, July 4. Without a locale the names come from the default locale of the JVM, and on a German system you would get Samstag, Juli 4. To print the holidays in order, keep them in a TreeMap: its LocalDate keys are sorted chronologically. For «floating» holidays use TemporalAdjusters: Thanksgiving is LocalDate.of(year, 11, 1).with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY)), Memorial Day is lastInMonth(DayOfWeek.MONDAY) in May.

Age and Days Until the Next Birthday

  1. Store a date of birth as a LocalDate.
  2. Use Period.between() to print the exact age: full years, months and days.
  3. Count the days left until the next birthday. If this year’s birthday has already passed, count to the birthday next year; if it is today, print a greeting instead.
  4. Test the program with a birthday on February 29 (for example, 02/29/2004).

Example output for the date of birth 03/15/2000 if today is 10/20/2026:

Age: 26 years 7 months 5 days
Days until the birthday: 146

A tricky detail

Count the days until the birthday with ChronoUnit.DAYS.between(today, nextBirthday), not with Period.getDays(): in a period that is only the days left over after the full months. The birthday in a given year is easy to build with withYear(): for February 29 in a non-leap year it returns February 28, while LocalDate.of(2027, 2, 29) throws DateTimeException. Pass today as a parameter so that you can check the example above on any day.

Work Shift Duration with LocalTime and Duration

  1. Define several shifts as pairs of LocalTime values «start — end», including at least one night shift: 09:00–18:00, 08:30–17:15, 22:00–06:00.
  2. For each shift, print its length in hours and minutes using Duration.
  3. Print the total length of all shifts.

Expected output:

09:00–18:00: 9 h 0 min
08:30–17:15: 8 h 45 min
22:00–06:00: 8 h 0 min
Total: 25 h 45 min

The night shift catch

Duration.between(LocalTime.of(22, 0), LocalTime.of(6, 0)) returns PT-16H: a LocalTime does not know that the shift ended the next day. If the duration is negative (isNegative()), add Duration.ofDays(1), or store the start and the end as LocalDateTime. For one shift, toHours() and toMinutesPart() (Java 9+) give the hours and minutes. For the total use toHours(), not toHoursPart(): the latter returns the hours after whole days, so for 25 h 45 min it gives 1.

Measure Code Execution Time with Instant

  1. Before the work starts, save the moment Instant.now() and print the start time in the format HH:mm:ss.
  2. Build a string of the numbers from 1 to 20,000 in two ways: with += concatenation in a loop and with a StringBuilder.
  3. For each way, print the execution time in milliseconds with Duration.between(start, end).toMillis().

The exact numbers depend on the computer, but the difference between the two ways will be clear. Output format:

Start: 14:05:31
Concatenation +=: ... ms
StringBuilder: ... ms

Easy to trip over

DateTimeFormatter.ofPattern("HH:mm:ss").format(instant) throws UnsupportedTemporalTypeException: Unsupported field: HourOfDay: an Instant is a point on the UTC time-line with no time zone, so it has no hour of day. Add a zone: .withZone(ZoneId.systemDefault()). For precise micro-benchmarks Instant is not suitable — use System.nanoTime() or the JMH library.

Salary Date in the Employee Report

Employee Report · part 2 of 3 · ← part 2

  1. Add a field LocalDate salaryDate (the payday) to the Employee class from part 1 of the project.
  2. Create a FullReport class that prints the same information as Report and adds a column with the salary date in the format MM/dd/yyyy.

Example output (the column widths are up to you):

John Smith             4500.00  10/30/2026
Mary Johnson           5200.50  10/30/2026

Avoid duplicated code

Do not copy the formatting code from Report. Its generateReport() method is static and cannot be overridden, so move the formatting of one employee row into a separate method of Report and call it from FullReport, adding the date column. DateTimeFormatter.ofPattern("MM/dd/yyyy") is immutable and thread-safe: create it once in a private static final constant, not for every printed row.

Date and Time on the Purchase Receipt

Online Store · part 5 of 5 · ← part 4

Add the date and time of the order to the purchase receipt from the previous part:

Date: 03/28/2019 21:10:58

Product        Price
--------------------
Jeans      $1,500.78
Skirt      $1,000.56
Tie          $500.78
--------------------
Total:     $3,002.12

A pattern detail

Store the purchase time in a LocalDateTime (LocalDateTime.now()) and format it with the pattern MM/dd/yyyy HH:mm:ss. The hours are written with capital HH: lowercase hh gives a 12-hour clock, and 21:10 turns into 09:10 with no AM/PM marker. If you prefer the US 12-hour style, use MM/dd/yyyy hh:mm:ss a with Locale.US, which prints 03/28/2019 09:10:58 PM. All pattern letters are in DateTimeFormatter pattern letters.

Solution on Patreon →

What to Review Before You Start

All seven exercises need only seven classes from the java.time package. The table shows what each class stores, where it is explained and which exercises use it. Every one of these classes is immutable: plusDays(), withYear() and similar methods return a new object and leave the original unchanged.

Class What it stores Where to read Exercises
LocalDate A date without a time or time zone java.time classes compared LocalDate Methods, Age, Salary Date
LocalTime A time of day without a date java.time classes compared Work Shifts
LocalDateTime A date and time without a time zone java.time classes compared Receipt Date and Time
Instant A point on the UTC time-line Instant in Java Execution Time
Period An amount of time in years, months and days Period in Java Age
Duration An exact amount of time in seconds and nanoseconds Duration in Java Work Shifts, Execution Time
DateTimeFormatter A pattern for turning a date into a string and back DateTimeFormatter in Java Holiday Calendar, Salary Date, Receipt Date and Time

Date and Time Bugs That Compile Fine

Most wrong solutions to java.time exercises come from a wrong letter in a formatting pattern, a lost result of plusDays() and confusing Period with Duration. Such code compiles and often works, but prints a wrong date or throws an exception only on certain data.

What does this code print?

LocalDate date = LocalDate.of(2024, 12, 30);
System.out.println(date.format(DateTimeFormatter.ofPattern("MM/dd/YYYY", Locale.US)));
System.out.println(date.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")));
// Output:
// 12/30/2025
// 12/30/2024

December 30, 2024 is a Monday, and the week it belongs to already counts as week 1 of 2025 under both the US and the ISO-8601 week rules, so the pattern with YYYY prints the «next» year. The bug appears only in the last days of December and the first days of January, which is why it slips through review. The meaning of every letter is in DateTimeFormatter pattern letters. Other frequent slips:

Code Result The right way
ofPattern("hh:mm") for 21:10 09:10 — a 12-hour clock without AM/PM HH:mm, or hh:mm a with Locale.US for 09:10 PM
LocalDate.now().format(ofPattern("mm/dd/yyyy")) UnsupportedTemporalTypeException: Unsupported field: MinuteOfHour — mm means minutes MM/dd/yyyy
date.plusDays(1); without assignment The date does not change: the object is immutable date = date.plusDays(1);
LocalDate.of(2025, 2, 29) DateTimeException: Invalid date 'February 29' as '2025' is not a leap year Check Year.isLeap(2025) or build the date with withYear() — it moves February 29 to February 28
Period.between(LocalDate.of(2025, 1, 1), LocalDate.of(2025, 3, 1)).getDays() 0 — the period is P2M, and the days are only the remainder ChronoUnit.DAYS.between(a, b) → 59
Duration.between(LocalTime.of(22, 0), LocalTime.of(6, 0)) PT-16H — a negative duration Store the start and the end as LocalDateTime, or add a day if isNegative()

Check your code

Before you compare your work with a solution, go through the code style checklist: meaningful variable names, DateTimeFormatter patterns kept in constants, «today» passed in as a parameter or a Clock instead of calling now() deep inside a method, and no duplicated code between the report classes.

Frequently Asked Questions

Can I use java.util.Date and SimpleDateFormat in these exercises?

Not in new code. Date is mutable, Calendar numbers months from zero, and SimpleDateFormat is not thread-safe, so one shared formatter can corrupt dates under load. The java.time classes have none of these problems. If an old API requires a Date, convert at the boundary with date.toInstant() and Date.from(instant); the full conversion table is in Date vs LocalDate.

How do I calculate the number of days between two dates in Java?

ChronoUnit.DAYS.between(start, end) or start.until(end, ChronoUnit.DAYS) return the number of days as a long; if end is before start, the result is negative. Period.between() is the wrong tool here: it splits the interval into years, months and days, so getDays() returns only the remaining days. The example with the difference is in the section Date and Time Bugs That Compile Fine.

How do I test code that calls LocalDate.now()?

Do not call the no-argument now() inside the method under test: it reads the system clock and the JVM default time zone, so near midnight «today» on a UTC server differs from today in New York. Pass a Clock instead: LocalDate.now(clock) with Clock.systemDefaultZone() in production and Clock.fixed(Instant.parse("2026-10-20T12:00:00Z"), ZoneOffset.UTC) in tests. For simple methods, such as the age exercise, you can pass today as a LocalDate parameter. More in now(ZoneId) and now(Clock).

Why does LocalDate.parse() throw DateTimeParseException for 3/5/2024?

Without a formatter, LocalDate.parse() accepts only the ISO format yyyy-MM-dd, such as 2024-03-05, so the US-style string fails with DateTimeParseException: Text '3/5/2024' could not be parsed at index 0. Pass a pattern: LocalDate.parse(text, DateTimeFormatter.ofPattern("M/d/yyyy")). The single letters M and d accept both 3 and 03 when parsing, while MM/dd/yyyy requires exactly two digits and rejects 3/5/2024. More in format() and parse().

Comments

Please log in or register to have a possibility to add comment.