datetime.isocalendar()の逆を見つける最良の方法は何ですか?
質問
Python datetime.isocalendar()
メソッドは、指定された datetime
オブジェクトのタプル(ISO_year、ISO_week_number、ISO_weekday)
を返します。対応する逆関数はありますか?そうでない場合、年、週番号、曜日を指定して日付を計算する簡単な方法はありますか?
解決
最近、この問題を自分で解決する必要があり、この解決策を思いつきました:
import datetime
def iso_year_start(iso_year):
"The gregorian calendar date of the first day of the given ISO year"
fourth_jan = datetime.date(iso_year, 1, 4)
delta = datetime.timedelta(fourth_jan.isoweekday()-1)
return fourth_jan - delta
def iso_to_gregorian(iso_year, iso_week, iso_day):
"Gregorian calendar date for the given ISO year, week and day"
year_start = iso_year_start(iso_year)
return year_start + datetime.timedelta(days=iso_day-1, weeks=iso_week-1)
いくつかのテストケース:
>>> iso = datetime.date(2005, 1, 1).isocalendar()
>>> iso
(2004, 53, 6)
>>> iso_to_gregorian(*iso)
datetime.date(2005, 1, 1)
>>> iso = datetime.date(2010, 1, 4).isocalendar()
>>> iso
(2010, 1, 1)
>>> iso_to_gregorian(*iso)
datetime.date(2010, 1, 4)
>>> iso = datetime.date(2010, 1, 3).isocalendar()
>>> iso
(2009, 53, 7)
>>> iso_to_gregorian(*iso)
datetime.date(2010, 1, 3)
他のヒント
import datetime
def iso_to_gregorian(iso_year, iso_week, iso_day):
"Gregorian calendar date for the given ISO year, week and day"
fourth_jan = datetime.date(iso_year, 1, 4)
_, fourth_jan_week, fourth_jan_day = fourth_jan.isocalendar()
return fourth_jan + datetime.timedelta(days=iso_day-fourth_jan_day, weeks=iso_week-fourth_jan_week)
これは、@ BenJamesの非常に良い回答から引用したものです。年の最初の日を知る必要はありません。確かに同じISO年にある日付の例と、その日付のISOカレンダーの週と日を知る必要があります。
1月4日は、Benが指摘したように、1月4日は常に同じISO年とグレゴリオ暦の年に属し、その年の最初の日であるため、単なる例です。
週はすべて同じ長さなので、必要な日付のISOと両方の形式で知っている日付のISOの間の日数と週数を単に減算し、その日数と週数を加算することができます。 (これらの数値が正か負かは関係ないので、12月28日など、他の「固定日」を選択できます。)
編集
これを修正しました。@ JoSoが有益に指摘したように、グレゴリオ暦年の最初の日はISO年にも属し、1月5日ではなく1月4日です。説明が示すように、基準点としてどの日付が選択されるかは問題ではありませんが、1月4日を選択すると、この選択は「魔法」ではなくなります。
Python 3.6では、新しい%G
、%u
、および%V
ディレクティブを使用できます。 issue 12006 および更新されたドキュメント:
%G
ISO週の大部分を含む年を表す世紀を含むISO 8601年(%V
)。
%u
10進数としてのISO 8601の曜日(1は月曜日)。
%V
月曜日を週の最初の日とする10進数としてのISO 8601週。週01は、1月4日を含む週です。
年、週番号、曜日番号を含む文字列を指定すると、それらを日付に簡単に解析できます。
from datetime import datetime
datetime.strptime('2002 01 1', '%G %V %u').date()
または整数入力の関数として:
from datetime import datetime
def date_from_isoweek(iso_year, iso_weeknumber, iso_weekday):
return datetime.strptime(
'{:04d} {:02d} {:d}'.format(iso_year, iso_weeknumber, iso_weekday),
'%G %V %u').date()
Python 3.6以降、 datetime.strptime()
は%G
、%V
、および%u をサポートしますcode>ディレクティブを使用して、
datetime.strptime( '2015 1 2'、 '%G%V%u')。date()
を実行できます。参照: https://hg.python.org/cpython/rev/acdebfbfbdcf
次に来る人のために、Benの優れたソリューションのより短いシングル定義バージョン:
def iso_to_gregorian(iso_year, iso_week, iso_day):
jan4 = datetime.date(iso_year, 1, 4)
start = jan4 - datetime.timedelta(days=jan4.isoweekday()-1)
return start + datetime.timedelta(weeks=iso_week-1, days=iso_day-1)
%Wは週#(0-53)であり、ISO週(1-53)とは異なることに注意してください。 %Wが機能しないエッジケースがあります。
Ben Jamesが投稿したものに似たソリューションを思いつきましたが、1つの関数を使用しました:
import datetime
def getDateFromWeek(year,week,day):
"""Method to retrieve the date from the specified week, year and weekday"""
year_start = datetime.date(year,1,1)
ys_weekday = year_start.weekday()
delta = (week*7)+(day-ys_weekday)
if ys_weekday<4:
delta -= 7
return year_start + datetime.timedelta(days=delta)
2020年の最後の週や2021年の最初の週などの境界値でテストしましたが、かなりうまくいきました。
編集:これを無視してください。エッジケースは苦痛です。ベンのソリューションを使用してください。
OK、よく調べてみると、 strptime
には%W
と%w
のパラメーターがあるため、次のように動作します:
def fromisocalendar(y,w,d):
return datetime.strptime( "%04dW%02d-%d"%(y,w-1,d), "%YW%W-%w")
いくつかの落とし穴:ISO週番号は 1
から始まり、%W
は 0
から始まります。 ISOの曜日は 1
(月曜日)から始まり、これは%w
と同じです。したがって、日曜日はおそらく 0
である必要があります。 7
...