Great work!

XP to next level

BugEater

Recurring Date Generation Testing

Learning Objectives

  • Understand how rule-based recurrence engines generate dates and where they fail
  • Identify the most common edge cases in monthly, weekly, and yearly recurrence
  • Build a systematic test matrix for recurring event features

The Recurrence Problem

Recurring events seem simple: "every Monday," "every month on the 15th," "every year on January 1st." But when the calendar doesn't cooperate — specifically when a date doesn't exist in a given month — recurrence engines must make a decision, and different systems make different decisions.

The classic example: "every month on the 31st."

Month What happens?
January 31 Exists — OK
February 31 Does not exist. Skip? Use Feb 28/29? Use March 3?
March 31 Exists — OK
April 31 Does not exist. Use April 30? Skip?

There is no universally correct answer — it depends on the business rule. But there is always a wrong answer: crashing with an exception or silently skipping months without telling the user.

Language and Library Behavior

// Java: January 31 + 1 month
LocalDate.of(2024, 1, 31).plusMonths(1);
// Result: February 29, 2024 (leap year) — Java clamps to last valid day

// Java in a non-leap year
LocalDate.of(2023, 1, 31).plusMonths(1);
// Result: February 28, 2023

// Adding another month from Feb 28 (not Feb 31!):
LocalDate.of(2023, 2, 28).plusMonths(1);
// Result: March 28 — NOT March 31!
// This is "month drift" — the 31st became the 28th and never recovers

This "month drift" bug is one of the most insidious in date handling. A subscription created on January 31 should renew on February 28/29, March 31, April 30... but a naive +1 month chain starting from Feb 28 will drift to the 28th permanently.

Testing Recurrence Edge Cases

Monthly recurrence test matrix:

Start date +1 month +2 months Notes
Jan 28 Feb 28 Mar 28 No clamping needed
Jan 29 Feb 28/29 Mar 29 Leap year matters
Jan 30 Feb 28/29 Mar 30 Check clamping behavior
Jan 31 Feb 28/29 Mar 31 Month drift risk
Mar 31 Apr 30 May 31 April clamp, then recovery?

Weekly recurrence:

  • Test DST transition weeks — does "every Monday at 9 AM" shift by an hour?
  • Test year boundary — does "every week" generate December 31 and January 1 correctly?

Yearly recurrence:

  • Test February 29 — does "every year on Feb 29" generate Feb 28 or Mar 1 in non-leap years?

What to Verify in Your Bug Report

When a recurrence bug is found, document: the start date, the recurrence rule, the expected sequence, the actual sequence, and the first occurrence where they diverge. Include the timezone, as DST gaps can cause "every day at midnight" to skip or repeat a day.

Quiz

A recurring task is scheduled for "the 31st of every month." What happens to this rule in February?

What date does the Java method LocalDate.of(2024, 1, 31).plusMonths(1) return?

Which test input most directly exposes an end-of-month recurrence bug in a subscription billing system?

What is the safest approach to monthly recurrence when the target day may not exist in every month?