yoshiislandblog.net
元営業の駆け出しアラサーSEが、休日にMACと戯れた際の殴り書きメモ。日々勉強。日々進歩。
python

datetime?timedelta?timeモジュールとの違い?、、Pythonのdatetimeと関連モジュールを整理する

2022-09-30

Pythonでの日時操作について、今まで理解を怠っていたが、改めて公式ドキュメントを読んでそれぞれのモジュールの使い方と違いを理解した
※ 全てPython3.9の標準ライブラリの範囲内

この記事で使ったコードはこちら
https://github.com/yoshi-island/datetime_work

datetimeモジュール

「datetime」は日付や時間を扱うための一番基本のモジュール

datetimeでタイムゾーン情報を持つものはaware、持たないものはnativeと言う(=「tzinfo」で何かしら指定されているかどうか)
awareとnativeが混ざった状態では計算できない

公式ドキュメントに書いてある通りだが、datetimeはいくつかデータ型を提供していて、まとめると以下の通り

データ型 説明 書き方 書き方例 書き方例のprint結果
date 日付 datetime.date(【year】, 【month】, 【day】) datetime.date(1990, 7, 2) 1990-07-02
time 時間 datetime.time(【hour】, 【minute】, 【second】, 【microsecond】, 【tzinfo】) datetime.time(16, 30, 20, 79060, tzinfo=zoneinfo.ZoneInfo(“Asia/Tokyo”)) 16:30:20.079060
datetime 日付と時間 datetime.datetime(【year】, 【month】, 【day】, 【hour】, 【minute】, 【second】, 【microsecond】, 【tzinfo】) datetime.datetime(1990, 7, 2, 16, 30, 20, 79060, tzinfo=zoneinfo.ZoneInfo(“Asia/Tokyo”)) 1990-07-02 16:30:20.079060
timedelta date/datetimeなどの差 【date/datetime型】-【date/datetime型】
など
datetime.date(2022, 9, 29) – datetime.date(1990, 7, 2) 11777 days, 0:00:00

datetime型で時間を指定しなければ「ゼロ(00:00:00)」になる
tzinfoに何も指定しないと「None」になる

以下はコード例と出力結果
(zoneinfoモジュールについては後述)

% cat datetime_work.py
import datetime
import zoneinfo

print("=====date型=====")
type_date = datetime.date(1990, 7, 2)
print(type_date)
print(type(type_date))

print("=====time型=====")
type_time = datetime.time(16, 30, 20, 79060, tzinfo=zoneinfo.ZoneInfo("Asia/Tokyo"))
print(type_time)
print(type_time.tzinfo)
print(type(type_time))

print("=====datetime型=====")
type_datetime = datetime.datetime(1990, 7, 2, 16, 30, 20, 79060)
print(type_datetime)
print(type_datetime.tzinfo)
print(type(type_datetime))

print("=====timedelta型=====")
type_timedelta = datetime.date(2022, 9, 29) - datetime.date(1990, 7, 2)
print(type_timedelta)
print(type(type_timedelta))
% python datetime_work.py
=====date型=====
1990-07-02
<class 'datetime.date'>
=====time型=====
16:30:20.079060
Asia/Tokyo
<class 'datetime.time'>
=====datetime型=====
1990-07-02 16:30:20.079060
None
<class 'datetime.datetime'>
=====timedelta型=====
11777 days, 0:00:00
<class 'datetime.timedelta'>

print結果を整形したい場合は、strftime(datetime→str)と strptime(str→datetime)を使う

以下の例では、strftimeで、datetime型のtype_datetimeを「%Y/%m/%d %H:%M:%S」という形式のstr型に変換した後、strptimeでdatetime型に再び戻している

type_datetime = datetime.datetime(1990, 7, 2, 16, 30, 20, 79060)
print(type_datetime)
print(type(type_datetime))
print("=====strftimeを使ってdatetime型からstr型へ変換=====")
type_str = type_datetime.strftime("%Y/%m/%d %H:%M:%S")
print(type_str)
print(type(type_str))

print("=====strptimeを使ってstr型からdatetime型へ変換=====")
type_datetime_2 = datetime.datetime.strptime(type_str, "%Y/%m/%d %H:%M:%S")
print(type_datetime_2)
print(type(type_datetime_2))
1990-07-02 16:30:20.079060
<class 'datetime.datetime'>
=====strftimeを使ってdatetime型からstr型へ変換=====
1990/07/02 16:30:20
<class 'str'>
=====strptimeを使ってstr型からdatetime型へ変換=====
1990-07-02 16:30:20
<class 'datetime.datetime'>
※ 書式の書き方については以下公式ドキュメント参照
参考:strftime() と strptime() の書式コード

zoneinfoモジュール

「zoneinfo」はPythonバージョン3.9で追加されたモジュールで、tzinfoの指定をする

公式ドキュメント:zoneinfo — IANA time zone support

上のdatetimeモジュールのコード例で言うと、この部分で使っている

print("=====time型=====")
type_time = datetime.time(16, 30, 20, 79060, tzinfo=zoneinfo.ZoneInfo("Asia/Tokyo"))
print(type_time)
print(type_time.tzinfo)
print(type(type_time))

※ 出力結果

=====time型=====
16:30:20.079060
Asia/Tokyo
<class 'datetime.time'>

Pythonバージョン3.9以前であれば、timedelta型で指定したり、deteutilやpytzなどのサードパーティのモジュールを使う
例えば上のコードをtimedelta型に書き換えると以下の通り

print("=====timedeltaを使ってtzinfoを指定する=====")
#type_time = datetime.time(16, 30, 20, 79060, tzinfo=zoneinfo.ZoneInfo("Asia/Tokyo"))
type_time = datetime.time(16, 30, 20, 79060, tzinfo=datetime.timezone(datetime.timedelta(hours=+9), "Asia/Tokyo"))
print(type_time)
print(type_time.tzinfo)
print(type(type_time))

※ 出力結果

=====timedeltaを使ってtzinfoを指定する=====
16:30:20.079060+09:00
Asia/Tokyo
<class 'datetime.time'>

“Asia/Tokyo”はUTC+9だから、、と指定しなければならず、少し面倒、、
故に、deteutilやpytzなどのモジュールがあるのだと思われる

まあ、3.9からは何も考えずzoneinfoを使うのが良さそう

timeモジュール

次はtimeモジュール

今までは、time.sleepでしかお世話になっていなかったが、実は色々な使い方があった

正直、公式ドキュメントを読んだだけでは、datetimeとの使い分け方をしっかり理解できた訳ではないが、より時刻操作をしたいのであればtimeモジュールを使うのが良いと思われる

timeモジュールでは、localtimeなどで取得した時刻は「struct_time型」という型として返される
struct_time型で返された時刻は、strftimeを使ってstr型に変換できる

% cat time_work.py
import time

print("=====現在のlocal時間の取得=====")
local_time = time.localtime()
print(local_time)
print(type(local_time))

print("=====struct_time型→str型への変換=====")
type_str = time.strftime("%Y/%m/%d %H:%M:%S", local_time)
print(type_str)
print(type(type_str))

print("=====str型→struct_time型への変換=====")
type_struct_time = time.strptime(type_str, "%Y/%m/%d %H:%M:%S")
print(type_struct_time)
print(type(type_struct_time))
% python time_work.py
=====現在のlocal時間の取得=====
time.struct_time(tm_year=2022, tm_mon=9, tm_mday=30, tm_hour=0, tm_min=14, tm_sec=57, tm_wday=4, tm_yday=273, tm_isdst=0)
<class 'time.struct_time'>
=====struct_time型→str型への変換=====
2022/09/30 00:14:57
<class 'str'>
=====str型→struct_time型への変換=====
time.struct_time(tm_year=2022, tm_mon=9, tm_mday=30, tm_hour=0, tm_min=14, tm_sec=57, tm_wday=4, tm_yday=273, tm_isdst=-1)
<class 'time.struct_time'>

calendarモジュール

calendarモジュールについては、「カレンダーを出力してくれるモジュールでしょ(いつ使うん?)」くらいの知識だったが、datetime.date型で返してくれるメソッドが結構あったので試しに動かしてみた

% cat calendar_work.py
import calendar

print("=====calendar出力=====")
print(calendar.month(2022, 9))

print("=====datetime.date型で週リスト出力=====")
C = calendar.Calendar(firstweekday=0)
print(C.monthdatescalendar(2022, 9))
% python calendar_work.py
=====calendar出力=====
   September 2022
Mo Tu We Th Fr Sa Su
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

=====datetime.date型で週リスト出力=====
[[datetime.date(2022, 8, 29), datetime.date(2022, 8, 30), datetime.date(2022, 8, 31), datetime.date(2022, 9, 1), datetime.date(2022, 9, 2), datetime.date(2022, 9, 3), datetime.date(2022, 9, 4)], [datetime.date(2022, 9, 5), datetime.date(2022, 9, 6), datetime.date(2022, 9, 7), datetime.date(2022, 9, 8), datetime.date(2022, 9, 9), datetime.date(2022, 9, 10), datetime.date(2022, 9, 11)], [datetime.date(2022, 9, 12), datetime.date(2022, 9, 13), datetime.date(2022, 9, 14), datetime.date(2022, 9, 15), datetime.date(2022, 9, 16), datetime.date(2022, 9, 17), datetime.date(2022, 9, 18)], [datetime.date(2022, 9, 19), datetime.date(2022, 9, 20), datetime.date(2022, 9, 21), datetime.date(2022, 9, 22), datetime.date(2022, 9, 23), datetime.date(2022, 9, 24), datetime.date(2022, 9, 25)], [datetime.date(2022, 9, 26), datetime.date(2022, 9, 27), datetime.date(2022, 9, 28), datetime.date(2022, 9, 29), datetime.date(2022, 9, 30), datetime.date(2022, 10, 1), datetime.date(2022, 10, 2)]]

これでdatetimeと関連モジュール周りについてなんとなく違いが理解できた
以上。