プログラムで日時処理関係のコードを書いていると
- 期待した表現で日時処理を出力したいけど、どうやるんだっけ?
- タイムゾーン、めったに使わないから、いざ使うとなるとどうやるんだっけ?
- 別のオブジェクトに変換したいけど、やり方を忘れちゃった。
といった事態にならないでしょうか?
しかもネットで探し回っても、なかなか見つからない事が多いように思えます。
そこで本記事だけで、ほとんどのケースが網羅できるよう、まとめてみました。
timeモジュールと、datetimeモジュール どう違うの?
Pythonで日時関係を扱うモジュールはこの2つが多いですが、基本は、datetimeモジュールを使います。
timeモジュール
timeモジュールのtime()関数を使うと、UNIX時間(エポック秒、UNIXタイムスタンプとも言われる)が取得できます。
UNIX時間というのは、1970年1月1日 00時00分00秒 からの、経過秒数です。実例を見てみます。
import time
print(time.time())
# ⬆︎実行結果 1615249165.490577
print(type(time.time()))
# ⬆︎実行結果 <class 'float'>
現在のUNIX時間が少数点(float型)で返ってきています。
datetimeモジュール
日時関係の処理ができます。「2020年12月23日 08:32:21」みたいに、普段の日時感覚で処理できます。
このモジュールのdatetimeオブジェクトが基本的に使用するオブジェクトになります。(本モジュールのその他オブジェクトは、後ほど出てきます。)
よく使うインスタンス方法を下記に記します。
import datetime
# 現在日時にてインスタンス化
dt = datetime.datetime.now()
print(dt)
# ⬆︎実行結果 2021-03-11 19:59:29.304648
# 直接日時を打ち込む場合 例)2021年3月11日 10時23分34秒
dt = datetime.datetime(2021, 3, 11, 10, 23, 34)
print(dt)
# ⬆︎実行結果 2021-03-11 10:23:34
# インスタンス化の際、時間を入力しない場合 例)2021年3月11日
dt = datetime.datetime(2021, 3, 11)
print(dt)
# ⬆︎実行結果 2021-03-11 00:00:00
timeモジュールとdatetimeモジュール間の変換方法
正確には、UNIX時間(float型) ⇄ datetime オブジェクト の変換になります。
# Unix時間 ➡︎ datetimeオブジェクト
now_ts = time.time()
dt = datetime.datetime.fromtimestamp(now_ts)
print(dt)
# ⬆︎実行結果 2021-03-12 06:50:27.610806
# datetimeオブジェクト ➡︎ Unix時間
dt = datetime.datetime.now()
now_ts = dt.timestamp()
print(now_ts)
# ⬆︎実行結果 1615499391.672584
「日のみ」、「時間のみ」を扱いたい場合
「日のみ」を扱いたい場合
datetimeモジュールのdateオブジェクトを使います。
インスタンス化の事例
import datetime
# インスタンス 1例目
dt_date = datetime.date.today()
# インスタンス 2例目
dt_date = datetime.date(2021, 3, 11)
datetimeオブジェクトから、dateオブジェクトへの変換
dt = datetime.datetime(2021, 3, 11, 10, 23, 34)
dt_date = dt.date() #datetime ➡︎ dateオブジェクト に変換
print(dt_date)
# ⬆︎実行結果 2021-03-11
dateオブジェクトの「年、月、日」を取得する方法
dt_date = datetime.date(2021, 3, 11)
print(dt_date.year) #2021
print(dt_date.month) #3
print(dt_date.day) #11
時間のみを扱いたい場合
datetimeモジュールのtimeオブジェクトを使用します。
インスタンス化の事例
import datetime
dt_time = datetime.time(10, 23, 34, 2000) #10時23分34秒2000マイクロ秒
datetimeオブジェクトから、timeオブジェクトへの変換
dt = datetime.datetime(2021, 3, 11, 10, 23, 34)
dt_time = dt.time() #datetime ➡︎ timeオブジェクト に変換
print(dt_time)
# ⬆︎実行結果 10:23:34
timeオブジェクトの「時間、分、秒」を取得する方法
dt_time = datetime.time(10, 23, 34)
print(dt_time.hour) #10
print(dt_time.minute) #23
print(dt_time.second) #34
日時の差分を扱いたい場合
日時の差分を扱うためには、datetimeモジュールのtimedeltaオブジェクトを使用します。
インスタンスかは以下の通りです。
# コンストラクタ
class datetime.timedelta(days=0, seconds=0,
microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
# インスタンス化 事例1
time_delta = datetime.timedelta(days=1)
print(time_delta)
# ⬆︎実行結果 1 day, 0:00:00
# インスタンス化 事例2
time_delta = datetime.timedelta(hours=1)
print(time_delta)
# ⬆︎実行結果 1:00:00
またdatetimeオブジェクトに、加算(減算)する事例を記します。
dt = datetime.datetime.now()
print(dt)
# ⬆︎実行結果 2021-03-12 07:19:37.197561
dt2 = dt + datetime.timedelta(days=-1)
print(dt2)
# ⬆︎実行結果 2021-03-11 07:18:04.992962
datetimeオブジェクトどうしの差分(日時差、時刻差)も、timedelataオブジェクトになります。
dt1 = datetime.datetime(2021, 3, 11, 10, 23, 34)
dt2 = datetime.datetime(2021, 3, 10, 10, 23, 34)
time_delta = dt2 - dt1
print(time_delta)
# ⬆︎実行結果 -1 day, 0:00:00
print(type(time_delta)
# ⬆︎実行結果 <class 'datetime.timedelta'>
文字型の日時からdatetimeオブジェクトに変換したい場合
標準的な処理方法(標準ライブラリを使用)
次に様々な、文字列をdatetimeオブジェクトに変換する方法を記します。datetimeオブジェクトのstrptimeメソッドを使用します。
tstr = '2021-03-12 07:19:37'
dt = datetime.datetime.strptime(tstr, '%Y-%m-%d %H:%M:%S')
上記コードで記されている、%記号は以下の意味です。
(よく使用する物のみ、ピックアップしました。)
書式コード | 意味 | 表示例 |
---|---|---|
%Y | 年を数字で表現 (0埋めした、西暦4桁) | 0893 |
%y | 年を数字で表現 (0埋めした、西暦2桁) | 08 |
%m | 月を数字で表現 (0埋めした、月) | 03 |
%d | 日を数字で表現 (0埋めした、日) | 09 |
%H | 時間を数字で表現 (0埋めした、24h表記) | 21 |
%I | 時間を数字で表現 (0埋めした、12h表記) | 09 |
%M | 分を数字で表現 (0埋めした、分) | 03 |
%S | 秒を数字で表現 (0埋めした、秒) | 09 |
%f | マイクロ秒を表現 (0埋めした、6桁のマイクロ秒) | 000000 |
%A | 曜日を表現(指定したロケールでの曜日になります。) | Sunday |
表中のロケールという表現の詳細は、後ほどでてきます。
上記処理が面倒くさい場合
どんな文字列が来ても、同じ方法で処理する方法です。
dateutilモジュールを使用します。(サードパーティー製です。)
インストール方法
pip install python-dateutil
文字型の日時からの変換は、dateutil.parserオブジェクトのparseメソッドを使用します。
import dateutil.parser
time = '2021-03-11 17:13:39'
print(dateutil.parser.parse(time))
# ⬆実行結果 2021-03-11 17:13:39
print(type(dateutil.parser.parse(time)))
# ⬆実行結果 <class 'datetime.datetime'>
# 下記のような物でもOK
time = '2021.03.11'
time = '2021-03-11 17:13:39.182690'
time = '2021-03-11T17:13:39Z'
time = 'Mar 11 2021 17:13PM'
datetimeオブジェクトから文字型の日時へ変換したい場合
標準的な、年、月、日、時、分、秒、曜日を取得する方法を記します。
曜日は、「0:月曜日、1:火曜日、2:水曜日、3:木曜日、4:金曜日、5:土曜日、6:日曜日」になります。
dt = datetime.datetime(2021, 3, 11, 10, 23, 34)
# 年、月、日、時、分、秒 を取得
print(dt.year) #2021
print(dt.month) #3
print(dt.day) #11
print(dt.hour) #10
print(dt.minute) #23
print(dt.second) #34
# 曜日を取得
print(dt.weekday()) #3 ➡︎ 木曜日
書式コードでの表現は、datetimeオブジェクトのstrftimeメソッドを使用します。
書式コードは、既に記した表と同じです。
dt = datetime.datetime.now()
print(dt.strftime('%Y-%m-%d %H:%M:%S'))
# ⬆実行結果 '2021-03-14 09:47:13'
ロケール(国や地域)関係を扱いたい場合
今まで見てきたdatetimeオブジェクトは、タイムゾーンが指定されていないタイプ(naiveオブジェクトと呼ばれる)であり、タイムゾーンを別途、指定する事ができます。(awareオブジェクトと呼ばれる)
タイムゾーンを指定する方法を記します。
# ケース1
dt_aware = datetime.datetime(2021, 3, 15, 3, 15, 8, 2000,
tzinfo=datetime.timezone.utc)
# ケース2 now()の場合、タイムゾーンオブジェクトを引数に入力
dt_aware = datetime.datetime.now(tzinfo)
上記コードの引数(tzinfo)は、timezoneオブジェクトを入力します。timezoneオブジェクトの主な作り方は以下の通りです。
# 標準時間(UTC)の場合
tzinfo = datetime.timezone.utc
# 標準時間から時差のあるタイムゾーンを使用する場合 (第2引数の、nameは任意)
# 下記は、日本の例です。(標準時間から、+9h差)
tzinfo = datetime.timezone(datetime.timedelta(hours=9), name='JST')
既にインスタンス化しているdatetimeオブジェクトを、同時刻のまま、タイムゾーンを変更するコードは以下の通りです。(標準時間でのdatetimeオブジェクトを、日本時間にしたい場合を例にします。標準時間から、+9hになっています。)
# datetimeオブジェクトをインスタンス化
dt_aware = datetime.datetime(2021, 3, 15, 3, 15, 8, 2000,
tzinfo=datetime.timezone.utc)
print(dt_aware)
# ⬆実行結果 2021-03-15 03:15:08.002000+00:00
# 同時刻で、timezoneを変更
dt_aware_jpy = dt_aware.astimezone(datetime.timezone(
datetime.timedelta(hours=9)))
print(dt_aware_jpy)
# ⬆実行結果 2021-03-15 12:15:08.002000+09:00
以下は、日時の値は同じままで、timezoneのみを変更するコードです。
# datetimeオブジェクトをインスタンス化
dt_aware = datetime.datetime(2021, 3, 15, 3, 15, 8, 2000,
tzinfo=datetime.timezone.utc)
print(dt_aware)
# ⬆実行結果 2021-03-15 03:15:08.002000+00:00
# 同じ値のまま、timezoneを変更
dt_aware_jpy = dt_aware.replace(tzinfo=datetime.timezone(
datetime.timedelta(hours=9)))
print(dt_aware_jpy)
# ⬆実行結果 2021-03-15 03:15:08.002000+09:00
naiveオブジェクト(timezone無し) ⇔ awareオブジェクト(timezone有り)の変換
# naive ⇨ aware (標準時間に設定したい場合)
dt_naive_to_utc = dt_naive.replace(tzinfo=datetime.timezone.utc)
# aware ⇨ naive (timezone情報を削除したい場合)
dt_aware_to_naive = dt_aware.replace(tzinfo=None)
Numpyで日時を扱いたい場合
numpyの場合、numpy.datetime64オブジェクトを使用します。日時の差は、numpy.timedelta64オブジェクトです。
import datetime
import numpy as np
import pandas as pd
# 文字列からのインスタンス(下記、どれでも可能)
dt64 = numpy.datetime64('2020-03')
dt64 = numpy.datetime64('2020-03-25')
dt64 = np.datetime64('2020-03-25 03:30:24')
dt64 = np.datetime64('2020-03-25T03:30')
# arrayで使う場合、下記のような使い方をします。
array = np.array([dt64, ・・・・・])
# Datetimeからの変換
dt = datetime.datetime.now()
dt64 = np.datetime64(dt)
# Datetimeへの変換
dt = dt64.astype(datetime.datetime)
基本的に、次に示すPandasで日時を処理するケースが多いですが、numpyのみで処理したい場合、上記を使用します。numpyのみで処理すると、pythonの弱点である、処理速度の遅さが克服できます。
Pandasで日時を扱いたい場合
Pandasの場合、datetimeクラスを継承したTimestampオブジェクトを使用します。
ここで混乱するのが、DataFrameの場合、列(つまり、Series)のdtypeはdatetime64[ns]型であり、各要素がTimestamp型になる所です。具体例を下記に記します。
# 下記値がdfに代入されているとします。
print(df)
# ⬆実行結果
# time count
# 0 2017-01-01 10:00:00 1
# 1 2017-01-01 11:00:00 2
# 2 2017-01-01 12:00:00 3
# 3 2017-01-01 13:00:00 4
# 4 2017-01-01 14:00:00 5
# 列(Series)のdtype
print(df['time'].dtype)
# ⬆実行結果 datetime64[ns]
# 各要素の型
print(type(df['time'][0]))
# ⬆実行結果 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
インスタンス化には、pandas.to_datetime()を使用します。このメソッド、pandas独自の日時処理ができない場合、サードパーティー製のdateutilモジュールを使用するので、ほとんどの物が変換できます。
import datetime
import numpy as np
import pandas as pd
# 下記は全部OK
pd_time = pd.to_datetime('2020-03')
pd_time = pd.to_datetime('2020-03-25')
pd_time = pd.to_datetime('2020-03-25 03:30:24')
pd_time = pd.to_datetime('2020-03-25T03:30')
# datetime, numpy.datetime64型でもOK
pd_time = pd.to_datetime(datetime.datetime.now())
pd_time = pd.to_datetime(np.datetime64('2020-03-25'))
# 日時フォーマットの指定も可能
pd_time = pd.to_datetime('2020-03-25', format='%Y-%m-%d')
引数にリストみたいな物を入れると、DatetimeIndex型が返ってきます。これは、そのままDataFrameに代入できます。
df_time = pd.to_datetime(['2020-03-12','2020-03-13'])
print(type(df_time))
# ⬆実行結果 <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
df = pd.DataFrame()
df['time'] = df_time
print(df)
# ⬆実行結果
# time
# 0 2020-03-12
# 1 2020-03-13
DatetimeIndexオブジェクトで、連続した要素を取得したい場合、date_range()が使用できます。
import pandas as pd
# 引数に、開始日時、終了日時、間隔を記します。
dates = pd.date_range(start='3/1/2021', end='3/3/2021', freq='D')
print(dates)
# ⬆実行結果 DatetimeIndex(['2021-03-01', '2021-03-02', '2021-03-03'],
dtype='datetime64[ns]', freq='D')
Timestampオブジェクトから、datetimeオブジェクト、numpy.datetime64オブジェクトへの変換は以下の通りです。
# datetime型への変換
df['time'][0].to_pydatetime()
# numpy.datetime64型への変換
df['time'][0].to_datetime64()
加算等は、timedeltaオブジェクトが使えます。
pd_time = pd.to_datetime('2020-03')
print(pd_time + datetime.timedelta(days=1))
# ⬆実行結果 2020-03-02 00:00:00
タイムゾーンの指定は、tz_localize()メソッドを使用し、また同時刻のままタイムゾーンを変換した場合は、tz_convert()メソッドを使用します。下記はTimestamp型の事例ですが、DatetimeIndex型にも使用できます。
pd_time = pd.to_datetime('2020-03')
# タイムゾーンの設定
pd_tz = pd_time.tz_localize('GMT') # 標準時間に設定
pd_japan_tz = pd_time.tz_localize('Asia/Tokyo') # 日本時間に設定
# 同時刻のまま、タイムゾーンを変更
pd_tz.tz_convert('Europe/Paris')
タイムゾーンの削除は以下の通り、引数にNoneを指定します。
pd_tz.tz_localize(None)
最後に
本記事を書く前に、こんなに長くなるとは思っていませんでした。
それだけ日時関係の処理が多岐に渡っていて、なかなか覚えられない筈だと妙に納得してしまいました。まだまだたくさんの使い方がありますが、本記事の内容だけで、ほとんどの場面で対応できると思います。
参考になれば、幸いです。