\ ポイント最大11倍! /

【Python】日付と時刻を効率的に操作する方法

  • Python で日付データを扱いたい
  • 日付から別の型への変換がしたい
  • 日付型に苦手意識がある

他の型と比べるとやや分かりづらいのが「日付型」です。

それもそのはず、ひとくちに日付型といっても以下の複合的な要因で成り立っています。

  • 「年」「月」「日」などの複数要素から成り立つ
  • 地球上には時差があるので、測定する場所で時間は異なる
  • 色々な型変換の方法がある

そのため、初見では理解しづらいテーマです。

本記事では、そんな日付型の操作に必要な知識を Python 初心者の方でも理解できるように網羅的に扱いました。

Python で日時を扱うための基礎知識

まずは日付型を理解するために知っておくと便利な予備知識から解説します。

日付を扱うライブラリ

基本的には以下のモジュールを使うことになります。

  • datetime
  • time
  • calendar

ただし、少し複雑な集計などを行う場合には Pandas ライブラリを使うと便利です。

Pandas の方が少ないコードで複雑な実装ができますし、C言語で最適化されているので動作も超高速です。

Pandas について詳しく知りたい方は別記事をどうぞ。
» 業務系アプリケーションで Pandas を活用する方法

タイムゾーン

地球上には時差があるので、どの場所を基準にするかによって現在時刻は異なります。

この「どこを基準にした時間かを記録する」ものがタイムゾーンです。

ここで覚えておくと便利な用語が、Aware と Naive です。

Aware がタイムゾーンを含んだ日時オブジェクト、 Naive がタイムゾーンを含まない日時オブジェクトを指します。

以下のようにタイムゾーンに関する指定をせずに日付取得する場合は、タイムゾーン情報なしの値が取得されます。つまり、Naive なオブジェクトが取得されます。

import datetime

now = datetime.datetime.now()
timezone = now.tzinfo

# [timezone]
# None

Aware なタイムゾーン付きの日付オブジェクトを取得する場合には、以下のように timezone() メソッドなどを使って場所に関する情報を付与してあげることになります。

import datetime


# タイムゾーン = UTC にセット
now_utc = datetime.datetime.now(datetime.timezone.utc)
timezone_utc = now_utc.tzinfo

# [timezone_utc]
# UTC
# <class 'datetime.timezone'>

# タイムゾーン = 日本にセット
jst = datetime.timezone(datetime.timedelta(hours=9))
now_jst = datetime.datetime.now(jst)
timezone_jst = now_jst.tzinfo

# [timezone_jst]
# UTC+09:00
# <class 'datetime.timezone'>

上記で timedelta(hours=9) としている理由は、日本は UTC から9時間の時差があるためです。

ただしすべての時差をぱっと見で理解するのはそこそこ大変だと思いますので、これらの情報があらかじめ記録された pytz ライブラリで実現するとより簡潔に記述できます。

imoprt pytz
import datetime


# タイムゾーン = Asia/Tokyo にセット
jst = pytz.timezone("Asia/Tokyo")
now_jst = datetime.datetime.now(jst)
timezone_jst = now_jst.tzinfo

# [timezone_jst]
# Asia/Tokyo
# <class 'pytz.tzfile.Asia/Tokyo'>

時差を直接指定するよりも、Asia/Tokyo とした方が視覚的に分かりやすいでしょう。

現在の日時を取得

ログをはじめ、現在の日時を出力したいシチュエーションはかなり多いと思います。

日付だけ、時間だけといったバリエーションも含めて、取得方法をご紹介します。

現在日時の取得

日時情報を丸ごと取得したい場合は datetime() メソッドを使います。

import datetime

now = datetime.datetime.now()

# [now]
# 2023-10-14 22:08:25.355012
# <class 'datetime.datetime'>

現在日付の取得

「年月日」部分だけを取得したい場合には、today() メソッドを使います。

import datetime

today = datetime.date.today()

# [today]
# 2023-10-14
# <class 'datetime.date'>

現在時刻の取得

「時間」部分だけを取得したい場合は、まずdatetime.now() で日時を求めた後に time() メソッドで時間部分だけを切り出します。

import datetime

now_time = datetime.datetime.now().time()

# [now_time]
# 22:53:17.458025
# <class 'datetime.time'>

現在のエポック秒(UNIX 時間)を取得

UTC(協定世界時)からの経過秒数をエポック秒といいます。

具体的には1970年1月1日0時0分0秒からの経過時間です。

import time

unix_time = time.time()

# [unix_time]
# 1697325018.0934207
# <class 'float'>

特定の日時データを取得する

日時データからより細かい情報を取得する方法をご紹介します。

ある月の日数を取得する

monthrange() メソッドを使うと、タプルの形式で (月の初日の曜日, 月の日数)という形で戻り値が得られます。

import calendar

# 2023年10月の初日の曜日と、日数
days_of_week, days = calendar.monthrange(2023, 10)

# [days_of_week]
# 6

# [days]
# 31

上記のようにアンパッキングしてそれぞれの変数に、各値を入れると便利です。

なお曜日は月曜日を0、日曜日を6とする値になります。

日数だけ取得したい場合は以下のように書けます。

import calendar

# 2023年10月の日数
_, days = calendar.monthrange(2023, 10)

# [days]
# 31

もちろん、calendar.monthrange(2023, 10)[1]のような書き方でも OK です。

日付から曜日を取得する

weekday() メソッドを使うことで、曜日を表す数値が得られます。

import datetime

now = datetime.datetime.now()
weekday = now.weekday()

# [now]
# 2023-10-17 09:58:04.530179
# <class 'datetime.datetime'>

# [weekday]
# 1
# <class 'int'>

なお曜日は月曜日を0、日曜日を6とする値になります。

【timedelta】時間どうしの差分から計算する

時間どうしの差分を計算したい場合には timedelta を使いましょう。

timedelta を使った時間の足し算・引き算

以下が timedelta() メソッドを使った時間の足し算の方法です。

from datetime import datetime, timedelta

# 現在日時を取得
now = datetime.now()

# 現在日時から時間を足し算する
tomorrow = now + timedelta(
    # 1週間後
    weeks=1,
    # 1日後
    days=1,
    # 1時間後
    hours=1,
    # 1分後
    minutes=1,
    # 1秒後
    seconds=1,
    # 1マイクロ秒後
    microseconds=1,
    # ミリ秒後
    milliseconds=1,
)

上記では説明のためにすべての属性を使いましたが、値を0としたい場合には属性を省略すれば大丈夫です。

例えば、単に現在から1日後の日付型を取得したいなら以下でいけます。

from datetime import datetime, timedelta

# 現在日時を取得
now = datetime.now()

# 現在日時から1日後を取得
tomorrow = now + timedelta(days=1)

引き算の場合にはマイナス(-)を指定します。

from datetime import datetime, timedelta

# 現在日時を取得
now = datetime.now()

# 現在日時から1日前を取得
tomorrow = now - timedelta(days=1)

上記では- timedelta() としましたが、+ timedelta(days=-1) としても結果は同じです。どちらか可読性の高い方を採用すべきと思います。

relativedelta を使った時間の足し算・引き算

簡単な時間の足し算・引き算は timedelta() で十分ですが、月単位・年単位の時間の足し算・引き算は relativedelta() を使うのが簡単です。

from datetime import datetime
from dateutil.relativedelta import relativedelta

# 現在日時を取得
now = datetime.now()

# 2ヶ月後の日付
two_months_later = now + relativedelta(months=+2)

このdatiutil は標準ライブラリではないので、pip 等でインストールしておく必要があります。

pip install python-dateutil

datetime 型オブジェクトの変換

datetime オブジェクトは他の形式に変換することも可能です。

datetime 型から str 型に変換する方法

datetime 型から str 型に変換する方法は、strftime() メソッドを使う方法と属性名で呼び出す方法の二つがあります。

strftime() メソッドを使う方法

「2023年10月14日」のように、複数の属性の組み合わせで取得したい場合には strftime() メソッドを使います。

属性とは年、月、時間などの各要素のことです。

import datetime

now = datetime.datetime.now()

ymd = now.strftime("%Y年%m月%d日")

# [ ymd ]
# 2023年10月14日
# <class 'str'>

ymdhms = now.strftime("%Y年%m月%d日 %H:%M:%S")

# [ ymdhms ]
# 2023年10月14日 22:16:02
# <class 'str'>

どの属性を使って文字列にするかは、書式コードというものを使います。

どんな書式コードがあるかについては公式ドキュメントに網羅されているので、必要に応じて参照しつつコードを完成させてみてください。

» 公式ドキュメント:strftime() と strptime() の書式コード

属性名で呼び出す方法

日付だけを抽出したい場合など、一つの属性だけが必要な場合には.属性名とすることで int 型で取得できます。

以下は指定できる属性のすべてになります。

import datetime

now = datetime.datetime.now()

now.year

# [ now.year ]
# 2023
# <class 'int'>

now.month

# [ now.month ]
# 10
# <class 'int'>

now.day

# [ now.day ]
# 14
# <class 'int'>

now.hour

# [ now.hour ]
# 22
# <class 'int'>

now.minute

# [ now.minute ]
# 37
# <class 'int'>

now.second

# [ now.second ]
# 7
# <class 'int'>

now.microsecond

# [ now.microsecond ]
# 691433
# <class 'int'>

datetime オブジェクトの判定

判定系のメソッドをまとめました。

うるう年の判定

calendar モジュールを使うことで簡単に判定ができます。

import calendar

is_leap_2020 = calendar.isleap(2020)

# [ is_leap_2020 ]
# True

is_leap_2023 = calendar.isleap(2023)

# [ is_leap_2023 ]
# False

上記の関数を使えば気にする必要はないですが、うるう年かどうかの判定ロジックは以下のように求められます。

  1. 西暦年が4で割り切れる年は(原則として)閏年。
  2. ただし、西暦年が100で割り切れる年は(原則として)平年。
  3. ただし、西暦年が400で割り切れる年は必ず閏年。
Wikipedia: グレゴリオ暦

calendar モジュールを使いたくない場合は、自作の関数で対応することもできます。

def is_leap_year(target_year: int):
    """うるう年か判定"""
    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
        return True
    else:
        return False

この記事が気に入ったら
フォローしてね!

シェア・記事の保存はこちら!

この記事を書いた人

karo@プログラマのアバター karo@プログラマ プログラマ

「書くことで人の役にたつ」をモットーに活動中。
本職はプログラマで、Python(Django)が得意。最近ではフロント側の技術に触れる機会も増えてきました。
基本情報技術者試験合格。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)