Pythonメモ: Python で日時(日付/時刻)の情報を扱う (datetime, date, time, timedelta)

日時を表すデータ型

Python で日時を扱うには datetime モジュール を使用します。 datetime には次のようなデータ型が定義されています。

データ型保持する情報
datetime.datetime日付+時刻
datetime.date日付
datetime.time時刻
datetime.timedelta時間差(マイクロ秒)

すべてのオブジェクトは immutable(不変)で、ハッシュのキーとして使用できます。

日時を表す datetime オブジェクト

datetime.datetime オブジェクト は、日付と時刻の両方の情報を保持します。 datetime.date と datetime.time の情報を合わせたものです。 以下、datetime.datetime オブジェクトを datetime オブジェクトと記述します。

現在の日時を表す datetime オブジェクト

datetime.now() で、ローカルな現在日時を表す datetime オブジェクトを取得できます。 datetime オブジェクトには、年月日時分秒などの各フィールドの値を取り出す属性が定義されています。

import datetime from datetime

dt = datetime.now()
print(dt)  # 2021-01-13 20:10:58.598240
print(dt.year)        # 2021
print(dt.month)       # 1
print(dt.day)         # 13
print(dt.hour)        # 20
print(dt.minute)      # 10
print(dt.second)      # 58
print(dt.microsecond) # 598240
print(dt.tzinfo)      # None

タイムゾーン (Aware/Naive)

datetime オブジェクトはタイムゾーン情報(tzinfo 属性)を持つことができ、この情報が存在するとき(tzinfo が None でないとき)、そのオブジェクトは Aware であるといいます。 逆に、タイムゾーン情報を持たないとき(tzinfo が None のとき)、そのオブジェクトは Naive であるといいます。

アプリケーション内で、タイムゾーンを意識した時刻情報を扱いたい場合は、Aware な datetime オブジェクトを生成する必要があります。

下記の datetime クラスメソッドはすべて現在日時を表す datetime オブジェクトを生成しますが、内部的に保持するタイムゾーン情報に違いがあります。

現在日時の取得方法意味
datetime.now()Naive なローカル日時 (tzinfo == None)
datetime.utcnow()Naive な UTC 日時 (tzinfo == None)
datetime.now().astimezone()Aware なローカル日時 (tzinfo == システム設定)
datetime.now(timezone.utc)Aware な UTC 日時 (tzinfo == timezone.utc)

下記のサンプルコードを実行すると、これらの違いを調べることができます。

from datetime import datetime, timezone

dt_local_naive = datetime.now()
print(dt_local_naive.isoformat())  # 2021-01-13T20:46:50.587950
print(dt_local_naive.tzinfo)       # None

dt_utc_naive = datetime.utcnow()
print(dt_utc_naive.isoformat())    # 2021-01-13T11:46:50.587950
print(dt_utc_naive.tzinfo)         # None

dt_local_aware = datetime.now().astimezone()
print(dt_local_aware.isoformat())  # 2021-01-13T20:46:50.587950+09:00
print(dt_local_aware.tzinfo)       # JST

dt_utc_aware = datetime.now(timezone.utc)
print(dt_utc_aware.isoformat())    # 2021-01-13T11:46:50.587950+00:00
print(dt_utc_aware.tzinfo)         # UTC

日付を表す date オブジェクト

datetime.date オブジェクト は、日付(年月日)の情報を保持します。 タイムゾーン情報(Aware/Naive)は意識しません。

from datetime import date

dt = date.today()
print(dt.isoformat())  # 2021-01-13
print(dt.year)         # 2021
print(dt.month)        # 1
print(dt.day)          # 13

時刻を表す time オブジェクト

datetime.time オブジェクト は、時刻(時分秒)の情報を保持します。 datetime オブジェクトと同様、tzinfo 属性でタイムゾーン情報を扱うため、Aware/Naive の区別があります。

from datetime import datetime, time, timezone

# t = time(hour=20, minute=30, second=55, tzinfo=timezone.utc)
t = datetime.now().astimezone().timetz()

print(t.isoformat())  # 20:30:55.364310+09:00
print(t.hour)         # 20
print(t.minute)       # 30
print(t.second)       # 55
print(t.microsecond)  # 0
print(t.tzinfo)       # JST

時間差を表す timedelta オブジェクト

datetime.timedelta オブジェクト は、2 つの日時の差分を表します。

datetime、date、time すべてのオブジェクトに対して、timedelta オブジェクトを加算、減算することができます。 次の例では、datetime オブジェクトに対して、1 日の時間差を表す timedelta オブジェクトを使って演算処理しています。 timedelta の属性名は、days、hours、seconds のように複数形になることに注意してください。

from datetime import datetime, timedelta

dt = datetime(year=2021, month=1, day=1, hour=15, minute=30, second=0)
delta = timedelta(days=1)

print(dt)              # 2021-01-01 15:30:00
print(dt + delta)      # 2021-01-02 15:30:00
print(dt + delta * 2)  # 2021-01-03 15:30:00
print(dt - delta)      # 2020-12-31 15:30:00
print(dt - delta * 2)  # 2020-12-30 15:30:00

日時を表すオブジェクト (datetime / date / time) 同士で引き算すると、その時間差を表す timedelta オブジェクトを取得することができます。

from datetime import date, timedelta

d1 = date(year=2021, month=1, day=1)
d2 = date(year=2021, month=1, day=20)
delta = d2 - d1

print(delta)       # 19 days, 0:00:00
print(delta.days)  # 19
print(delta.total_seconds())  # 1641600.0

あと、時間差ではないですが、等号・不等号で 2 つの日時データの大小を比較することができます。

from datetime import datetime

dt1 = datetime(year=2000, month=1, day=1)
dt2 = datetime(year=2000, month=1, day=2)
print(dt1 > dt2)  # False
print(dt1 < dt2)  # True

これを利用して、ある日時の範囲を次のようにループ処理したりできます。

from datetime import date, timedelta

START_DATE = date(year=2000, month=1, day=1)
END_DATE = date(year=2000, month=1, day=3)
STEP = timedelta(days=1)

d = START_DATE
while (d <= END_DATE):
    print(d)
    d += STEP
2000-01-01
2000-01-02
2000-01-03

各フィールドの値を変更する (replace)

datetime、date、time オブジェクトの replace メソッドを使用すると、年月日時分秒などの各フィールドの値を個別に変更することができます。 datetime、time オブジェクトに関しては、tzinfo フィールドの値を変更することで、Naive と Aware を変換することも可能です。

from datetime import date, datetime, time

# 現在時刻から datetime, date, time オブジェクトを作成
dt = datetime.now().astimezone()
d = dt.date()
t = dt.timetz()

# datetime の内容を変更する
print(dt)                              # 2021-01-13 18:23:46.827369+09:00
print(dt.replace(year=1970, hour=21))  # 1970-01-13 21:23:46.827369+09:00

# date の内容を変更する
print(d)                           # 2021-01-13
print(d.replace(month=12, day=4))  # 2021-12-04

# time の内容を変更する
print(t)                       # 18:23:46.827369+09:00
print(t.replace(hour=7))       # 07:23:46.827369+09:00
print(t.replace(tzinfo=None))  # 18:23:46.827369

日時系オブジェクトの変換方法一覧

変換内容コード
date → ISO文字列d.isoformat()
date → 指定形式文字列d.strftime(format)
time → ISO文字列t.isoformat()
time → 指定形式文字列t.strftime(format)
datetime → ISO文字列dt.isoformat()
datetime → 指定形式文字列dt.strftime(format)
datetime (Naive) → datetime (Aware)dt.astimezone(tz=None)
datetime (Aware) → datetime (Naive)dt.replace(tzinfo=None)
datetime → datedt.date()
datetime → time (Naive)dt.time()
datetime → time (Aware)dt.timetz()
datetime → POSIXタイムスタンプdt.timestamp()
ISO文字列 → datedate.fromisoformat('2019-12-04')
ISO文字列 → time (Naive or Aware)time.fromisoformat('04:23:01')
ISO文字列 → datetime (Naive or Aware)datetime.fromisoformat('2011-11-04T00:05:23')
POSIXタイムスタンプ → datedate.fromtimestamp(timestamp)
POSIXタイムスタンプ → datetime (Naive or Aware)datetime.fromtimestamp(timestamp, tz=None)
POSIXタイムスタンプ → datetime (UTC Naive)datetime.utcfromtimestamp(timestamp)