Business Days Guide¶
Comprehensive guide to working with business days, weekends, and business day calculations in Carbonic.
Overview¶
Carbonic provides built-in support for business day operations: - Weekend detection (Saturday and Sunday) - Business day arithmetic with automatic weekend skipping - Weekday validation for scheduling and planning - Clean integration with Date and DateTime objects
Basic Concepts¶
Weekdays vs Weekends¶
from carbonic import Date
# Create some test dates
monday = Date(2024, 1, 15) # Monday
friday = Date(2024, 1, 19) # Friday
saturday = Date(2024, 1, 20) # Saturday
sunday = Date(2024, 1, 21) # Sunday
# Check if dates are weekdays
print(f"Monday is weekday: {monday.is_weekday()}") # True
print(f"Friday is weekday: {friday.is_weekday()}") # True
print(f"Saturday is weekday: {saturday.is_weekday()}") # False
print(f"Sunday is weekday: {sunday.is_weekday()}") # False
# Check if dates are weekends
print(f"Saturday is weekend: {saturday.is_weekend()}") # True
print(f"Sunday is weekend: {sunday.is_weekend()}") # True
print(f"Monday is weekend: {monday.is_weekend()}") # False
Week Structure¶
Carbonic follows the ISO 8601 standard where: - Monday = 1 (first day of week) - Sunday = 7 (last day of week) - Weekdays = Monday through Friday (1-5) - Weekend = Saturday and Sunday (6-7)
from carbonic import Date
date = Date(2024, 1, 15) # Monday
print(f"Day of week: {date.weekday}") # 1 (Monday)
# Check each day of the week
dates = [Date(2024, 1, 15 + i) for i in range(7)] # Monday to Sunday
for date in dates:
day_name = date.format("l")
day_num = date.weekday
is_business = date.is_weekday()
print(f"{day_name} ({day_num}): Business day = {is_business}")
Business Day Arithmetic¶
Adding Business Days¶
from carbonic import Date
start_date = Date(2024, 1, 15) # Monday
print(f"Start: {start_date.format('l, F j')} (Monday)")
# Add business days
next_business_day = start_date.add_business_days(1)
print(f"1 business day later: {next_business_day.format('l, F j')}") # Tuesday
# Add multiple business days
five_business_days = start_date.add_business_days(5)
print(f"5 business days later: {five_business_days.format('l, F j')}") # Monday (next week)
# Starting from Friday
friday = Date(2024, 1, 19) # Friday
next_from_friday = friday.add_business_days(1)
print(f"1 business day after Friday: {next_from_friday.format('l, F j')}") # Monday (skips weekend)
Adding Business Days from Weekends¶
from carbonic import Date
# Starting from Saturday
saturday = Date(2024, 1, 20) # Saturday
print(f"Start: {saturday.format('l, F j')} (Saturday)")
# Add business days from weekend
next_business = saturday.add_business_days(1)
print(f"1 business day later: {next_business.format('l, F j')}") # Monday
# Add multiple business days from weekend
three_business = saturday.add_business_days(3)
print(f"3 business days later: {three_business.format('l, F j')}") # Wednesday
Subtracting Business Days¶
from carbonic import Date
start_date = Date(2024, 1, 19) # Friday
print(f"Start: {start_date.format('l, F j')} (Friday)")
# Subtract business days
prev_business_day = start_date.subtract_business_days(1)
print(f"1 business day earlier: {prev_business_day.format('l, F j')}") # Thursday
# Subtract multiple business days
five_business_days_ago = start_date.subtract_business_days(5)
print(f"5 business days earlier: {five_business_days_ago.format('l, F j')}") # Friday (previous week)
# Subtracting from Monday
monday = Date(2024, 1, 22) # Monday
prev_from_monday = monday.subtract_business_days(1)
print(f"1 business day before Monday: {prev_from_monday.format('l, F j')}") # Friday (skips weekend)
Zero Business Days¶
from carbonic import Date
# Adding zero business days
date = Date(2024, 1, 20) # Saturday
same_date = date.add_business_days(0)
print(f"Original: {date}")
print(f"Zero business days added: {same_date}")
print(f"Same date: {date == same_date}") # True
Practical Examples¶
Project Planning¶
from carbonic import Date
def calculate_project_schedule(start_date, business_days_required):
"""Calculate project end date given business days required."""
if not start_date.is_weekday():
# If starting on weekend, move to next Monday
actual_start = start_date.add_business_days(0) # Moves to next business day
print(f"Adjusted start date from {start_date} to {actual_start}")
start_date = actual_start
end_date = start_date.add_business_days(business_days_required - 1) # -1 because start day counts
return start_date, end_date
# Example: 10-day project starting on Friday
project_start = Date(2024, 1, 19) # Friday
start, end = calculate_project_schedule(project_start, 10)
print(f"Project starts: {start.format('l, F j')} (business day 1)")
print(f"Project ends: {end.format('l, F j')} (business day 10)")
# Calculate actual calendar days
calendar_days = (end - start).days + 1
print(f"Total calendar days: {calendar_days}")
Meeting Scheduler¶
from carbonic import Date, DateTime
def schedule_next_meeting(last_meeting_date, frequency_business_days=5):
"""Schedule next meeting on a business day."""
next_date = last_meeting_date.add_business_days(frequency_business_days)
return next_date
def schedule_weekly_meetings(start_date, num_weeks=4):
"""Schedule weekly meetings, always on business days."""
meetings = []
current_date = start_date
# Ensure start date is a business day
if not current_date.is_weekday():
current_date = current_date.add_business_days(0)
for week in range(num_weeks):
meetings.append(current_date)
current_date = schedule_next_meeting(current_date, 5) # Every 5 business days (weekly)
return meetings
# Schedule meetings starting from a Saturday
start = Date(2024, 1, 20) # Saturday
meetings = schedule_weekly_meetings(start, 4)
print("Scheduled meetings:")
for i, meeting in enumerate(meetings, 1):
print(f"Meeting {i}: {meeting.format('l, F j')} ({meeting.format('Y-m-d')})")
Business Day Counter¶
from carbonic import Date
def count_business_days_between(start_date, end_date):
"""Count business days between two dates (inclusive)."""
if start_date > end_date:
start_date, end_date = end_date, start_date
business_days = 0
current_date = start_date
while current_date <= end_date:
if current_date.is_weekday():
business_days += 1
current_date = current_date.add(days=1)
return business_days
# Example: Count business days in January 2024
jan_start = Date(2024, 1, 1)
jan_end = Date(2024, 1, 31)
business_days_in_jan = count_business_days_between(jan_start, jan_end)
total_days_in_jan = (jan_end - jan_start).days + 1
print(f"January 2024:")
print(f"Total days: {total_days_in_jan}")
print(f"Business days: {business_days_in_jan}")
print(f"Weekend days: {total_days_in_jan - business_days_in_jan}")
SLA Calculator¶
from carbonic import Date, DateTime
class BusinessDaySLA:
"""Calculate SLA deadlines based on business days."""
def __init__(self, business_days=5):
self.business_days = business_days
def calculate_deadline(self, request_date):
"""Calculate SLA deadline from request date."""
if isinstance(request_date, DateTime):
request_date = request_date.to_date()
deadline = request_date.add_business_days(self.business_days)
return deadline
def is_overdue(self, request_date, current_date=None):
"""Check if request is overdue."""
if current_date is None:
current_date = Date.today()
deadline = self.calculate_deadline(request_date)
return current_date > deadline
def days_remaining(self, request_date, current_date=None):
"""Calculate business days remaining until deadline."""
if current_date is None:
current_date = Date.today()
deadline = self.calculate_deadline(request_date)
if current_date > deadline:
# Count business days overdue
business_days = 0
check_date = deadline.add(days=1)
while check_date <= current_date:
if check_date.is_weekday():
business_days += 1
check_date = check_date.add(days=1)
return -business_days
else:
# Count business days remaining
business_days = 0
check_date = current_date.add(days=1)
while check_date <= deadline:
if check_date.is_weekday():
business_days += 1
check_date = check_date.add(days=1)
return business_days
# Example usage
sla = BusinessDaySLA(business_days=3) # 3 business day SLA
# Request submitted on Friday
request_date = Date(2024, 1, 19) # Friday
deadline = sla.calculate_deadline(request_date)
print(f"Request submitted: {request_date.format('l, F j')}")
print(f"SLA deadline: {deadline.format('l, F j')}")
# Check status on different days
check_dates = [
Date(2024, 1, 22), # Monday
Date(2024, 1, 23), # Tuesday
Date(2024, 1, 24), # Wednesday (deadline)
Date(2024, 1, 25), # Thursday (overdue)
]
for check_date in check_dates:
overdue = sla.is_overdue(request_date, check_date)
remaining = sla.days_remaining(request_date, check_date)
status = "OVERDUE" if overdue else "ON TIME"
if remaining > 0:
time_info = f"{remaining} business days remaining"
elif remaining == 0:
time_info = "due today"
else:
time_info = f"{abs(remaining)} business days overdue"
print(f"{check_date.format('l')}: {status} - {time_info}")
Advanced Usage¶
Business Day Validation¶
from carbonic import Date
def validate_business_date(date, field_name="date"):
"""Validate that a date is a business day."""
if not isinstance(date, Date):
raise TypeError(f"{field_name} must be a Date object")
if not date.is_weekday():
day_name = date.format("l")
raise ValueError(f"{field_name} ({date}) falls on {day_name}, which is not a business day")
return date
# Example usage
try:
business_date = validate_business_date(Date(2024, 1, 15)) # Monday - OK
print(f"Valid business date: {business_date}")
except ValueError as e:
print(f"Validation error: {e}")
try:
weekend_date = validate_business_date(Date(2024, 1, 20)) # Saturday - Error
except ValueError as e:
print(f"Validation error: {e}")
Business Day Range Generation¶
from carbonic import Date
def generate_business_days(start_date, end_date):
"""Generate all business days between two dates."""
if start_date > end_date:
start_date, end_date = end_date, start_date
current_date = start_date
while current_date <= end_date:
if current_date.is_weekday():
yield current_date
current_date = current_date.add(days=1)
def get_business_days_in_month(year, month):
"""Get all business days in a specific month."""
start_date = Date(year, month, 1)
# Get last day of month by going to next month and subtracting 1 day
next_month = start_date.add(months=1)
end_date = next_month.subtract(days=1)
return list(generate_business_days(start_date, end_date))
# Example: Get all business days in January 2024
business_days = get_business_days_in_month(2024, 1)
print(f"Business days in January 2024: {len(business_days)}")
for i, date in enumerate(business_days[:5], 1): # Show first 5
print(f"{i:2d}. {date.format('l, F j')} ({date})")
print(f"... and {len(business_days) - 5} more business days")
Holiday Awareness (Basic)¶
from carbonic import Date
def is_holiday(date, holidays=None):
"""Check if date is a holiday (basic implementation)."""
if holidays is None:
# Basic US federal holidays (fixed dates only)
holidays = [
Date(date.year, 1, 1), # New Year's Day
Date(date.year, 7, 4), # Independence Day
Date(date.year, 12, 25), # Christmas Day
]
return date in holidays
def add_business_days_excluding_holidays(start_date, business_days, holidays=None):
"""Add business days while excluding holidays."""
current_date = start_date
days_added = 0
while days_added < business_days:
current_date = current_date.add(days=1)
if current_date.is_weekday() and not is_holiday(current_date, holidays):
days_added += 1
return current_date
# Example: Add 5 business days from December 20, 2024
start = Date(2024, 12, 20) # Friday before Christmas
end = add_business_days_excluding_holidays(start, 5)
print(f"Start: {start.format('l, F j')}")
print(f"5 business days later (excluding Christmas): {end.format('l, F j')}")
Performance Considerations¶
Business day calculations are optimized for common use cases:
from carbonic import Date
import time
# Efficient for reasonable ranges
start_time = time.time()
start_date = Date(2024, 1, 1)
result = start_date.add_business_days(100) # Fast
end_time = time.time()
print(f"Added 100 business days in {(end_time - start_time) * 1000:.2f}ms")
# For very large ranges, consider chunking
def add_business_days_chunked(start_date, business_days, chunk_size=50):
"""Add business days in chunks for very large ranges."""
current_date = start_date
remaining = business_days
while remaining > 0:
chunk = min(remaining, chunk_size)
current_date = current_date.add_business_days(chunk)
remaining -= chunk
return current_date
See Also¶
- Date Guide - Complete Date operations
- DateTime Guide - DateTime with business day support
- Examples - More business day examples
- API Reference - Complete business day API