Python で指定時間前(1日前、1年前など)、指定時間後(1日後、1年後)を求める方法

シェアする

フリーランスエンジニアの収入例を見てみる→ フリーエンジニアの案件なら、レバテックフリーランス

時系列情報を含むデータを扱っていると、ある基準日からのn日前(後)を知りたいというケースがあります。

例えば、月末日付を求めるには、翌月の1日から1日前を遡ればカンタンに求めることができます。

直近3ヶ月を対象としてデータを抽出したい場合、まず先頭となる日付を求める必要があります。

Python で時系列情報を扱うには date や datetime クラスを使うのが一般的ですが、これらのクラス自体には、日付や時間を加算(減算)するためのメソッドは実装されていません。

しかし、Python の datetime モジュールには、日付計算のための仕組みがちゃんと用意されています。

datetime モジュールの timedelta を使う

標準の datetime モジュールとして時系列計算用のクラスが提供されています。

日付はもちろんのこと、時間についても計算することができます。

では、さっそくやってみましょう。

基準日の翌日を求める

まずは、最もオーソドックスなパターンをやってみましょう。

基準日の翌日(1日後)を算出する方法がこちらです。

サンプルコード

from datetime import date,timedelta

# 翌日を求める timedelta
td = timedelta(days=1)
# 基準日を 2020年2月28日とする
d = date(2020,2,28)
# 基準日の翌日を計算して出力 2020-02-29
print(d + td)

出力結果
2020-02-29

2020年の2月29日として計算されましたね。

ちゃんと閏年も計算できてます。

解説

4行目で timedelta オブジェクトを生成しています。

このときのパラメータに days=1 と指定することで、「日付を+1」する計算オブジェクトが生成されます。

8行目で基準日となる datetime オブジェクトに対して計算オブジェクトを加算すると、計算結果の datetime オブジェクトを取得することができます。

timedelta を生成する際に指定できるパラメータは次のようなものがあります。

パラメータ 意味
days 日付に対して加減算する値
seconds 秒に対して加減算する値
microseconds 秒に対して加減算するミリ秒単位の値
minutes 分に対して加減算する値
hours 時間に対して加減算する値
weeks 日付に対して加減算する週単位の値

基準日から遡った日付を取得する

基準日以後の日付の算出する方法を紹介しましたが、基準日を遡ることもできます。

基準日以降の日付を求めるためには、生成した timedelta オブジェクトを加算する必要がありましたが、減算をすることで日付を遡ることができます。

それでは、さっそくやってみましょう。

サンプルコード

from datetime import date,timedelta
import calendar

# 基準日を 2018年7月29日とする
d = date(2018,7,29)
# 基準日の前日を算出
yesterday = d - timedelta(days=1)
# 基準日の20日前を算出
before_20_days = d - timedelta(days=1) * 20
# 基準日の40日前を算出
before_40_days = d - timedelta(days=40)
# 基準日の2週間前を算出
two_weeks_ago = d + timedelta(weeks=-2)

# 結果の検証用にカレンダーを出力
calendar.prmonth(2018,6)
calendar.prmonth(2018,7)

# 遡った日付の計算結果を出力
print('\nBase date is \t%s' % d)
print('A day ago \t%s' % yesterday)
print('20 days ago \t%s' % before_20_days)
print('40 days ago \t%s' % before_40_days)
print('Two weeks ago \t%s\n' % two_weeks_ago)

出力結果
      June 2018
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
      July 2018
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 31
 
Base date is 	2018-07-29
A day ago 	2018-07-28
20 days ago 	2018-07-09
40 days ago 	2018-06-19
Two weeks ago 	2018-07-15

基準日を2018年7月29日として、

  • 1日前
  • 20日前
  • 40日前
  • 2週間前

をそれぞれ算出しています。

今回は検証結果を確認しやいように calendar モジュールを使って 2018年6月と7月のカレンダーを出力しています。

それぞれ正しく出力されていることが確認できましたね。

解説

6行目から13行目で基準日に対して timedelta を減算することで遡った日付を算出しています。

9行目では days=1 の timedelta に対して20をかけることで20日前を算出しています。

11行目では days=40 の timedelta を生成して減算することで40日前を算出しています。

そして13行目に注目して欲しいのですが、

ここでは基準日の変数に対して timedelta を減算でなく加算をしています。

これでは遡りできないように思えるのですが、生成している timedelta のパラメータにマイナス値を与えることで遡りを実現しています。

これをみてわかるように、日付に対しての加算もしくは減算は、日付に対しての計算記号と timedelta のパラメータ符号によって決定されることに注意しましょう。

dateutil モジュールの relativedelta を使う

timedelta を使った日付計算方法をみてきました。

基本的なことをするだけなら timedelta を使えば十分ですが次のようなケースではどうなるか考えてみましょう。

翌月の1日や月末日を取得する

これも timedelta を使えば求めることができます。

色々なアプローチがあるかと思いますが、一例としてこのようなコードで算出してみました。

サンプルコード

from datetime import date,timedelta

# 基準日を設定
d = date(2018,7,28)
td = timedelta(days=1)

next_day = d

# 基準日に対して1日ずつ加算し続ける
while True:
    next_day += td
    # 求めた日付が1日ならループを抜ける
    if next_day.day == 1:
        break

# 算出結果を出力
print('current date \t%s' % d)
print('next 1st date \t%s' % next_day)

出力結果
current date 	2018-07-28
next 1st date 	2018-08-01
7月28日の翌月1日の日付が取得できていることがわかりますね。
ただ、もう少しスマートにできないかな?
ということで dateutil モジュールの登場です。
relarivedelta 関数を使えば実に簡単に同様のことができます。
では、さっそくみてみましょう。
サンプルコード
from dateutil import relativedelta
from datetime import date

# 基準日を設定
d = date(2018,7,28)
# 基準日の翌月1日を求める
n = d + relativedelta.relativedelta(months=1,day=1)

# 算出結果を出力
print('current date \t%s' % d)
print('next 1st date \t%s' % n)

出力結果
current date 2018-07-28
next 1st date 2018-08-01

timedelta での計算結果と同じものが出力されていますね。

同じことがたった1行で実現できてしまいました。すごいですね!

解説

7行目で relativedelta 関数にパラメータを渡しています。

このサンプルでは

  • mohths に 1
  • day に 1

をセットしています。

months に与えた値だけ月が進みます。 dayに1 を与えることで「1日」として解釈されます。

この day に実際の月末日より大きな値を与えると「月末日」として解釈されます。

例えば、このサンプルの7行目のday=1 をday=99 にすると 2018年8月31日として解釈されます。

1年前や1年後の日付を取得する

それでは、記事のタイトルにあるように1年前や1年後の日付を取得してみましょう。

単純にdate オブジェクトの year 属性に 1 を足しても実現できそうですが、せっかくなので relativedelta の実演と解説を兼ねて紹介しておこうと思います。

まず、 relativedelta 関数のパラメータのリストを見てみましょう。

パラメータ 意味
year 年の絶対数値を指定します。
month 月の絶対数値を指定します。
day 日の接待数値を指定します。実際の月末日より大きな値を指定された場合は月末日として解釈されます。
years 年の相対数値を指定します。1 であれば1年後、-1 であれば1年前となります。
months 月の相対数値を指定します。1 であれば1ヶ月後、-1 であれば1ヶ月前となります。

これ以外にも様々なパラメータがありますが、今回のテーマに必要なものに絞ってピックアップしています。

それでは、さっそくやってみましょう。

サンプルコード
from dateutil import relativedelta
from datetime import date

# 基準日を設定
d = date(2020,2,29)
# 基準日の1年後を求める
ny = d + relativedelta.relativedelta(years=1)
# 基準日の1年前を求める
py = d - relativedelta.relativedelta(years=1)

# 基準日の1月後を求める
nm = d + relativedelta.relativedelta(months=1)
# 基準日の1月前を求める
pm = d - relativedelta.relativedelta(months=1)

# 算出結果を出力
print('current date \t%s' % d)
print('next year date \t%s[' % ny)
print('previous year date \t%s' % py)
print('next month date \t%s' % nm)
print('previous month date \t%s' % pm)

出力結果
current date 	2020-02-29
next year date 	2021-02-28
previous year date 	2019-02-28
next month date 	2020-03-29
previous month date 	2020-01-29

基準日付けを閏年の2月29日にしてみました。

それぞれの日付が算出されています。

それでは、ひとつずつみていきましょう。

解説

基準日の1年後はは2月28日になっていますね。

1年後も同様に2月28日となっています。

これは1年前(後)に移動した際に、そのままだと day が29のままになるので、実際の月末日よりも大きい値となります。

実際の月末日よりも大きな day の値は月末日として解釈されるので28日となっています。

前月は1月29日、次月は3月29日とこちらは、両方とも29日がそのままセットされています。

これは移動先の日数が元の日よりも小さいため、そのままの値が解釈されています。

つまり、 relativedelta で年や月の計算をしたときは、移動先の月日数の影響を受けることがある点に注意しましょう。

例えば、2月末の1ヶ月後を3月末としたい場合は、 relativedelta のパラメーターに day=99 を指定しておけば末日付として解釈させることができます。

まとめ

Python で日付を計算する方法を見てきました。

最後にポイントを挙げておきます。

  • date オブジェクトに対して計算をするには、datetime モジュールの timedelta オブジェクトを生成して加算(減算)する。
  • timedelta オブジェクトに対して数値を掛け合わせることで移動幅を調整できる。
  • timedelta オブジェクトに負数のパタメータを与えることで逆向きに移動させることができる。
  • 月初日・月末日を求めるには、 timedelta を使うよりも、 reletivedelta を使うと便利。
  • 月単位、年単位の移動には、dateutil モジュールの relativedelta を使うと便利。

一度覚えておくと色々なケースで使い回しが効くので、ぜひ抑えておきましょう。

シェアする

フォローする