【Python実践プロジェクト】datetimeと正規表現(re)で作る!未来時刻計算ツール開発チュートリアル 🛠️

「3日と5時間40分後って、具体的に何月何日の何時何分だろう?」

「Pythonの勉強を一通りしたけど、実際に何か便利なものを作ってみたい!」
「これまで学んだ標準ライブラリの知識、どうやって組み合わせればいいんだろう?」

こんにちは! Pythonistaの皆さん、これまでの標準ライブラリ探検、お疲れ様でした! これまでに、日付を扱うdatetimeや、複雑な文字列を解析するreなど、たくさんの強力な武器を手に入れてきましたね。

今回は、これまでのシリーズとは少し趣向を変えた「実践プロジェクト編」です! これまで学んだ知識を総動員して、日常のちょっとした疑問を解決してくれる便利なツール、「未来時刻計算ツール」をゼロから作り上げていきましょう。このツールは、「3d23h50m」(3日23時間50分後)や「12:40」(12時間40分後)といった様々な形式の時間を入力すると、現在の時刻からその時間だけ未来の正確な日時を計算してくれます。 この記事を読みながら一緒にコーディングを進めれば、あなたも実用的なPythonツールを完成させることができます。さあ、学んだ知識を形にする楽しさを味わいましょう!


1. 今回作るツール:どんな問題を解決する?(設計図を描こう)

プログラムを作り始める前に、まず「何を作るのか」「どんな機能が必要か」を明確にするのが成功の秘訣です。今回作成する「未来時刻計算ツール」の主な機能は以下の通りです。

  • ユーザーからの柔軟な入力受付: ユーザーが様々な形式で経過時間を入力できるようにします。
    • 例1: 3d23h50m (日・時間・分の組み合わせ)
    • 例2: 12:40 (コロン区切りの時間・分)
    • 例3: 5h30m といった単一単位
    • 例4: 50m 1d のように、単位の順序がバラバラでも、スペースが含まれていてもOK
  • 現在時刻の取得と未来時刻の計算: プログラム実行時の現在時刻を基準に、入力された時間だけ未来の時刻を正確に計算します。
  • 結果の分かりやすい表示: 計算前の現在時刻、入力された経過時間、そして計算後の未来時刻を、人間が読みやすい形式で表示します。

このツールを実現するために、以下の標準ライブラリが中心的な役割を果たします。

  • datetimeモジュール: 現在時刻の取得 (datetime.now()) や、時間の差分・期間を扱う (timedelta)、そして結果の表示形式を整える (strftime()) ために使います。
  • reモジュール (正規表現): 「3d23h50m」「12:40」といった多様なユーザー入力を柔軟に解析するために使います。まさに正規表現の腕の見せ所です!

2. ステップ1:プロジェクトの骨格と基本の時刻計算

まずは、プログラム全体の骨格と、中核となる時刻計算の基本部分から作っていきましょう。

from datetime import datetime, timedelta

def main():
    """
    メイン処理。ユーザーとの対話、計算、結果表示を行う。
    """
    print("🚀 未来時刻計算スクリプト 🚀")
    print("経過時間を入力してください。")
    print("例: 3d23h50m (3日23時間50分), 12:40 (12時間40分), 1d12h (1日と12時間)")

    # --- まずは固定の時間で計算の基本を試してみる ---
    now = datetime.now()
    fixed_delta = timedelta(days=3, hours=23, minutes=50) # 3d23h50m をtimedeltaで表現
    future_time = now + fixed_delta

    print("\n--- 計算テスト ---")
    print(f"現在時刻  : {now.strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"経過時間  : {str(fixed_delta)}")
    print(f"未来の時刻: {future_time.strftime('%Y-%m-%d %H:%M:%S')}")
    print("------------------")
    # --- ここまでが基本テスト ---


# このスクリプトが直接実行された場合にのみmain()関数を呼び出す
if __name__ == "__main__":
    main()

このコードを実行すると、現時点での時刻と、そこから3日23時間50分後の時刻が表示されるはずです。datetimeオブジェクトとtimedeltaオブジェクトの足し算で未来の時刻が簡単に計算できることが分かりますね。私たちの最終目標は、このfixed_deltaの部分を、ユーザーの入力から動的に生成することです。


3. ステップ2:ユーザー入力を解析する心臓部!parse_time_input()関数

ここが今回のプロジェクトの最も面白く、挑戦的な部分です。「50m 1d」のような順序がバラバラな入力や、「12:40」のような異なる形式をどうやって一つの時間に変換するか。ここで正規表現(reモジュール)が活躍します。

以下のような、ユーザーの多様な時間入力を解析してtimedeltaオブジェクトを返す関数を作成します。

import re
from datetime import timedelta

def parse_time_input(input_str: str) -> timedelta | None:
    """
    ユーザーの多様な時間入力を解析してtimedeltaオブジェクトを返す。
    順序不同の入力 (例: 50m3d23h) やスペースを含む入力にも対応。

    Args:
        input_str (str): ユーザーによって入力された時間文字列。

    Returns:
        timedelta | None: 解析に成功した場合はtimedeltaオブジェクト、
                          失敗した場合はNoneを返す。
    """
    # 入力からスペースを削除し、全て小文字に変換して処理を簡単にする
    clean_str = input_str.strip().lower().replace(" ", "")

    days = 0
    hours = 0
    minutes = 0

    # --- d, h, m を含む形式の解析 ---
    # re.findall() を使って、各単位の数値を文字列中から全て見つけ出す
    days_list = re.findall(r'(\d+)d', clean_str)    # 例: "3d" -> ['3']
    hours_list = re.findall(r'(\d+)h', clean_str)   # 例: "23h" -> ['23']
    minutes_list = re.findall(r'(\d+)m', clean_str) # 例: "50m" -> ['50']

    # --- コロン(:) を含む形式の解析 ---
    # 文字列の先頭(^)から末尾($)までが「数字:数字」のパターンに完全に一致するかチェック
    colon_match = re.match(r'^(\d+):(\d+)$', clean_str)

    # --- 解析結果を時間に変換 ---
    is_dhm_format = days_list or hours_list or minutes_list

    if is_dhm_format:
        # 同じ単位が複数回指定されても対応できるよう、見つけた数値を全て合計する
        days = sum(map(int, days_list))
        hours = sum(map(int, hours_list))
        minutes = sum(map(int, minutes_list))
        
        # d, h, m と数字以外の不正な文字がないかチェック
        check_str = re.sub(r'\d+d|\d+h|\d+m', '', clean_str)
        if check_str:
            return None # 無効な文字が残っていれば解析失敗
            
        return timedelta(days=days, hours=hours, minutes=minutes)

    elif colon_match:
        # コロン形式の場合は、マッチしたグループから時間と分を取得
        hours = int(colon_match.group(1))
        minutes = int(colon_match.group(2))
        return timedelta(hours=hours, minutes=minutes)

    else:
        # どの形式にも一致しない場合は失敗
        return None

この関数のポイント:

  • 入力の前処理: .strip().lower().replace(" ", "") で、大文字・小文字、前後の空白、途中のスペースを吸収し、後続の正規表現処理をシンプルにしています。
  • re.findall(r'(\d+)d', ...): 文字列全体から「数字+d」というパターンを全て探し出し、数字の部分だけをリストとして返します。(\d+)の括弧が「キャプチャグループ」となり、この部分だけが結果として得られます。h, mも同様です。
  • re.match(r'^(\d+):(\d+)$', ...): 文字列の先頭からパターンが一致するかを試します。^は文字列の先頭、$は末尾を意味するため、「12:30abc」のような余計な文字が含まれている入力を不正として弾くことができます。
  • sum(map(int, ...)): findallが返すのは文字列のリスト(例: ['1', '2'])なので、map(int, ...)で各要素を整数に変換し、sum()で合計しています。これにより「1d 2d」のような入力も正しく「3日」として扱えます。

4. ステップ3:全てを統合!未来時刻計算ツールの完成形

それでは、ステップ1で作った骨格と、ステップ2で作った入力解析関数を組み合わせて、ツールを完成させましょう!

import re
from datetime import datetime, timedelta

# (ここに、上記で作成した parse_time_input() 関数を貼り付ける)
def parse_time_input(input_str: str) -> timedelta | None:
    # ... (内容はステップ2と同じ) ...
    clean_str = input_str.strip().lower().replace(" ", "")
    days_list = re.findall(r'(\d+)d', clean_str)
    hours_list = re.findall(r'(\d+)h', clean_str)
    minutes_list = re.findall(r'(\d+)m', clean_str)
    colon_match = re.match(r'^(\d+):(\d+)$', clean_str)
    is_dhm_format = days_list or hours_list or minutes_list
    if is_dhm_format:
        days = sum(map(int, days_list))
        hours = sum(map(int, hours_list))
        minutes = sum(map(int, minutes_list))
        check_str = re.sub(r'\d+d|\d+h|\d+m', '', clean_str)
        if check_str: return None
        return timedelta(days=days, hours=hours, minutes=minutes)
    elif colon_match:
        hours = int(colon_match.group(1))
        minutes = int(colon_match.group(2))
        return timedelta(hours=hours, minutes=minutes)
    else:
        return None


def main():
    """
    メイン処理。ユーザーとの対話、計算、結果表示を行う。
    """
    print("🚀 未来時刻計算スクリプト 🚀")
    print("経過時間を入力してください。")
    print("例: 3d23h50m (3日23時間50分), 12:40 (12時間40分), 1d12h (1日と12時間)")

    while True:
        try:
            user_input = input("\n経過時間を入力してください (終了するには 'q' を入力): ")
            
            if user_input.lower() == 'q':
                print("スクリプトを終了します。")
                break

            if not user_input:
                print("⚠️ 入力がありません。もう一度入力してください。")
                continue

            # 入力文字列を解析してtimedeltaを取得
            time_delta = parse_time_input(user_input)

            if time_delta:
                now = datetime.now()
                future_time = now + time_delta

                print("-" * 40)
                print(f"現在時刻  : {now.strftime('%Y-%m-%d %H:%M:%S')}")
                print(f"経過時間  : {str(time_delta)}")
                print(f"未来の時刻: {future_time.strftime('%Y-%m-%d %H:%M:%S')}")
                print("-" * 40)

            else:
                print("❌ 無効な形式です。もう一度入力してください。")
                print("例: 3d23h50m, 12:40, 1d, 5h, 30m")

        except KeyboardInterrupt:
            print("\nスクリプトを中断しました。")
            break
        except Exception as e:
            print(f"エラーが発生しました: {e}")

if __name__ == "__main__":
    main()

使い方と実行例

  1. 上記のコード全体をコピーし、future_time_calculator.pyのような名前でファイルを保存します。
  2. ターミナルやコマンドプロンプトを開き、以下のコマンドでスクリプトを実行します。
    python future_time_calculator.py
    
  3. 画面の指示に従って、色々な形式で経過時間を入力してみてください!
    🚀 未来時刻計算スクリプト 🚀
    経過時間を入力してください。
    例: 3d23h50m (3日23時間50分), 12:40 (12時間40分), 1d12h (1日と12時間)
    
    経過時間を入力してください (終了するには 'q' を入力): 1d 12h
    ----------------------------------------
    現在時刻  : 2025-06-08 21:00:00
    経過時間  : 1 day, 12:00:00
    未来の時刻: 2025-06-10 09:00:00
    ----------------------------------------
    
    経過時間を入力してください (終了するには 'q' を入力): 50m 2d
    ----------------------------------------
    現在時刻  : 2025-06-08 21:00:30
    経過時間  : 2 days, 0:50:00
    未来の時刻: 2025-06-10 21:50:30
    ----------------------------------------
    
    経過時間を入力してください (終了するには 'q' を入力): 10:30
    ----------------------------------------
    現在時刻  : 2025-06-08 21:01:00
    経過時間  : 10:30:00
    未来の時刻: 2025-06-09 07:31:00
    ----------------------------------------
    
    経過時間を入力してください (終了するには 'q' を入力): q
    スクリプトを終了します。
            

5. まとめ:標準ライブラリの組み合わせでアイデアを形に!

今回は、これまでの学習内容を組み合わせる実践プロジェクトとして、「未来時刻計算ツール」を作成しました。このプロジェクトを通して、

といった、多くの重要なテクニックを体験することができました。

プログラムは、このように基本的な部品(関数やモジュール)を一つずつ作り、それらを組み合わせていくことで完成します。今回のツールをベースに、さらに機能を追加してみる(例: 秒(s)や週(w)に対応させる、過去の日時を計算できるようにする、GUIを追加する)のも、素晴らしい学習になるはずです。

自分で考えたアイデアを、Pythonという強力な道具を使って形にする楽しさを、これからもぜひ味わっていってください。応援しています!


次回は【Pillow入門 第1回】Pythonで画像処理をはじめよう!読み込み・リサイズ・回転・切り抜きも自由自在 🖼️✨

【本記事の改善版スクリプトを紹介】曜日を追加する方法について解説

その他の投稿はこちら

コメント

このブログの人気の投稿

タイトルまとめ

これで迷わない!WindowsでPython環境構築する一番やさしい方法 #0

【Python標準ライブラリ完結!】11の冒険をありがとう!君のPython力が飛躍する「次の一歩」とは? 🚀