flashman
← All guides

Cron calendar edge cases for month-end and leap years

Design reliable calendar schedules across short months, leap years, business-day rules, timezones, missed dates, retries, and next-run monitoring.

2026-08-31 · 8 min read

  • cron
  • scheduling
  • reliability

Cron fields select matching calendar values; they do not automatically reinterpret an impossible date. A job constrained to day 31 has no match in April, June, September, November, or February, and February 29 has no occurrence in most years.

Reliable scheduling starts by separating the business rule from the expression. Last calendar day, last business day, every 30 days, and day 30 are different requirements with different recovery and timezone behavior.

Translate intent into calendar cases

Write examples for every month before choosing an expression. Include leap and non-leap February, year boundaries, holidays if relevant, and the timezone whose calendar controls the decision.

  • Define whether the run belongs to the opening or closing period.
  • Define what happens when the target date does not exist.
  • Define whether weekends and holidays move the run.
  • Define the latest acceptable completion time.

Use application logic for richer calendars

Traditional five-field cron cannot directly express every last-day or business-day rule. A daily trigger can evaluate the calendar in idempotent application code, using a maintained holiday source when the requirement depends on a business calendar.

Persist a logical period key such as billing-month plus task name. Acquire work atomically so scheduler duplication, retries, manual recovery, and overlapping daily checks cannot repeat the period's side effect.

Make missing work observable

A green scheduler heartbeat does not prove that a sparse calendar expression produced the intended run. Calculate and store the next expected logical occurrence, then alert when completion misses its deadline.

  • Preview sparse schedules across multiple years.
  • Record nominal date, dispatch time, timezone, and logical key.
  • Distinguish an impossible date from a failed dispatch.
  • Provide a controlled backfill path with the same idempotency guard.

Test the complete lifecycle

Use Flashman's cron helper to inspect expressions, timestamp converter to align UTC and local events, JSON formatter for scheduler messages, diff tool for configuration reviews, and UUID generator for synthetic dispatch IDs.

Test every month length, leap-year rules, year transitions, daylight-saving changes, scheduler downtime, delayed and duplicate delivery, retries, manual backfills, and concurrent workers. Assert the logical period processed, not only the wall-clock trigger.

Try these tools