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
- Create a
LocalDateobject with your date of birth usingLocalDate.of(year, month, day). - Print the day of the week, the day of the year, the month and the year of this date.
- Create a
LocalDateobject with the current date. - Compare the current date with your date of birth using
isAfter(),isBefore()andisEqual(), 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: trueHint
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
- Get the current year with
Year.now()orLocalDate.now().getYear(). - 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. - 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 DayWhere 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
- Store a date of birth as a
LocalDate. - Use
Period.between()to print the exact age: full years, months and days. - 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.
- 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: 146A 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
- Define several shifts as pairs of
LocalTimevalues «start — end», including at least one night shift: 09:00–18:00, 08:30–17:15, 22:00–06:00. - For each shift, print its length in hours and minutes using
Duration. - 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 minThe 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
- Before the work starts, save the moment
Instant.now()and print the start time in the formatHH:mm:ss. - Build a string of the numbers from 1 to 20,000 in two ways: with
+=concatenation in a loop and with aStringBuilder. - 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: ... msEasy 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
- Add a field
LocalDate salaryDate(the payday) to theEmployeeclass from part 1 of the project. - Create a
FullReportclass that prints the same information asReportand adds a column with the salary date in the formatMM/dd/yyyy.
Example output (the column widths are up to you):
John Smith 4500.00 10/30/2026
Mary Johnson 5200.50 10/30/2026Avoid 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.
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