リファクタリングをPythonで実践!基本手法とコード例・注意点まで
- 1.datetimeモジュールとは?
- 2.timeモジュールとdatetimeモジュールの違い
- 3.datetimeモジュールの使い方
- 4.datetimeモジュールのオブジェクト
- 5.日付・時刻の計算、比較
- 6.日付・時刻オブジェクトと文字列の変換
- 7.datetimeモジュールを使うときの注意点
情報を扱う上で欠かせない項目の一つに「日時」がありますね。
Pythonの場合は、「datetime」という日時に関するモジュールが用意されています。
datetimeの特徴から使い方までをご紹介していきます。
1.datetimeモジュールとは?
datetimeモジュールは、Pythonに標準で備わっているモジュールの一つで、Pythonの実行環境があればすぐに使用できます。
datetimeを使用すると、今日の日付や明日の日付をプログラムから取得でき、日付の表示や時間の表示ができるようになります。
また特定の日付の曜日を取得したり、開始から何秒経過したか計測したりと人間では把握しにくい時間情報も取得可能。
データ分析をはじめ、Webアプリケーションやスクレイピング、ゲーム開発など様々な場面でdatetimeは使用しますので、基礎をおさえておきましょう。
2.timeモジュールとdatetimeモジュールの違い
pythonには、datetimeモジュール以外に「timeモジュール」という似たようなモジュールがあります。
2つの違いをご紹介します。
①時間の基準が違う
私たちが普段見る時計は、日本時間に設定された時刻をみます。基準は、地球の自転ですね。
プログラミングの世界では、私たちが普段見る時間とは異なる時刻基準の「UNIX時間」があります。
UNIX時間は、1970年1月1日0時0分0秒を基準とし、基準から何秒経っているかを表す時刻になります。
たとえば今の時間をUNIX時間と普通の時間(非UNIX時間)で見てみると以下のようになります。
UNIX時間 |
結果
|
非UNIX時間 |
結果
|
UNIX時間の方は、1970年1月1日から16億4783万秒経っていることを示し、非UNIX時間の方は見慣れた時刻を表示してくれています。
Pythonで使用する timeモジュールは、UNIX時間を扱います。一方のdatetimeモジュールは非UNIX時間を扱います。
②用途が違う
UNIX時間はコンピューターの使用環境に関係なく同じ出力を行います。アメリカのパソコンでも日本のパソコンでも上記のような time.time() とすると同じ出力結果がでます。
一方非UNIXのdatetimeは、コンピューターにセットされている時刻情報を取得します。パソコンの初期設定で、利用する国やタイムゾーンを設定した覚えはありませんか?
このUNIX、非UNIXの特徴から、一般的に用途は以下のように分かれます。
UNIX時間
・処理時間の計測用
・処理の一時停止用など
非UNIX時間
・現在日時を表示する場合
・カレンダーのような日時データを扱う場合
・時系列でデータ処理を行いたい場合など
③データ型が違う
timeモジュールとdatetimeモジュールでは、取得した時刻のデータ型が異なります。
文字列と連結させたり、時間の差を求めたりする場合にデータ型を知っておく必要がありますね。
timeモジュール |
結果
|
datetimeモジュール |
結果
|
timeモジュールは、float型がデフォルトで、str() や int() でデータ型を変換して利用できます。
一方のdatetimeは、datetimeオブジェクトで出力されます。一見すると扱いにくそうに思われるかもしれませんが、日付だけ扱いたい場合や時間だけ扱いたい場合など時間を制御したい時には便利なデータ型です。
たとえば今の時間を単位別に表示してみます。
now = datetime.datetime.now()
print( now.year )
print( now.month )
print( now.day )
print( now.hour )
print( now.minute )
print( now.second )
結果
2022
3
21
7
38
25
このようにtimeとdatetimeでは出力の形態も異なりますので、用途に合わせてtimeを使うべきか、datetimeを使うべきか判断するといいでしょう。
3.datetimeモジュールの使い方
改めてdatetimeの使い方をご紹介します。
①モジュールをimportする
datetimeモジュールはPython標準モジュールなので、pip install ◯◯することなくすぐに利用できます。
import datetime
②オブジェクトを使う
datetimeオブジェクトを使うと、日時に関する情報を制御できるようになります。
たとえば今の日時は、以下のようにプログラムし、表示できます。
import datetime
print(datetime.datetime.now().date())
結果
2022-03-21
datetimeオブジェクトに、now()メソッドとdate()メソッドを加えて今日の日付を表示しています。
4.datetimeモジュールのオブジェクト
datetimeに備わっている便利なオブジェクトをご紹介します。
①dateオブジェクトで日付を扱う
日付の情報のみを扱いたい場合は、dateオブジェクトを使います。
import datetime
my_birth_day = datetime.date(2022, 4, 1)
print(my_birth_day)
print(type(my_birth_day))
結果
2022-04-01
<class 'datetime.date'>
dateオブジェクトには、西暦や月、日の情報を入れることができます。
②timeオブジェクトで時刻を扱う
特定の時間を扱いたい場合は、timeオブジェクト使います。
import datetime
wakeup_time = datetime.time(8,10,11)
print(wakeup_time)
print(type(wakeup_time))
結果
08:10:11
<class 'datetime.time'>
time()オブジェクトの中に時間、分、秒、ミリ秒、タイムゾーンの情報を入れることができます。
③datetimeオブジェクトで日時を扱う
日付と時刻の両方を扱いたい場合は、datetimeオブジェクトを利用します。
import datetime
now = datetime.datetime(2022,3,22,18,19)
print(now)
print(type(now))
結果
2022-03-22 18:19:00
<class 'datetime.datetime'>
datetime()オブジェクトには、西暦、月、日、時間、分、秒、ミリ秒、タイムゾーンの情報を入れることができます。
④timedeltaオブジェクトで時間差を扱う
1週間後の日付や5日前の日付などを求めるときは、timedeltaオジェクトが便利です。ブログやSNSでよく「◯日前の記事」など書かれていますよね。そういった情報として使えます。
たとえば5日前の日付を確認してみます。
import datetime
prev_5_days = datetime.timedelta(days=5)
today = datetime.date.today()
diff_days = today - prev_5_days
print(today)
print(diff_days)
結果
2022-03-22
2022-03-17
timedelta()オブジェクトに days=5 とすることで、5日前の日付データを設定。そして今日の日付(変数:today)からtimedelta()オブジェクトを引くことで5日前の 3-17 を出力しています。
timedelta()オブジェクトには、年数と月以外の、週、日、時間、分、秒、ミリ秒の情報を設定できます。
5.日付・時刻の計算、比較
時間によって処理を分岐したいときは、日時の計算や比較が必要になります。特にIoT系のプログラムを作成するときは必須ですね。
①timedeltaを使って加算、減算する
今から明日の2時間後の時間を求めたい場合は「加算」、今から昨日の1時間前を求める場合は「減算」をして日時を取得できます。
加算する場合
import datetime
next_day = datetime.timedelta(days=1, hours=2)
today = datetime.datetime.now()
diff_days = today + next_day
print('今 : ' + str(today))
print('加算結果: ' + str(diff_days))
結果
今 : 2022-03-22 09:39:42.528012
加算結果: 2022-03-23 11:39:42.528012
timedelta()オブジェクトに days=1とすることで1日の意味を持たせ、 hours=2とすることで2時間の意味を持たすことができます。
そして今の日時データを持つ変数:todayにtimedelta()オブジェクトを加えることで、1日後+2時間後の日時データを取得することができます。
減算する場合
import datetime
previous_day = datetime.timedelta(days=1, hours=2)
today = datetime.datetime.now()
diff_days = today - previous_day
print('今 : ' + str(today))
print('減算結果: ' + str(diff_days))
結果
今 : 2022-03-22 09:47:23.507083
減算結果: 2022-03-21 07:47:23.507083
先ほどの加算とは逆に、今の日時(変数:today)からtimedelta()オブジェクトを引くことで、昨日の2時間前の情報を取得することができます。
足し算、引き算と同じ感覚で時間を操作できるので便利ですね。
②差を求める
利用を始めてからどれぐらい経過したか、最後に使ってからどれぐらい経過したか、などの時間情報を求めたい場合は、「差」を求めると確認できますよね。
import datetime
prev_use = datetime.datetime(2021, 2, 2, 22)
next_use = datetime.datetime(2022, 3, 29, 10)
diff_date = next_use - prev_use
print(diff_date)
結果
419 days, 12:00:00
419 days, 12:00:00
ポイントとしては、同じオブジェクト同士で引き算をする必要がある点。
上記とは違って下記のようにするとエラーが出ます。
import datetime
prev_use = datetime.datetime(2021, 2, 2)
next_use = datetime.date(2022, 3, 29)
diff_date = next_use - prev_use
print(diff_date)
結果
TypeError: unsupported operand type(s) for -: 'datetime.date' and 'datetime.datetime'
datetimeオブジェクトとdateオブジェクトと異なるオブジェクトの差を求めようとしているため、エラーになっています。
③比較する
datetimeモジュールを使うと日時の比較も数字比較のように簡単にできます。
import datetime
prev_use = datetime.datetime(2021, 2, 2, 22)
now = datetime.datetime.now()
if now > prev_use:
print('以前にお使い頂いたお客様ですね.')
else:
print('初めてのご利用ですね.')
結果
以前にお使い頂いたお客様ですね.
日時を比較するときも「差」のときと同様、同じオブジェクトで比較する必要があります。
6.日付・時刻オブジェクトと文字列の変換
datetimeモジュールから生成したオブジェクトを文字列に変換する方法とその逆の文字列からdatetimeのオブジェクトを生成する方法をご紹介します。
①strftime()でオブジェクトを文字列に変換
datetimeモジュールには日時を表示しやすいように、 .minute や .month 以外に%Yや%Mなどのコントローラーも用意されています。
%Yや%Mは、.minuteや.monthなどに比べて、日時情報を文字として扱いやすくなる特徴があります。
.minuteや.monthの場合
import datetime
now = datetime.datetime.now()
print( '今の時間: ' + str(now.year) + '年' + str(now.month) + '月' + str(now.day) + '日' + ' ' + str(now.hour) + str(':') + str(now.minute) )
結果
今の時間: 2022年3月22日 19:25
%Yや%Mの場合
import datetime
now = datetime.datetime.now()
print( '今の時間: ' + now.strftime ( '%Y 年 %m 月 %d 日 (%A) %H : %M' ) )
結果
今の時間: 2022 年 03 月 22 日 (Tuesday) 19 : 25
datetimeオブジェクトの年や時間などを表す書式化コードの一例を以下に紹介します。
コード | 意味 |
%a | ロケールの曜日名を省略形で表示。(Sun, Mon etc) |
%A | ロケールの曜日名を表示。(Sunday, Monday etc) |
%b | ロケールの月名を省略形で表示。(Jan, Feb etc) |
%B | ロケールの月名を表示。(January, February etc) |
%d | 10進数で月の初めから何日目かを表示。 |
%f | 10進数でマイクロ秒を表示。 |
%H | 10進数で24時間形式での時を表示。 |
%I | 10進数で12時間形式での時を表示。 |
%j | 10進数で年の初めから何日目かを表示。 |
%m | 10進数で月を表示。 |
%M | 10進数で分を表示。 |
%p | ロケールのAM・PMに対応する文字列。 |
&S | 10進数で秒を表示。 |
%w | 曜日を10進表記した文字列を表示。表示は日曜日が0、土曜日が6。 |
%x | ロケールの適切な日付を表示。 |
%X | ロケールの適切な時刻を表示。 |
%y | 10進数で西暦年の下2桁を表示。 |
%Y | 10進数で4桁の西暦年を表示。 |
%Z | タイムゾーン名。タイムゾーンがない場合は空文字列。 |
書式化コードは上記以外にもあります。詳しくはPythonの公式ページを参考にしてみてください。
②strptime()で文字列をオブジェクトに変換
文字列の日時情報をdatetimeのオブジェクトに変換する方法は2種類あります。
・日時情報を数値型に変換して、datetimeのオブジェクトに投入
・strptime()メソッドでdatetimeのオブジェクトに変換
strftime()同様、後者の方がスマートにプログラムを制御することができます。
strftime()の使用例
import datetime
date_str = '1980/8/5 12:30'
birthday = datetime.datetime.strptime(date_str, '%Y/%m/%d %H:%M')
print(birthday)
print(type(birthday))
結果
1980-08-05 12:30:00
<class 'datetime.datetime'>
上記コードのポイントは、strptime(date_str, ‘%Y/%m/%d %H:%M’)の部分ですね。
strptime()メソッドの第一引数に文字型の日時情報、第二引数に書式化コードを割り当てています。この時の注意点は、 / や : などの文字をそのまま書式化コード内に引用する必要があるところです。/ を年や月に変えるとエラーが出ますね。
エラーになるケース
date_str = '1980/8/5 12:30'
birthday = datetime.datetime.strptime(date_str, '%Y年%m月%d日 %H:%M')
print(birthday)
結果
ValueError: time data '1980/8/5 12:30' does not match format '%Y年%m月%d日 %H:%M'
7.datetimeモジュールを使うときの注意点
datetimeモジュールを使う時の注意点をいくつかご紹介します。
①importに注意
本稿ではimport datetimeでdatetimeモジュールを使用してきました。しかし、サンプルコードによっては以下のような書き方もあります。
(datetimeをクラスで読み込む場合)
import datetime as dt
print( dt.datetime.now() )
(datetimeを関数で読み込む場合)
from datetime import datetime
print( datetime.now() )
datetimeモジュールをクラスとして読み込むか、関数として読み込むかの違いですね。
datetime.datetime.now()とするか、上記のような書き方でいくかご判断ください。
②タイムゾーンに注意
datetimeモジュールは、非UNIX時間であるため、プログラムを動かす環境によって表示時間が異なります。
自分のパソコン
Google Colab
今は日本時間朝5時ですが、Google Colabの方はイギリス時間を表示しています。
タイムゾーンを合わせるためには、専用のコードを記述する必要があります。
japan_time = datetime.datetime.now(
datetime.timezone(datetime.timedelta(hours=9))
)
print(japan_time)
結果
2022-03-23 05:26:33.916664+09:00
datetimeモジュールに標準で用意されているtimezoneコンストラクタにdatetime.timedelta(hours=9)を設定することで日本時間を表現できます。
9の部分がUTC時刻基準との差を表しますね。