Pythonのtimedeltaで日付・時間を簡単に加算・減算する方法

python

プログラムを作っていると、「3日前の日付が知りたい」「2時間後の時刻を計算したい」「どのくらい時間が経ったか調べたい」といった場面がよく出てきます。

そんなときに便利なのが、Pythonのtimedelta(タイムデルタ)という機能です。これは時間の「差」や「長さ」を表すツールで、日付や時刻の計算を簡単にしてくれます。

この記事では、timedeltaを使って日付や時間を自由に計算する方法を、初心者の方にもわかりやすく説明します。

この記事で学べること

  • timedeltaの基本的な使い方
  • 日・時間・分・秒単位での計算方法
  • 時間の差を比較する方法
  • 実際のプロジェクトで使える応用テクニック
  • よくある注意点とその対処法
スポンサーリンク

timedeltaの基本的な使い方を覚えよう

timedeltaってなに?

timedeltaは、「時間の長さ」や「時間の差」を表すPythonの機能です。例えば:

  • 1日という時間の長さ
  • 3時間という時間の長さ
  • 2つの日時の差

これらをプログラムで扱えるようにしたものがtimedeltaです。

最初の例:明日の日付を求める

まずは簡単な例から始めてみましょう:

from datetime import datetime, timedelta

# 現在の日時を取得
now = datetime.now()
print(f"今:{now}")

# 1日という時間の長さを作る
one_day = timedelta(days=1)
print(f"1日の長さ:{one_day}")

# 現在の時刻に1日を足す
tomorrow = now + one_day
print(f"明日:{tomorrow}")

実行結果の例

今:2024-06-01 14:25:30.123456
1日の長さ:1 day, 0:00:00
明日:2024-06-02 14:25:30.123456

コードの説明

  • timedelta(days=1):1日という時間の長さを作ります
  • now + one_day:現在の時刻に1日を足し算します
  • 結果として、明日の同じ時刻が計算されます

過去の日付を求める(引き算)

足し算だけでなく、引き算もできます:

from datetime import datetime, timedelta

now = datetime.now()

# 1週間前の日付
last_week = now - timedelta(weeks=1)
print(f"1週間前:{last_week}")

# 3日前の日付
three_days_ago = now - timedelta(days=3)
print(f"3日前:{three_days_ago}")

# 昨日の日付
yesterday = now - timedelta(days=1)
print(f"昨日:{yesterday}")

実行結果の例

1週間前:2024-05-25 14:25:30.123456
3日前:2024-05-29 14:25:30.123456
昨日:2024-05-31 14:25:30.123456

timedeltaで指定できる時間の単位

timedeltaでは、様々な時間の単位を指定できます:

from datetime import timedelta

# 日数
days_delta = timedelta(days=7)
print(f"7日:{days_delta}")

# 週数
weeks_delta = timedelta(weeks=2)
print(f"2週間:{weeks_delta}")

# 時間
hours_delta = timedelta(hours=5)
print(f"5時間:{hours_delta}")

# 分
minutes_delta = timedelta(minutes=30)
print(f"30分:{minutes_delta}")

# 秒
seconds_delta = timedelta(seconds=45)
print(f"45秒:{seconds_delta}")

# 複数の単位を組み合わせ
complex_delta = timedelta(days=1, hours=2, minutes=30)
print(f"1日2時間30分:{complex_delta}")

実行結果の例

7日:7 days, 0:00:00
2週間:14 days, 0:00:00
5時間:5:00:00
30分:0:30:00
45秒:0:00:45
1日2時間30分:1 day, 2:30:00

実用的な例:期限の計算

実際のプロジェクトでよく使われる例をご紹介します:

from datetime import datetime, timedelta

def calculate_deadline(start_date, work_days):
    """作業開始日から締切日を計算する"""
    deadline = start_date + timedelta(days=work_days)
    return deadline

def time_until_deadline(deadline):
    """締切まであと何日かを計算する"""
    now = datetime.now()
    time_left = deadline - now
    return time_left

# 使用例
project_start = datetime(2024, 6, 1, 9, 0, 0)  # 6月1日 9時開始
deadline = calculate_deadline(project_start, 14)  # 14日間のプロジェクト

print(f"プロジェクト開始:{project_start.strftime('%Y年%m月%d日 %H時%M分')}")
print(f"締切:{deadline.strftime('%Y年%m月%d日 %H時%M分')}")

remaining_time = time_until_deadline(deadline)
print(f"締切まで:{remaining_time}")

timedeltaを使えば、日付の足し算・引き算が直感的にできるようになります。次は、より細かい時間単位での操作を学びましょう。

時間・分・秒単位での細かい操作をマスターしよう

時間単位での計算

時間単位での計算も、timedeltaを使えば簡単です:

from datetime import datetime, timedelta

now = datetime.now()
print(f"現在時刻:{now.strftime('%Y年%m月%d日 %H時%M分%S秒')}")

# 3時間後
three_hours_later = now + timedelta(hours=3)
print(f"3時間後:{three_hours_later.strftime('%Y年%m月%d日 %H時%M分%S秒')}")

# 5時間前
five_hours_ago = now - timedelta(hours=5)
print(f"5時間前:{five_hours_ago.strftime('%Y年%m月%d日 %H時%M分%S秒')}")

# 12時間後(翌日になる場合もある)
twelve_hours_later = now + timedelta(hours=12)
print(f"12時間後:{twelve_hours_later.strftime('%Y年%m月%d日 %H時%M分%S秒')}")

実行結果の例

現在時刻:2024年06月01日 14時25分30秒
3時間後:2024年06月01日 17時25分30秒
5時間前:2024年06月01日 09時25分30秒
12時間後:2024年06月02日 02時25分30秒

分・秒単位での精密な計算

もっと細かい時間単位での計算も可能です:

from datetime import datetime, timedelta

now = datetime.now()

# 30分後
thirty_minutes_later = now + timedelta(minutes=30)
print(f"30分後:{thirty_minutes_later.strftime('%H時%M分')}")

# 45分前
forty_five_minutes_ago = now - timedelta(minutes=45)
print(f"45分前:{forty_five_minutes_ago.strftime('%H時%M分')}")

# 90秒後
ninety_seconds_later = now + timedelta(seconds=90)
print(f"90秒後:{ninety_seconds_later.strftime('%H時%M分%S秒')}")

# 複合的な時間計算
complex_time = now + timedelta(hours=1, minutes=15, seconds=30)
print(f"1時間15分30秒後:{complex_time.strftime('%H時%M分%S秒')}")

時間差を秒で取得する

2つの時刻の差を秒数で知りたい場合は、total_seconds()メソッドを使います:

from datetime import datetime, timedelta

# 開始時刻と終了時刻を設定
start_time = datetime(2024, 6, 1, 12, 0, 0)  # 12:00:00
end_time = datetime(2024, 6, 1, 14, 30, 45)   # 14:30:45

# 時間差を計算
time_diff = end_time - start_time
print(f"時間差:{time_diff}")

# 秒数で取得
total_seconds = time_diff.total_seconds()
print(f"総秒数:{total_seconds}秒")

# 分数で表示
total_minutes = total_seconds / 60
print(f"総分数:{total_minutes}分")

# 時間数で表示
total_hours = total_seconds / 3600
print(f"総時間数:{total_hours}時間")

実行結果の例

時間差:2:30:45
総秒数:9045.0秒
総分数:150.75分
総時間数:2.5125時間

実用例:処理時間の測定

プログラムの実行時間を測定する例:

from datetime import datetime, timedelta
import time

def measure_processing_time():
    """処理時間を測定する例"""
    print("処理を開始します...")
    start_time = datetime.now()
    
    # 何らかの処理をシミュレーション(3秒待機)
    time.sleep(3)
    
    end_time = datetime.now()
    processing_time = end_time - start_time
    
    print(f"処理が完了しました")
    print(f"開始時刻:{start_time.strftime('%H時%M分%S秒')}")
    print(f"終了時刻:{end_time.strftime('%H時%M分%S秒')}")
    print(f"処理時間:{processing_time}")
    print(f"処理時間(秒):{processing_time.total_seconds()}秒")

# 関数を実行
measure_processing_time()

営業時間の計算

ビジネスシステムでよく使われる営業時間の計算例:

from datetime import datetime, timedelta

class BusinessHours:
    """営業時間を管理するクラス"""
    
    def __init__(self, open_hour=9, close_hour=18):
        self.open_hour = open_hour    # 営業開始時刻
        self.close_hour = close_hour  # 営業終了時刻
    
    def get_business_hours_today(self):
        """今日の営業時間を取得"""
        today = datetime.now().date()
        
        # 営業開始時刻
        open_time = datetime.combine(today, datetime.min.time()) + timedelta(hours=self.open_hour)
        
        # 営業終了時刻
        close_time = datetime.combine(today, datetime.min.time()) + timedelta(hours=self.close_hour)
        
        return open_time, close_time
    
    def is_business_hours(self, check_time=None):
        """指定時刻が営業時間内かチェック"""
        if check_time is None:
            check_time = datetime.now()
        
        open_time, close_time = self.get_business_hours_today()
        
        return open_time <= check_time <= close_time
    
    def time_until_open(self):
        """営業開始まであと何時間か"""
        now = datetime.now()
        open_time, _ = self.get_business_hours_today()
        
        if now < open_time:
            return open_time - now
        else:
            # 明日の営業開始時刻
            tomorrow_open = open_time + timedelta(days=1)
            return tomorrow_open - now
    
    def time_until_close(self):
        """営業終了まであと何時間か"""
        now = datetime.now()
        _, close_time = self.get_business_hours_today()
        
        if now < close_time:
            return close_time - now
        else:
            return timedelta(0)  # すでに営業終了

# 使用例
business = BusinessHours(9, 18)  # 9時〜18時営業

print("営業時間チェック:")
print(f"現在営業中?:{business.is_business_hours()}")

time_to_open = business.time_until_open()
time_to_close = business.time_until_close()

print(f"営業開始まで:{time_to_open}")
print(f"営業終了まで:{time_to_close}")

時間・分・秒単位での計算も、timedeltaがあれば簡単に実現できます。精密な時間管理が必要なシステムでも活用できます。

timedeltaの比較・条件分岐を活用しよう

時間差の比較

timedeltaは、大小比較ができるので条件分岐で使えます:

from datetime import datetime, timedelta

# 2つの時刻を設定
start_time = datetime(2024, 6, 1, 10, 0, 0)
end_time = datetime(2024, 6, 1, 12, 30, 0)

# 時間差を計算
time_diff = end_time - start_time
print(f"時間差:{time_diff}")

# 1時間より長いかチェック
if time_diff > timedelta(hours=1):
    print("1時間以上の差があります")
else:
    print("1時間未満の差です")

# 2時間より短いかチェック
if time_diff < timedelta(hours=2):
    print("2時間未満の差です")

# 3時間と同じかチェック
if time_diff == timedelta(hours=2, minutes=30):
    print("ちょうど2時間30分の差です")

実行結果の例

時間差:2:30:00
1時間以上の差があります
2時間未満の差です
ちょうど2時間30分の差です

経過時間をチェックする実用例

プログラムの実行状況を監視する例:

from datetime import datetime, timedelta
import time

def monitor_process():
    """プロセスの実行時間を監視する"""
    start_time = datetime.now()
    print(f"処理開始:{start_time.strftime('%H時%M分%S秒')}")
    
    # 長時間かかる処理をシミュレーション
    for i in range(10):
        time.sleep(0.5)  # 0.5秒待機
        
        # 経過時間をチェック
        elapsed = datetime.now() - start_time
        
        print(f"ステップ {i+1}: 経過時間 {elapsed}")
        
        # 5秒経過したら警告
        if elapsed > timedelta(seconds=5):
            print("⚠️ 処理に時間がかかりすぎています")
            break
    
    total_time = datetime.now() - start_time
    print(f"処理完了:総実行時間 {total_time}")

# 実行
monitor_process()

ファイルの更新チェック

ファイルが最近更新されたかをチェックする例:

from datetime import datetime, timedelta
import os

def check_file_freshness(filepath, max_age_hours=24):
    """ファイルが指定時間以内に更新されているかチェック"""
    try:
        # ファイルの最終更新時刻を取得
        file_time = datetime.fromtimestamp(os.path.getmtime(filepath))
        current_time = datetime.now()
        
        # 経過時間を計算
        age = current_time - file_time
        max_age = timedelta(hours=max_age_hours)
        
        print(f"ファイル:{filepath}")
        print(f"最終更新:{file_time.strftime('%Y年%m月%d日 %H時%M分')}")
        print(f"経過時間:{age}")
        
        if age < max_age:
            print(f"✅ ファイルは新しいです({max_age_hours}時間以内)")
            return True
        else:
            print(f"⚠️ ファイルが古くなっています({max_age_hours}時間以上経過)")
            return False
            
    except FileNotFoundError:
        print(f"❌ ファイルが見つかりません:{filepath}")
        return False

# 使用例(実際のファイルパスに変更してください)
is_fresh = check_file_freshness("example.txt", 6)  # 6時間以内かチェック

タスクスケジューラの例

定期的にタスクを実行するスケジューラの例:

from datetime import datetime, timedelta

class TaskScheduler:
    """簡単なタスクスケジューラ"""
    
    def __init__(self):
        self.last_daily_task = None
        self.last_hourly_task = None
        self.last_weekly_task = None
    
    def should_run_daily_task(self):
        """日次タスクを実行すべきかチェック"""
        now = datetime.now()
        
        if self.last_daily_task is None:
            return True
        
        elapsed = now - self.last_daily_task
        return elapsed >= timedelta(days=1)
    
    def should_run_hourly_task(self):
        """時間次タスクを実行すべきかチェック"""
        now = datetime.now()
        
        if self.last_hourly_task is None:
            return True
        
        elapsed = now - self.last_hourly_task
        return elapsed >= timedelta(hours=1)
    
    def should_run_weekly_task(self):
        """週次タスクを実行すべきかチェック"""
        now = datetime.now()
        
        if self.last_weekly_task is None:
            return True
        
        elapsed = now - self.last_weekly_task
        return elapsed >= timedelta(weeks=1)
    
    def run_daily_task(self):
        """日次タスクを実行"""
        print("📅 日次タスクを実行中...")
        self.last_daily_task = datetime.now()
        print(f"次回実行予定:{(self.last_daily_task + timedelta(days=1)).strftime('%m月%d日 %H時%M分')}")
    
    def run_hourly_task(self):
        """時間次タスクを実行"""
        print("⏰ 時間次タスクを実行中...")
        self.last_hourly_task = datetime.now()
        print(f"次回実行予定:{(self.last_hourly_task + timedelta(hours=1)).strftime('%H時%M分')}")
    
    def run_weekly_task(self):
        """週次タスクを実行"""
        print("📆 週次タスクを実行中...")
        self.last_weekly_task = datetime.now()
        print(f"次回実行予定:{(self.last_weekly_task + timedelta(weeks=1)).strftime('%m月%d日')}")
    
    def check_and_run_tasks(self):
        """すべてのタスクをチェックして必要に応じて実行"""
        print(f"タスクチェック開始:{datetime.now().strftime('%Y年%m月%d日 %H時%M分')}")
        
        if self.should_run_weekly_task():
            self.run_weekly_task()
        
        if self.should_run_daily_task():
            self.run_daily_task()
        
        if self.should_run_hourly_task():
            self.run_hourly_task()
        
        print("タスクチェック完了\n")

# 使用例
scheduler = TaskScheduler()
scheduler.check_and_run_tasks()

# 1時間1分後のシミュレーション
scheduler.last_hourly_task = datetime.now() - timedelta(hours=1, minutes=1)
scheduler.check_and_run_tasks()

セッション管理の例

ユーザーセッションの有効期限をチェックする例:

from datetime import datetime, timedelta

class UserSession:
    """ユーザーセッション管理"""
    
    def __init__(self, user_id, session_duration_hours=2):
        self.user_id = user_id
        self.login_time = datetime.now()
        self.last_activity = datetime.now()
        self.session_duration = timedelta(hours=session_duration_hours)
    
    def update_activity(self):
        """最終活動時刻を更新"""
        self.last_activity = datetime.now()
        print(f"ユーザー {self.user_id} の活動を記録しました")
    
    def is_session_valid(self):
        """セッションが有効かチェック"""
        now = datetime.now()
        elapsed = now - self.last_activity
        
        return elapsed < self.session_duration
    
    def time_until_expiry(self):
        """セッション期限切れまでの時間"""
        now = datetime.now()
        expiry_time = self.last_activity + self.session_duration
        
        if now < expiry_time:
            return expiry_time - now
        else:
            return timedelta(0)
    
    def get_session_info(self):
        """セッション情報を表示"""
        print(f"ユーザーID:{self.user_id}")
        print(f"ログイン時刻:{self.login_time.strftime('%H時%M分')}")
        print(f"最終活動:{self.last_activity.strftime('%H時%M分')}")
        print(f"セッション有効:{self.is_session_valid()}")
        
        if self.is_session_valid():
            remaining = self.time_until_expiry()
            hours = remaining.total_seconds() // 3600
            minutes = (remaining.total_seconds() % 3600) // 60
            print(f"残り時間:{int(hours)}時間{int(minutes)}分")
        else:
            print("セッション期限切れ")

# 使用例
user_session = UserSession("user123", 1)  # 1時間のセッション
user_session.get_session_info()

print("\n--- 30分後の活動 ---")
user_session.last_activity = datetime.now() - timedelta(minutes=30)
user_session.update_activity()
user_session.get_session_info()

timedeltaの比較機能を使うことで、時間ベースの条件判定を簡単に実装できます。これにより、より柔軟で実用的なプログラムが作れるようになります。

注意点と応用テクニックを理解しよう

timedeltaの重要な注意点

年や月の単位はサポートされていない

timedeltaで一番注意すべき点は、年や月の単位が使えないことです:

from datetime import timedelta

# ❌ これらはエラーになります
# wrong1 = timedelta(years=1)     # TypeError
# wrong2 = timedelta(months=3)    # TypeError

# ✅ 使える単位
correct = timedelta(
    weeks=2,      # 週
    days=5,       # 日
    hours=3,      # 時間
    minutes=30,   # 分
    seconds=45,   # 秒
    microseconds=123456  # マイクロ秒
)

print(f"正しいtimedelta:{correct}")

なぜ年や月が使えないの?

年や月は日数が一定ではないからです:

  • 月:28日〜31日(月によって異なる)
  • 年:365日または366日(うるう年によって異なる)

年月の計算が必要な場合の対処法

年や月の計算が必要な場合は、dateutilライブラリを使うか、手動で計算します:

from datetime import datetime, timedelta

def add_months(start_date, months):
    """月を加算する関数(手動実装)"""
    year = start_date.year
    month = start_date.month + months
    
    # 年の調整
    while month > 12:
        year += 1
        month -= 12
    
    while month < 1:
        year -= 1
        month += 12
    
    # 日付の調整(月末の場合)
    try:
        return start_date.replace(year=year, month=month)
    except ValueError:
        # 例:1月31日から1ヶ月後は2月28日(または29日)
        from calendar import monthrange
        last_day = monthrange(year, month)[1]
        return start_date.replace(year=year, month=month, day=last_day)

# 使用例
start = datetime(2024, 1, 31)
print(f"開始日:{start.strftime('%Y年%m月%d日')}")

one_month_later = add_months(start, 1)
print(f"1ヶ月後:{one_month_later.strftime('%Y年%m月%d日')}")

six_months_later = add_months(start, 6)
print(f"6ヶ月後:{six_months_later.strftime('%Y年%m月%d日')}")

負の値も使える

timedeltaでは負の値も使えます:

from datetime import datetime, timedelta

now = datetime.now()

# 負の値を使った計算
past_time = now + timedelta(days=-7)  # 7日前
future_time = now + timedelta(hours=-3)  # 3時間前

print(f"現在:{now.strftime('%Y年%m月%d日 %H時%M分')}")
print(f"7日前:{past_time.strftime('%Y年%m月%d日 %H時%M分')}")
print(f"3時間前:{future_time.strftime('%Y年%m月%d日 %H時%M分')}")

# 引き算と同じ結果
same_result1 = now - timedelta(days=7)
same_result2 = now + timedelta(days=-7)

print(f"引き算:{same_result1}")
print(f"負の値:{same_result2}")
print(f"同じ結果?:{same_result1 == same_result2}")

繰り返し処理での活用

一定期間ごとの繰り返し処理は、timedeltaの得意分野です:

from datetime import datetime, timedelta

def generate_date_range(start_date, end_date, step_days=1):
    """指定期間の日付リストを生成する"""
    dates = []
    current_date = start_date
    
    while current_date <= end_date:
        dates.append(current_date)
        current_date += timedelta(days=step_days)
    
    return dates

def print_weekly_schedule(start_date, weeks=4):
    """週次スケジュールを表示する"""
    print(f"週次スケジュール({weeks}週間):")
    
    for week in range(weeks):
        week_start = start_date + timedelta(weeks=week)
        week_end = week_start + timedelta(days=6)
        
        print(f"第{week + 1}週:{week_start.strftime('%m月%d日')} 〜 {week_end.strftime('%m月%d日')}")

def create_backup_schedule():
    """バックアップスケジュールを作成する"""
    print("バックアップスケジュール:")
    base_time = datetime(2024, 6, 1, 2, 0, 0)  # 深夜2時
    
    # 毎日のバックアップ(1週間分)
    print("日次バックアップ:")
    for day in range(7):
        backup_time = base_time + timedelta(days=day)
        print(f"  {backup_time.strftime('%m月%d日 %H時%M分')}")
    
    print("\n週次バックアップ:")
    for week in range(4):
        weekly_backup = base_time + timedelta(weeks=week)
        print(f"  {weekly_backup.strftime('%m月%d日 %H時%M分')}")

# 使用例
start = datetime(2024, 6, 1)
end = datetime(2024, 6, 10)

print("期間内の全日付:")
date_list = generate_date_range(start, end)
for date in date_list:
    print(f"  {date.strftime('%Y年%m月%d日 (%a)')}")

print("\n" + "="*40)
print_weekly_schedule(start, 3)

print("\n" + "="*40)
create_backup_schedule()

実用的な応用例:ログローテーション

ログファイルを定期的に切り替える仕組み:

from datetime import datetime, timedelta
import os

class LogRotator:
    """ログファイルのローテーション管理"""
    
    def __init__(self, log_dir="logs", keep_days=30):
        self.log_dir = log_dir
        self.keep_days = keep_days
        
    def get_today_log_filename(self):
        """今日のログファイル名を取得"""
        today = datetime.now()
        filename = f"app_{today.strftime('%Y%m%d')}.log"
        return os.path.join(self.log_dir, filename)
    
    def get_hourly_log_filename(self):
        """時間別ログファイル名を取得"""
        now = datetime.now()
        filename = f"app_{now.strftime('%Y%m%d_%H')}.log"
        return os.path.join(self.log_dir, filename)
    
    def should_rotate_daily(self, last_rotation):
        """日次ローテーションが必要かチェック"""
        if last_rotation is None:
            return True
        
        now = datetime.now()
        elapsed = now - last_rotation
        return elapsed >= timedelta(days=1)
    
    def should_rotate_hourly(self, last_rotation):
        """時間次ローテーションが必要かチェック"""
        if last_rotation is None:
            return True
        
        now = datetime.now()
        elapsed = now - last_rotation
        return elapsed >= timedelta(hours=1)
    
    def cleanup_old_logs(self):
        """古いログファイルを削除"""
        if not os.path.exists(self.log_dir):
            return
        
        cutoff_date = datetime.now() - timedelta(days=self.keep_days)
        deleted_count = 0
        
        for filename in os.listdir(self.log_dir):
            if filename.startswith("app_") and filename.endswith(".log"):
                filepath = os.path.join(self.log_dir, filename)
                
                try:
                    file_time = datetime.fromtimestamp(os.path.getmtime(filepath))
                    if file_time < cutoff_date:
                        os.remove(filepath)
                        deleted_count += 1
                        print(f"削除:{filename}")
                except OSError:
                    continue
        
        print(f"合計 {deleted_count} 個の古いログファイルを削除しました")

# 使用例
rotator = LogRotator()
print(f"今日のログファイル:{rotator.get_today_log_filename()}")
print(f"現在の時間別ログファイル:{rotator.get_hourly_log_filename()}")

パフォーマンス測定ツール

処理時間を詳細に測定するツール:

from datetime import datetime, timedelta
import time

class PerformanceTimer:
    """パフォーマンス測定ツール"""
    
    def __init__(self):
        self.start_time = None
        self.end_time = None
        self.checkpoints = []
    
    def start(self):
        """測定開始"""
        self.start_time = datetime.now()
        self.checkpoints = []
        print(f"⏱️ 測定開始:{self.start_time.strftime('%H時%M分%S秒')}")
    
    def checkpoint(self, description=""):
        """チェックポイントを記録"""
        if self.start_time is None:
            print("❌ 測定が開始されていません")
            return
        
        checkpoint_time = datetime.now()
        elapsed = checkpoint_time - self.start_time
        
        self.checkpoints.append({
            'time': checkpoint_time,
            'elapsed': elapsed,
            'description': description
        })
        
        print(f"📍 {description}: {elapsed}")
    
    def stop(self):
        """測定終了"""
        if self.start_time is None:
            print("❌ 測定が開始されていません")
            return
        
        self.end_time = datetime.now()
        total_time = self.end_time - self.start_time
        
        print(f"🏁 測定終了:{self.end_time.strftime('%H時%M分%S秒')}")
        print(f"📊 総実行時間:{total_time}")
        
        return total_time
    
    def report(self):
        """詳細レポートを表示"""
        if not self.checkpoints:
            print("チェックポイントがありません")
            return
        
        print("\n" + "="*50)
        print("📈 パフォーマンスレポート")
        print("="*50)
        
        prev_time = self.start_time
        
        for i, checkpoint in enumerate(self.checkpoints):
            step_time = checkpoint['time'] - prev_time
            total_elapsed = checkpoint['elapsed']
            
            print(f"ステップ {i+1}: {checkpoint['description']}")
            print(f"  ステップ時間: {step_time}")
            print(f"  累積時間: {total_elapsed}")
            print()
            
            prev_time = checkpoint['time']
        
        if self.end_time:
            final_step = self.end_time - prev_time
            total_time = self.end_time - self.start_time
            print(f"最終処理時間: {final_step}")
            print(f"総実行時間: {total_time}")

# 使用例
def example_long_process():
    """時間のかかる処理のシミュレーション"""
    timer = PerformanceTimer()
    timer.start()
    
    # 初期化処理
    time.sleep(0.5)
    timer.checkpoint("初期化処理完了")
    
    # データ読み込み
    time.sleep(1.0)
    timer.checkpoint("データ読み込み完了")
    
    # データ処理
    time.sleep(1.5)
    timer.checkpoint("データ処理完了")
    
    # 結果保存
    time.sleep(0.8)
    timer.checkpoint("結果保存完了")
    
    total_time = timer.stop()
    timer.report()
    
    # 推奨実行時間との比較
    recommended_time = timedelta(seconds=3)
    if total_time > recommended_time:
        print(f"⚠️ 処理時間が推奨時間({recommended_time})を超えています")
    else:
        print(f"✅ 処理時間は推奨範囲内です")

# 実行
example_long_process()

イベントスケジューリングシステム

イベントの管理とスケジューリング:

from datetime import datetime, timedelta

class Event:
    """イベントクラス"""
    
    def __init__(self, name, start_time, duration_minutes=60):
        self.name = name
        self.start_time = start_time
        self.duration = timedelta(minutes=duration_minutes)
        self.end_time = start_time + self.duration
    
    def __str__(self):
        return f"{self.name} ({self.start_time.strftime('%m/%d %H:%M')} - {self.end_time.strftime('%H:%M')})"

class EventScheduler:
    """イベントスケジューラー"""
    
    def __init__(self):
        self.events = []
    
    def add_event(self, event):
        """イベントを追加"""
        self.events.append(event)
        self.events.sort(key=lambda e: e.start_time)
    
    def get_events_today(self):
        """今日のイベントを取得"""
        today = datetime.now().date()
        today_events = []
        
        for event in self.events:
            if event.start_time.date() == today:
                today_events.append(event)
        
        return today_events
    
    def get_upcoming_events(self, hours=24):
        """指定時間内の今後のイベントを取得"""
        now = datetime.now()
        cutoff_time = now + timedelta(hours=hours)
        
        upcoming = []
        for event in self.events:
            if now <= event.start_time <= cutoff_time:
                upcoming.append(event)
        
        return upcoming
    
    def check_conflicts(self, new_event):
        """スケジュール競合をチェック"""
        conflicts = []
        
        for event in self.events:
            # 時間の重複をチェック
            if (new_event.start_time < event.end_time and 
                new_event.end_time > event.start_time):
                conflicts.append(event)
        
        return conflicts
    
    def get_free_time_today(self):
        """今日の空き時間を計算"""
        today = datetime.now().date()
        today_events = self.get_events_today()
        
        if not today_events:
            return "終日空いています"
        
        # 営業時間を9:00-18:00と仮定
        business_start = datetime.combine(today, datetime.min.time()) + timedelta(hours=9)
        business_end = datetime.combine(today, datetime.min.time()) + timedelta(hours=18)
        
        free_periods = []
        current_time = business_start
        
        for event in today_events:
            if current_time < event.start_time:
                duration = event.start_time - current_time
                if duration >= timedelta(minutes=30):  # 30分以上の空きのみ
                    free_periods.append(f"{current_time.strftime('%H:%M')}-{event.start_time.strftime('%H:%M')}")
            current_time = max(current_time, event.end_time)
        
        # 最後のイベント後の空き時間
        if current_time < business_end:
            duration = business_end - current_time
            if duration >= timedelta(minutes=30):
                free_periods.append(f"{current_time.strftime('%H:%M')}-{business_end.strftime('%H:%M')}")
        
        return free_periods if free_periods else "空き時間がありません"
    
    def print_schedule(self, days=1):
        """スケジュールを表示"""
        start_date = datetime.now().date()
        
        for day in range(days):
            target_date = start_date + timedelta(days=day)
            day_events = [e for e in self.events if e.start_time.date() == target_date]
            
            print(f"\n📅 {target_date.strftime('%Y年%m月%d日')} のスケジュール:")
            
            if not day_events:
                print("  予定はありません")
                continue
            
            for event in day_events:
                duration_hours = event.duration.total_seconds() / 3600
                print(f"  🕐 {event} (時間: {duration_hours:.1f}時間)")

# 使用例
scheduler = EventScheduler()

# イベントを追加
base_date = datetime.now().replace(hour=10, minute=0, second=0, microsecond=0)

events = [
    Event("チームミーティング", base_date, 60),
    Event("プレゼンテーション", base_date + timedelta(hours=2), 90),
    Event("クライアント打ち合わせ", base_date + timedelta(hours=5), 120),
    Event("明日の企画会議", base_date + timedelta(days=1), 90)
]

for event in events:
    conflicts = scheduler.check_conflicts(event)
    if conflicts:
        print(f"⚠️ 競合が見つかりました:{event.name}")
        for conflict in conflicts:
            print(f"   競合イベント:{conflict}")
    else:
        scheduler.add_event(event)
        print(f"✅ イベントを追加:{event}")

print("\n" + "="*50)
scheduler.print_schedule(2)

print(f"\n今後24時間のイベント:")
upcoming = scheduler.get_upcoming_events(24)
for event in upcoming:
    time_until = event.start_time - datetime.now()
    hours = time_until.total_seconds() / 3600
    print(f"  {event} (あと {hours:.1f}時間)")

print(f"\n今日の空き時間:")
free_time = scheduler.get_free_time_today()
if isinstance(free_time, list):
    for period in free_time:
        print(f"  🕐 {period}")
else:
    print(f"  {free_time}")

timedeltaの算術演算

timedeltaは算術演算も可能です:

from datetime import timedelta

# timedeltaの足し算
duration1 = timedelta(hours=2, minutes=30)
duration2 = timedelta(hours=1, minutes=45)
total_duration = duration1 + duration2

print(f"時間1:{duration1}")
print(f"時間2:{duration2}")
print(f"合計:{total_duration}")

# timedeltaの引き算
difference = duration1 - duration2
print(f"差:{difference}")

# timedeltaの掛け算
doubled = duration1 * 2
print(f"2倍:{doubled}")

# timedeltaの割り算
half = duration1 / 2
print(f"半分:{half}")

# 比率の計算
ratio = duration1 / duration2
print(f"比率:{ratio:.2f}")

# 絶対値
negative_duration = timedelta(hours=-3)
absolute_duration = abs(negative_duration)
print(f"負の値:{negative_duration}")
print(f"絶対値:{absolute_duration}")

まとめ

Pythonのtimedeltaは、日付や時間の計算を直感的で簡単にしてくれる非常に便利なツールです。

timedeltaの主な特徴

  • 簡単な日時計算:足し算・引き算で直感的に操作
  • 柔軟な時間単位:日・時間・分・秒・マイクロ秒に対応
  • 比較演算対応:条件分岐で時間ベースの判定が可能
  • 算術演算対応:掛け算・割り算で時間の計算が可能

実用的な活用場面

  • ログ分析:一定期間のデータフィルタリング
  • スケジュール管理:イベントの時間計算と競合チェック
  • パフォーマンス測定:処理時間の測定と分析
  • 定期処理:バックアップやメンテナンスのスケジューリング
  • セッション管理:ログイン状態の有効期限チェック

注意すべきポイント

  • 年や月の単位は直接サポートされていない
  • 必要に応じて手動実装や外部ライブラリを使用
  • タイムゾーンは別途考慮が必要

コメント

タイトルとURLをコピーしました