Duration¶
The Duration class represents a span of time - the difference between two moments. It provides a rich API for creating, manipulating, and working with time intervals.
Overview¶
Duration objects are immutable and represent exact amounts of time that can be added to or subtracted from datetime objects. Unlike periods (which are calendar-based), durations represent fixed amounts of time.
from carbonic import Duration, DateTime
# Create a duration
duration = Duration(days=2, hours=3, minutes=30)
print(duration)  # Duration(days=2, hours=3, minutes=30)
# Use with DateTime
dt = DateTime(2024, 1, 15, 10, 0, tz="UTC")
future = dt + duration
print(future)  # 2024-01-17T13:30:00+00:00
Creating Duration Objects¶
Constructor¶
from carbonic import Duration
# All units are optional
duration = Duration(
    days=7,
    hours=12,
    minutes=30,
    seconds=45,
    microseconds=123456
)
# Common patterns
one_hour = Duration(hours=1)
half_day = Duration(hours=12)
three_days = Duration(days=3)
mixed = Duration(days=1, hours=2, minutes=30)
From Total Units¶
from carbonic import Duration
# Create from total values
from_seconds = Duration(seconds=3661)     # 1 hour, 1 minute, 1 second
from_minutes = Duration(minutes=90)       # 1 hour, 30 minutes
from_hours = Duration(hours=25)           # 25 hours
from_days = Duration(days=1, hours=12)    # 1 day, 12 hours
# From microseconds
from_microseconds = Duration(microseconds=1500000)  # 1.5 seconds
From Timedelta¶
from carbonic import Duration
import datetime
# Convert from Python's timedelta
td = datetime.timedelta(days=3, hours=2, minutes=15)
duration = Duration(seconds=int(td.total_seconds()))
print(duration)  # 3.0 days, 2.0 hours, and 15.0 minutes
Zero Duration¶
from carbonic import Duration
# Zero duration
zero = Duration()
print(zero)  # 0 seconds
# Alternative - explicit zero values
zero_alt = Duration(days=0, hours=0, minutes=0, seconds=0)
print(zero_alt)  # 0 seconds
Duration Arithmetic¶
Adding Durations¶
from carbonic import Duration
d1 = Duration(hours=2, minutes=30)
d2 = Duration(hours=1, minutes=45)
# Add durations
total = d1 + d2
print(total)  # Duration(hours=4, minutes=15)
# Chain additions
result = (Duration(hours=1) +
    Duration(minutes=30) +
    Duration(seconds=45)
)
print(result)  # 1 hour, 30 minutes, and 45 seconds
Subtracting Durations¶
from carbonic import Duration
d1 = Duration(hours=3, minutes=30)
d2 = Duration(hours=1, minutes=15)
# Subtract durations
difference = d1 - d2
print(difference)  # Duration(hours=2, minutes=15)
# Same result using operator
result = d1 - d2
print(result)  # 2 hours and 15 minutes
Multiplication and Division¶
from carbonic import Duration
base = Duration(hours=2, minutes=30)
# Multiply by scalar
doubled = base * 2
print(doubled)  # 5 hours
# Division not directly supported, but can create new duration
half_seconds = base.total_seconds() / 2
halved = Duration(seconds=int(half_seconds))
print(halved)  # Duration for half the time
Negation and Absolute Value¶
from carbonic import Duration
duration = Duration(hours=2, minutes=30)
# Negate
negative = -duration
print(negative)  # Duration(days=0, hours=-2, minutes=-30)
# Absolute value
abs_duration = abs(negative)
print(abs_duration)  # Duration(hours=2, minutes=30)
# Check if negative
print(duration < Duration())   # False
print(negative < Duration())   # True
print(duration > Duration())   # True
Using Durations with DateTime¶
Adding to DateTime¶
from carbonic import DateTime, Duration
dt = DateTime(2024, 1, 15, 10, 0, tz="UTC")
duration = Duration(days=3, hours=2, minutes=30)
# Add duration to datetime
future = dt + duration
print(future)  # 2024-01-18T12:30:00+00:00
# Alternative syntax
future_alt = dt.add_duration(duration)
print(future_alt)  # Same result
Subtracting from DateTime¶
from carbonic import DateTime, Duration
dt = DateTime(2024, 1, 15, 10, 0, tz="UTC")
duration = Duration(days=2, hours=1)
# Subtract duration from datetime
past = dt - duration
print(past)  # 2024-01-13T09:00:00+00:00
# Alternative syntax
past_alt = dt.subtract_duration(duration)
print(past_alt)  # Same result
DateTime Differences Result in Durations¶
from carbonic import DateTime
start = DateTime(2024, 1, 15, 10, 0, tz="UTC")
end = DateTime(2024, 1, 17, 14, 30, tz="UTC")
# Get duration between datetimes
duration = end - start
print(duration)  # Duration(days=2, hours=4, minutes=30)
print(type(duration))  # <class 'carbonic.core.duration.Duration'>
Conversion and Access¶
Total Values¶
from carbonic import Duration
duration = Duration(days=2, hours=3, minutes=30, seconds=45)
# Get total values in different units
print(duration.total_seconds())      # 183045.0
print(duration.in_minutes())      # 3050.75
print(duration.in_hours())        # 50.845833333333336
print(duration.in_days())         # 2.118576388888889
# Get individual components
print(duration.days)                    # 2
total_seconds = duration.storage_seconds
remaining_hours = (total_seconds // 3600) % 24
remaining_minutes = (total_seconds // 60) % 60
remaining_seconds = total_seconds % 60
print(f"Remaining hours: {remaining_hours}")    # 3 (remaining after days)
print(f"Remaining minutes: {remaining_minutes}") # 30 (remaining after hours)
print(f"Remaining seconds: {remaining_seconds}") # 45 (remaining after minutes)
print(duration.microseconds)           # 0
Component Access¶
from carbonic import Duration
duration = Duration(days=5, hours=25, minutes=90)  # Overflow handled
print(f"Days: {duration.days}")           # 6 (25 hours = 1 day + 1 hour)
# Calculate remaining components
total_seconds = duration.storage_seconds
remaining_hours = (total_seconds // 3600) % 24
remaining_minutes = (total_seconds // 60) % 60
remaining_seconds = total_seconds % 60
print(f"Remaining hours: {remaining_hours}")    # 1 hour remaining after days
print(f"Remaining minutes: {remaining_minutes}") # 30 minutes remaining
print(f"Remaining seconds: {remaining_seconds}") # 0 seconds
To Python Timedelta¶
from carbonic import Duration
duration = Duration(days=3, hours=2, minutes=15)
# Convert to timedelta
import datetime
td = datetime.timedelta(seconds=duration.total_seconds())
print(type(td))  # <class 'datetime.timedelta'>
print(td)        # 3 days, 2:15:00
Comparisons¶
Basic Comparisons¶
from carbonic import Duration
d1 = Duration(hours=2)
d2 = Duration(minutes=120)  # Same as 2 hours
d3 = Duration(hours=3)
# Equality
print(d1 == d2)  # True (same duration)
print(d1 == d3)  # False
# Ordering
print(d1 < d3)   # True
print(d3 > d1)   # True
print(d1 <= d2)  # True
print(d3 >= d2)  # True
Fluent Comparison Methods¶
from carbonic import Duration
d1 = Duration(hours=2, minutes=30)
d2 = Duration(hours=3)
# Standard comparison operators
print(d1 == d2)      # False
print(d1 < d2)       # True
print(d1 > d2)       # False
# Check if d1 is between two durations
print(Duration(hours=2) <= d1 <= Duration(hours=3))  # True
Zero and Sign Checks¶
from carbonic import Duration
zero = Duration()
positive = Duration(hours=1)
negative = Duration(hours=-1)
# Zero checks
print(zero == Duration())        # True
print(positive == Duration())    # False
# Sign checks
print(positive > Duration())  # True
print(negative < Duration())  # True
print(zero > Duration())      # False
print(zero < Duration())      # False
Formatting and Display¶
String Representation¶
from carbonic import Duration
# Various durations
d1 = Duration(days=3, hours=2, minutes=30)
d2 = Duration(hours=1, seconds=45)
d3 = Duration(minutes=90)
print(str(d1))   # "Duration(days=3, hours=2, minutes=30)"
print(str(d2))   # "Duration(hours=1, seconds=45)"
print(str(d3))   # "Duration(hours=1, minutes=30)"  # Normalized
print(repr(d1))  # Shows full constructor call
Human-Readable Format¶
from carbonic import Duration
duration = Duration(days=2, hours=3, minutes=30)
# Human readable string (basic implementation)
def humanize_duration(d):
    parts = []
    total_seconds = d.storage_seconds
    days = d.days
    remaining_hours = (total_seconds // 3600) % 24
    remaining_minutes = (total_seconds // 60) % 60
    remaining_seconds = total_seconds % 60
    if days:
        parts.append(f"{days} day{'s' if days != 1 else ''}")
    if remaining_hours:
        parts.append(f"{remaining_hours} hour{'s' if remaining_hours != 1 else ''}")
    if remaining_minutes:
        parts.append(f"{remaining_minutes} minute{'s' if remaining_minutes != 1 else ''}")
    if remaining_seconds:
        parts.append(f"{remaining_seconds} second{'s' if remaining_seconds != 1 else ''}")
    return ", ".join(parts) if parts else "0 seconds"
print(humanize_duration(duration))  # "2 days, 3 hours, 30 minutes"
ISO Duration Format¶
from carbonic import Duration
duration = Duration(days=3, hours=2, minutes=30, seconds=45)
# ISO 8601 duration format (PT2H30M for 2 hours 30 minutes)
def to_iso_duration(d):
    parts = ["P"]
    total_seconds = d.storage_seconds
    days = d.days
    remaining_hours = (total_seconds // 3600) % 24
    remaining_minutes = (total_seconds // 60) % 60
    remaining_seconds = total_seconds % 60
    if days:
        parts.append(f"{days}D")
    time_parts = []
    if remaining_hours:
        time_parts.append(f"{remaining_hours}H")
    if remaining_minutes:
        time_parts.append(f"{remaining_minutes}M")
    if remaining_seconds or d.microseconds:
        if d.microseconds:
            total_secs = remaining_seconds + d.microseconds / 1_000_000
            time_parts.append(f"{total_secs}S")
        else:
            time_parts.append(f"{remaining_seconds}S")
    if time_parts:
        parts.append("T")
        parts.extend(time_parts)
    return "".join(parts) if len(parts) > 1 else "PT0S"
print(to_iso_duration(duration))  # "P3DT2H30M45S"
Common Use Cases¶
Timeouts and Intervals¶
from carbonic import Duration, DateTime
# Define timeouts
short_timeout = Duration(seconds=30)
long_timeout = Duration(minutes=5)
session_timeout = Duration(hours=2)
# Check if timeout exceeded
start_time = DateTime.now()
# ... some operation ...
current_time = DateTime.now()
elapsed = current_time - start_time
if elapsed > short_timeout:
    print("Operation timed out!")
Scheduling and Delays¶
from carbonic import Duration, DateTime
# Schedule regular intervals
now = DateTime.now()
intervals = [
    Duration(minutes=15),   # Every 15 minutes
    Duration(hours=1),      # Every hour
    Duration(days=1),       # Daily
    Duration(days=7),       # Weekly
]
# Calculate next occurrences
next_times = [now + interval for interval in intervals]
for next_time in next_times:
    print(f"Next: {next_time}")
Duration Calculations¶
from carbonic import Duration
# Work day duration
work_day = Duration(hours=8)
lunch_break = Duration(minutes=30)
actual_work = work_day - lunch_break
print(f"Actual work time: {actual_work}")  # 7 hours 30 minutes
# Calculate pay periods
hourly_wage = 25  # dollars
weekly_hours = work_day * 5  # 5 work days
monthly_hours = weekly_hours * 4  # 4 weeks
weekly_pay = hourly_wage * weekly_hours.in_hours()
monthly_pay = hourly_wage * monthly_hours.in_hours()
print(f"Weekly pay: ${weekly_pay}")
print(f"Monthly pay: ${monthly_pay}")
Measuring Performance¶
from carbonic import Duration, DateTime
import time
# Measure operation duration
start = DateTime.now()
# Some operation
time.sleep(0.1)  # Simulate work
end = DateTime.now()
duration = end - start
print(f"Operation took: {duration.total_seconds():.3f} seconds")
# Performance budget
max_duration = Duration(milliseconds=100)
if duration > max_duration:
    print("Performance budget exceeded!")
Advanced Features¶
Duration Rounding¶
from carbonic import Duration
# Create duration with fractional seconds
duration = Duration(seconds=45, microseconds=750000)  # 45.75 seconds
# Round to nearest second
rounded_seconds = Duration(seconds=round(duration.total_seconds()))
print(rounded_seconds)  # Duration(seconds=46)
# Round to nearest minute
rounded_minutes = Duration(minutes=round(duration.in_minutes()))
print(rounded_minutes)  # Duration(minutes=1)
Duration Normalization¶
from carbonic import Duration
# Durations are automatically normalized
duration = Duration(hours=25, minutes=90, seconds=120)
print(duration)  # Duration(days=1, hours=2, minutes=32)
# Manual normalization example
def normalize_duration(days=0, hours=0, minutes=0, seconds=0, microseconds=0):
    total_microseconds = (
        microseconds +
        seconds * 1_000_000 +
        minutes * 60 * 1_000_000 +
        hours * 60 * 60 * 1_000_000 +
        days * 24 * 60 * 60 * 1_000_000
    )
    return Duration(microseconds=total_microseconds)
Working with Different Precisions¶
from carbonic import Duration
# High precision duration
precise = Duration(microseconds=123456)
print(f"Microseconds: {precise.microseconds}")
# Convert to different precisions
milliseconds = precise.total_seconds() * 1000
print(f"Milliseconds: {milliseconds}")
# Round to millisecond precision
ms_duration = Duration(seconds=round(milliseconds / 1000, 3))
print(ms_duration)
Best Practices¶
Choose Appropriate Units¶
from carbonic import Duration
# Good - use natural units
meeting_duration = Duration(hours=1, minutes=30)
coffee_break = Duration(minutes=15)
project_deadline = Duration(days=30)
# Avoid - unnecessarily complex
meeting_duration_bad = Duration(seconds=5400)  # Hard to read
Use Constants for Common Durations¶
from carbonic import Duration
# Define common durations as constants
MINUTE = Duration(minutes=1)
HOUR = Duration(hours=1)
DAY = Duration(days=1)
WEEK = Duration(days=7)
# Use in calculations
meeting_time = HOUR + Duration(minutes=30)
project_duration = WEEK * 4
Handle Edge Cases¶
from carbonic import Duration
# Be careful with zero durations
zero = Duration()
print(zero == Duration())  # True
# Handle negative durations appropriately
negative = Duration(hours=-2)
print(negative < Duration())  # True
# Consider absolute values when needed
absolute = abs(negative)