Pythonスキルの習得

Python 日時 処理の棚卸し・まとめ

アイキャッチ画像

プログラムで日時処理関係のコードを書いていると

  • 期待した表現で日時処理を出力したいけど、どうやるんだっけ?
  • タイムゾーン、めったに使わないから、いざ使うとなるとどうやるんだっけ?
  • 別のオブジェクトに変換したいけど、やり方を忘れちゃった。

といった事態にならないでしょうか?

しかもネットで探し回っても、なかなか見つからない事が多いように思えます。

そこで本記事だけで、ほとんどのケースが網羅できるよう、まとめてみました。

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)

最後に

本記事を書く前に、こんなに長くなるとは思っていませんでした。

それだけ日時関係の処理が多岐に渡っていて、なかなか覚えられない筈だと妙に納得してしまいました。まだまだたくさんの使い方がありますが、本記事の内容だけで、ほとんどの場面で対応できると思います。

参考になれば、幸いです。