Skip to content

Date Generation Utilities

MoneyWarp provides convenient date generation functions powered by python-dateutil for robust and intelligent date arithmetic.

Why Date Generation Matters

Creating payment schedules manually is error-prone and tedious:

# ❌ Manual date creation (error-prone)
from datetime import datetime, timedelta

start_date = datetime(2024, 1, 31)
due_dates = []
for i in range(12):
    # This breaks for months with different lengths!
    due_dates.append(start_date + timedelta(days=30*i))

MoneyWarp's date utilities handle edge cases automatically:

# ✅ Smart date generation (robust)
from money_warp import generate_monthly_dates
from datetime import datetime

due_dates = generate_monthly_dates(datetime(2024, 1, 31), 12)
# Handles Feb 29, month lengths, leap years automatically!

Available Functions

Monthly Dates

Generate monthly payment schedules with intelligent end-of-month handling:

from money_warp import generate_monthly_dates
from datetime import datetime

# Basic monthly dates
dates = generate_monthly_dates(datetime(2024, 1, 15), 12)
print(f"12 monthly payments starting Jan 15")

# End-of-month intelligence
eom_dates = generate_monthly_dates(datetime(2024, 1, 31), 4)
# Results: [Jan 31, Feb 29, Mar 31, Apr 30]
# Each date anchors to the original day (31st), clamped per month

Bi-weekly Dates

Perfect for payroll-aligned payment schedules:

from money_warp import generate_biweekly_dates

# 26 bi-weekly payments (roughly 1 year)
dates = generate_biweekly_dates(datetime(2024, 1, 1), 26)
print(f"Payment every 14 days")

# Great for matching payroll schedules
payroll_dates = generate_biweekly_dates(datetime(2024, 1, 5), 26)  # Fridays

Weekly Dates

For high-frequency payment schedules:

from money_warp import generate_weekly_dates

# Weekly payments for a year
dates = generate_weekly_dates(datetime(2024, 1, 1), 52)
print(f"52 weekly payments")

Quarterly Dates

Business-friendly quarterly schedules:

from money_warp import generate_quarterly_dates

# Quarterly payments
dates = generate_quarterly_dates(datetime(2024, 1, 15), 8)  # 2 years
print(f"Quarterly payments: Q1, Q2, Q3, Q4...")

# End-of-quarter example
quarter_end = generate_quarterly_dates(datetime(2024, 3, 31), 4)
# Results: [Mar 31, Jun 30, Sep 30, Dec 31]
# Anchors to original day (31st), clamped for 30-day months

Annual Dates

For long-term loans and investments:

from money_warp import generate_annual_dates

# 30-year mortgage payments
dates = generate_annual_dates(datetime(2024, 1, 1), 30)

# Leap year handling
leap_dates = generate_annual_dates(datetime(2024, 2, 29), 5)
# Results: [2024-02-29, 2025-02-28, 2026-02-28, 2027-02-28, 2028-02-29]
# Anchors to 29th — returns to Feb 29 when the next leap year arrives

Custom Intervals

For any custom payment frequency:

from money_warp import generate_custom_interval_dates

# Every 45 days
dates = generate_custom_interval_dates(datetime(2024, 1, 1), 8, 45)

# Every 10 days (short-term financing)
short_term = generate_custom_interval_dates(datetime(2024, 1, 1), 12, 10)

Integration with Loans

Generators return List[datetime]; Loan expects calendar due_dates: List[date]. Convert with to_date from money_warp.tz:

from datetime import datetime

from money_warp import Loan, Money, InterestRate, generate_monthly_dates
from money_warp.tz import to_date

# Generate payment dates, then strip to calendar dates for the loan
due_dates = [to_date(d) for d in generate_monthly_dates(datetime(2024, 1, 15), 24)]

# Create loan with generated dates
loan = Loan(
    principal=Money("25000"),
    interest_rate=InterestRate("4.5% annual"),
    due_dates=due_dates
)

# Get payment schedule
schedule = loan.get_amortization_schedule()
print(f"Monthly payment: {schedule[0].payment_amount}")

Edge Cases Handled

End-of-Month Intelligence

# Starting on January 31st
dates = generate_monthly_dates(datetime(2024, 1, 31), 6)

# Results (each date anchored to original day 31):
# Jan 31 (start)
# Feb 29 (leap year, clamped from 31)
# Mar 31 (back to 31)
# Apr 30 (April has 30 days, clamped)
# May 31 (back to 31)
# Jun 30 (June has 30 days, clamped)

for i, date in enumerate(dates, 1):
    print(f"Payment {i}: {date} (day {date.day})")

Leap Year Handling

# Annual payments starting on leap day
leap_dates = generate_annual_dates(datetime(2024, 2, 29), 5)

# Results (anchored to 29th):
# 2024-02-29 (leap year)
# 2025-02-28 (not leap year, clamped)
# 2026-02-28 (not leap year, clamped)
# 2027-02-28 (not leap year, clamped)
# 2028-02-29 (leap year again, back to 29)

Quarter-End Variations

# Starting at end of March (31 days)
quarter_dates = generate_quarterly_dates(datetime(2024, 3, 31), 4)

# Results (anchored to 31st):
# Mar 31 (start)
# Jun 30 (June has 30 days, clamped)
# Sep 30 (September has 30 days, clamped)
# Dec 31 (back to 31)

Real-World Examples

Mortgage with Monthly Payments

from datetime import datetime

from money_warp import Loan, Money, InterestRate, generate_monthly_dates
from money_warp.tz import to_date

# 30-year mortgage starting mid-month
start_date = datetime(2024, 1, 15)
payment_dates = [to_date(d) for d in generate_monthly_dates(start_date, 360)]  # 30 years * 12 months

mortgage = Loan(
    principal=Money("400000"),  # $400k house
    interest_rate=InterestRate("6.5% annual"),
    due_dates=payment_dates
)

print(f"30-year mortgage with {len(payment_dates)} payments")
print(f"First payment: {payment_dates[0]}")
print(f"Final payment: {payment_dates[-1]}")

Bi-weekly Auto Loan

from datetime import datetime

from money_warp import Loan, Money, InterestRate, generate_biweekly_dates
from money_warp.tz import to_date

# Bi-weekly auto loan (pays off faster)
biweekly_dates = [to_date(d) for d in generate_biweekly_dates(datetime(2024, 1, 5), 130)]  # ~5 years

auto_loan = Loan(
    principal=Money("35000"),
    interest_rate=InterestRate("7.2% annual"),
    due_dates=biweekly_dates
)

print(f"Bi-weekly auto loan: {len(biweekly_dates)} payments")

Business Quarterly Loan

from datetime import datetime

from money_warp import Loan, Money, InterestRate, generate_quarterly_dates
from money_warp.tz import to_date

# Business loan with quarterly payments
quarterly_dates = [to_date(d) for d in generate_quarterly_dates(datetime(2024, 3, 31), 20)]  # 5 years

business_loan = Loan(
    principal=Money("100000"),
    interest_rate=InterestRate("8.5% annual"),
    due_dates=quarterly_dates
)

print(f"Business loan: {len(quarterly_dates)} quarterly payments")

API Reference

Function Signatures

def generate_monthly_dates(start_date: datetime, num_payments: int) -> List[datetime]:
    """Generate monthly payment dates with smart end-of-month handling."""

def generate_biweekly_dates(start_date: datetime, num_payments: int) -> List[datetime]:
    """Generate bi-weekly payment dates (every 14 days)."""

def generate_weekly_dates(start_date: datetime, num_payments: int) -> List[datetime]:
    """Generate weekly payment dates (every 7 days)."""

def generate_quarterly_dates(start_date: datetime, num_payments: int) -> List[datetime]:
    """Generate quarterly payment dates (every 3 months)."""

def generate_annual_dates(start_date: datetime, num_payments: int) -> List[datetime]:
    """Generate annual payment dates (every 12 months)."""

def generate_custom_interval_dates(
    start_date: datetime, num_payments: int, interval_days: int
) -> List[datetime]:
    """Generate payment dates with custom day intervals."""

Error Handling

All functions validate inputs and raise clear errors:

# Invalid payment count
try:
    generate_monthly_dates(datetime(2024, 1, 1), 0)
except ValueError as e:
    print(e)  # "Number of payments must be positive"

# Invalid interval
try:
    generate_custom_interval_dates(datetime(2024, 1, 1), 5, -1)
except ValueError as e:
    print(e)  # "Interval days must be positive"

Key Benefits

Robust Date Arithmetic

  • Powered by python-dateutil: Industry-standard date manipulation
  • Smart month handling: Handles varying month lengths automatically
  • Leap year aware: Correctly handles February 29th edge cases

Simple API

  • Type-safe: Full type annotations and validation
  • Minimal parameters: Just datetime and int, no complex options
  • Consistent behavior: All functions follow the same patterns

Immediate Integration

  • Loan compatibility: Pass [to_date(d) for d in dates] into Loan — generators yield datetime, loan due dates are date
  • Time Machine support: All dates work with Warp for temporal analysis
  • Schedule generation: Seamless integration with payment schedulers

MoneyWarp's date utilities eliminate the complexity and bugs of manual date arithmetic, letting you focus on financial modeling instead of calendar math!