The February 29th Crash
The Crime
February 29th: the day that exists only once every four years, yet manages to crash systems with clockwork regularity. From Microsoft Azure's global outage to PlayStation consoles becoming expensive paperweights, leap day exposes the dangerous assumptions developers make about calendar arithmetic and date validation.
Real-World Impact
Microsoft Azure Global Outage (February 29, 2012)
Incident: Azure Storage service crashed globally due to leap year certificate validation bug
Duration: 2.5 hours of complete outage, 8+ hours for full recovery
Root Cause: Certificate validation code couldn't handle February 29th dates
Affected Services: Azure Storage, SQL Azure, Service Bus, Access Control
Impact: Thousands of applications offline, estimated $millions in lost revenue
Microsoft Quote: "The leap year. It's a special case that only happens every four years, and we missed it."
PlayStation Network Leap Year Bug (March 1, 2010)
Incident: PS3 consoles worldwide became unusable due to leap year calculation error
Affected Models: Original "Fat" PS3 consoles (not PS3 Slim)
Symptoms: Consoles couldn't connect to PSN, games wouldn't start, system clock reset
Root Cause: Internal clock treated 2010 as a leap year (it's not)
Duration: 24 hours until automatic fix (date rolled over to March 2)
Impact: Millions of consoles affected, no permanent damage but widespread panic
European Banking Network (February 29, 2016)
Incident: ATM network across multiple countries failed during leap day
Affected Countries: Germany, Netherlands, Belgium
Root Cause: Date parsing library couldn't handle "29/02/2016" format
Duration: 6 hours of intermittent failures
Impact: 15,000+ ATMs offline, customer service chaos
Recovery: Emergency patch deployed, manual date override
Enterprise ERP System (February 29, 2020)
Incident: Fortune 100 company's payroll system crashed on leap day
Root Cause: Hardcoded assumption that February has maximum 28 days
Impact: 50,000+ employees not paid on time, HR crisis
Emergency Fix: Manual payroll processing, $2M in overtime costs
Quote: "We tested everything except the one day that only exists every four years."
Technical Analysis
The Leap Year Rules (It's Complicated)
Rule 1: Divisible by 4
Years divisible by 4 are leap years. 2020, 2024 ✓
Rule 2: Except Century Years
Years divisible by 100 are NOT leap years. 1900, 2100 ✗
Rule 3: Except Exception Years
Years divisible by 400 ARE leap years. 2000, 2400 ✓
Common Mistake
Most developers only implement Rule 1, missing the century exceptions. This causes bugs every 100 years!
Common Failure Patterns
1. Hardcoded Day Limits
Code that assumes February always has 28 days: if (month == 2 && day > 28)
2. Incomplete Leap Year Logic
Only checking divisibility by 4: year % 4 == 0
3. Date Parsing Failures
Libraries that reject "29/02/YYYY" as invalid without proper leap year checking
4. Certificate Validation
SSL/TLS certificates with February 29 expiry dates causing validation failures
5. Database Constraints
CHECK constraints that don't account for leap years: day <= 28
Azure Incident: The Certificate Validation Bug
Code Examples
❌ Problematic: Hardcoded February Limit
// This will crash on February 29th
function validateDate(month, day, year) {
const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (day > daysInMonth[month - 1]) {
throw new Error("Invalid date");
}
return true;
}
// February 29, 2024 will be rejected as invalid!
❌ Problematic: Incomplete Leap Year Logic
// This is wrong! Missing century rules
function isLeapYear(year) {
return year % 4 === 0;
}
// This will incorrectly say 1900 is a leap year
// 1900 % 4 === 0, but 1900 is NOT a leap year!
✅ Safe: Proper Leap Year Calculation
function isLeapYear(year) {
// Correct leap year logic with all three rules
if (year % 400 === 0) return true; // Rule 3: Divisible by 400
if (year % 100 === 0) return false; // Rule 2: Not divisible by 100
if (year % 4 === 0) return true; // Rule 1: Divisible by 4
return false;
}
function getDaysInMonth(month, year) {
const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (month === 2 && isLeapYear(year)) {
return 29; // February in leap year
}
return daysInMonth[month - 1];
}
function validateDate(month, day, year) {
if (month < 1 || month > 12) {
throw new Error("Invalid month");
}
const maxDays = getDaysInMonth(month, year);
if (day < 1 || day > maxDays) {
throw new Error(`Invalid day for ${month}/${year}`);
}
return true;
}
✅ Safe: Database Schema with Leap Year Support
-- PostgreSQL: Safe date constraints
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_date DATE NOT NULL,
-- Don't use hardcoded day limits!
-- CONSTRAINT bad_check CHECK (EXTRACT(DAY FROM event_date) <= 28)
-- Instead, let the database handle date validation
CONSTRAINT valid_date CHECK (event_date IS NOT NULL)
);
-- Function to validate leap year dates
CREATE OR REPLACE FUNCTION is_valid_date(y INT, m INT, d INT)
RETURNS BOOLEAN AS $$
BEGIN
-- Let PostgreSQL do the validation
PERFORM make_date(y, m, d);
RETURN TRUE;
EXCEPTION
WHEN others THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
✅ Safe: Python with Built-in Date Handling
from datetime import datetime, date
import calendar
def validate_date_safe(year, month, day):
"""Use Python's built-in date validation"""
try:
# This will raise ValueError for invalid dates
date(year, month, day)
return True
except ValueError as e:
print(f"Invalid date: {e}")
return False
def get_days_in_month_safe(year, month):
"""Use calendar module for accurate day counts"""
return calendar.monthrange(year, month)[1]
# Test with leap year dates
print(validate_date_safe(2024, 2, 29)) # True - valid leap day
print(validate_date_safe(2023, 2, 29)) # False - not a leap year
print(validate_date_safe(1900, 2, 29)) # False - century year exception
print(validate_date_safe(2000, 2, 29)) # True - 400-year exception
# Safe way to check if year is leap year
def is_leap_year_safe(year):
return calendar.isleap(year)
✅ Safe: Certificate Date Validation
// C# - Azure-style certificate validation fix
public static bool ValidateCertificateDate(DateTime certDate)
{
try
{
// Use .NET's built-in date validation
// This properly handles leap years
var validDate = new DateTime(certDate.Year, certDate.Month, certDate.Day);
// Check if certificate is still valid
return validDate >= DateTime.UtcNow;
}
catch (ArgumentOutOfRangeException)
{
// Invalid date in certificate
return false;
}
}
// The bug was likely something like this:
public static bool ValidateCertificateDateBuggy(int year, int month, int day)
{
// Hardcoded validation that fails on Feb 29
if (month == 2 && day > 28)
{
return false; // BUG: Rejects valid leap day!
}
// ... rest of validation
return true;
}
Prevention Strategies
1. Use Standard Libraries
- • Never implement date logic from scratch
- • Use language built-ins (Date, DateTime, calendar)
- • Trust battle-tested libraries over custom code
- • Avoid hardcoded day/month limits
2. Test Leap Year Scenarios
- • Include February 29th in test data
- • Test century years (1900, 2100)
- • Test 400-year exceptions (2000, 2400)
- • Automate leap year edge case testing
3. Validate All Date Inputs
- • Validate dates at system boundaries
- • Use try/catch for date construction
- • Reject invalid dates gracefully
- • Log date validation failures
4. Monitor Date-Related Operations
- • Alert on date parsing failures
- • Monitor certificate expiry validation
- • Track date-related error rates
- • Set up leap year monitoring
Lessons Learned
1. February 29th Is Not an Edge Case
Leap day happens every 4 years like clockwork. It's a regular, predictable event that should be part of standard testing.
2. Date Logic Is Harder Than It Looks
Leap year rules have three conditions, not one. Century years, 400-year exceptions, and other calendar quirks make date arithmetic complex.
3. Trust Standard Libraries
Don't reinvent date validation. Use your language's built-in date libraries - they've already solved these problems.
4. Test the Calendar Extremes
Include leap days, century years, and month boundaries in your test data. If your system handles dates, it must handle ALL dates.
Related Crimes
Age Calculation Catastrophe
When leap years break age calculations (Coming Soon)
Y2K Leap Year Oversight
Y2K fixes that broke leap year logic (Coming Soon)
📢 Share Your Leap Year Horror Story
Has February 29th crashed your systems? Share your leap year disaster story and help others avoid the same fate. The best submissions get featured in our hall of shame.