ある日付から何年か経過しているかどうかを確認する必要があります。現在、モジュールtimedelta
から取得しdatetime
ていますが、それを年に変換する方法がわかりません。
21 に答える
timedelta
何年が経過したかを知るには、a 以上のものが必要です。また、開始日 (または終了日) も知っておく必要があります。(うるう年のことです。)
あなたの最善の策はdateutil.relativedelta
objectを使用することですが、それはサードパーティのモジュールです。datetime
ある日付から何年経ったかを知りたい場合n
(デフォルトでは現在)、次のことができます:
from dateutil.relativedelta import relativedelta
def yearsago(years, from_date=None):
if from_date is None:
from_date = datetime.now()
return from_date - relativedelta(years=years)
標準ライブラリに固執したい場合、答えはもう少し複雑です::
from datetime import datetime
def yearsago(years, from_date=None):
if from_date is None:
from_date = datetime.now()
try:
return from_date.replace(year=from_date.year - years)
except ValueError:
# Must be 2/29!
assert from_date.month == 2 and from_date.day == 29 # can be removed
return from_date.replace(month=2, day=28,
year=from_date.year-years)
2/29 で、18 年前に 2/29 がなかった場合、この関数は 2/28 を返します。3/1 を返したい場合は、最後のreturn
ステートメントを次のように変更してください::
return from_date.replace(month=3, day=1,
year=from_date.year-years)
あなたの質問はもともと、ある日付から何年経ったか知りたいと言っていました。整数の年数が必要だと仮定すると、1 年あたり 365.2425 日に基づいて推測し、yearsago
上記で定義した関数のいずれかを使用して確認できます。
def num_years(begin, end=None):
if end is None:
end = datetime.now()
num_years = int((end - begin).days / 365.2425)
if begin > yearsago(num_years, end):
return num_years - 1
else:
return num_years
誰かが 18 歳かどうかを確認しようとしているtimedelta
場合、閏年が原因で一部のエッジ ケースでは を使用すると正しく機能しません。たとえば、2000 年 1 月 1 日に生まれた人は、2018 年 1 月 1 日からちょうど 6575 日後の 18 歳になりますが (閏年 5 年を含む)、2001 年 1 月 1 日に生まれた人は、ちょうど 6574 日後の 1 月 1 日に 18 歳になります。 2019 年 (うるう年 4 年を含む)。したがって、誰かが正確に 6574 日である場合、生年月日に関する情報をもう少し知らなければ、その人が 17 歳なのか 18 歳なのかを判断することはできません。
これを行う正しい方法は、日付から直接年齢を計算し、2 年を引き、現在の月/日が誕生月/日より前の場合は 1 を引くことです。
まず、最も詳細なレベルでは、問題を正確に解決することはできません。年の長さはさまざまであり、年の長さの明確な「正しい選択」はありません。
そうは言っても、「自然な」単位(おそらく秒)で差を取り、それと年の比率で割ります。例えば
delta_in_days / (365.25)
delta_in_seconds / (365.25*24*60*60)
...または何でも。月は年よりも明確に定義されていないため、月は避けてください。
これは更新された DOB 関数で、人間と同じ方法で誕生日を計算します。
import datetime
import locale
# Source: https://en.wikipedia.org/wiki/February_29
PRE = [
'US',
'TW',
]
POST = [
'GB',
'HK',
]
def get_country():
code, _ = locale.getlocale()
try:
return code.split('_')[1]
except IndexError:
raise Exception('Country cannot be ascertained from locale.')
def get_leap_birthday(year):
country = get_country()
if country in PRE:
return datetime.date(year, 2, 28)
elif country in POST:
return datetime.date(year, 3, 1)
else:
raise Exception('It is unknown whether your country treats leap year '
+ 'birthdays as being on the 28th of February or '
+ 'the 1st of March. Please consult your country\'s '
+ 'legal code for in order to ascertain an answer.')
def age(dob):
today = datetime.date.today()
years = today.year - dob.year
try:
birthday = datetime.date(today.year, dob.month, dob.day)
except ValueError as e:
if dob.month == 2 and dob.day == 29:
birthday = get_leap_birthday(today.year)
else:
raise e
if today < birthday:
years -= 1
return years
print(age(datetime.date(1988, 2, 29)))
日数を取得し、年数を 365.2425 (グレゴリオ暦の平均年) で割ります。月は 30.436875 (平均グレゴリオ月) で割ります。
どのくらい正確である必要がありますか? td.days / 365.25
うるう年が心配な場合は、かなり近づくことができます。
def age(dob):
import datetime
today = datetime.date.today()
if today.month < dob.month or \
(today.month == dob.month and today.day < dob.day):
return today.year - dob.year - 1
else:
return today.year - dob.year
>>> import datetime
>>> datetime.date.today()
datetime.date(2009, 12, 1)
>>> age(datetime.date(2008, 11, 30))
1
>>> age(datetime.date(2008, 12, 1))
1
>>> age(datetime.date(2008, 12, 2))
0
ここで言及されていないさらに別のサードパーティ ライブラリは、mxDateTime (pythondatetime
とサードパーティの両方の前身timeutil
) であり、このタスクに使用できます。
前述のものは次のyearsago
ようになります。
from mx.DateTime import now, RelativeDateTime
def years_ago(years, from_date=None):
if from_date == None:
from_date = now()
return from_date-RelativeDateTime(years=years)
最初のパラメーターはDateTime
インスタンスである必要があります。
datetime
通常を変換するDateTime
には、これを 1 秒の精度で使用できます):
def DT_from_dt_s(t):
return DT.DateTimeFromTicks(time.mktime(t.timetuple()))
またはこれは1マイクロ秒の精度です:
def DT_from_dt_u(t):
return DT.DateTime(t.year, t.month, t.day, t.hour,
t.minute, t.second + t.microsecond * 1e-6)
そして、はい、問題のこの 1 つのタスクに依存関係を追加することは、timeutil を使用する場合と比べても間違いなくやり過ぎです (Rick Copeland が提案)。
最後に、あなたが持っているのは数学の問題です。4 年ごとに 1 日余分にある場合、timedelta を 365 日ではなく 365*4 + 1 日で割ると、4 年になります。次に、再度 4 で割ります。 timedelta / ((365*4) +1) / 4 = timedelta * 4 / (365*4 +1)
このメソッドは、datetime ライブラリのハックです。
def calculate_age(birthday_date, fmt="%Y-%m-%d"):
birthday = datetime.datetime.strptime(birthday_date, fmt)
age = datetime.datetime.now() - birthday
# first datime valid is (1, 1, 1), I use (1, 12, 31) => (2, 0, 0) to hack the lib
age = (datetime.datetime(1, 12, 31) + age)
return age.year - 2
この解決策は奇妙なので、共有しました。これはおそらく最も洗練された関数ではありません。
now : 2019-09-21
print(calculate_age("2019-09-21")) => 2 (Done)
-> age + (1,12,31) = 0004-01-01 23:42:17.767031
print(calculate_age("2020-09-21")) => 0 (Undone)
-> age + (1,12,31) = 0002-12-31 23:46:39.144091
その差は1日。違いの理由は bisextile (閏年) によるものだと理解しています。
この望ましくない動作を修正するには、誕生日の年を使用して結果を追加する必要があります。
このスレッドはすでに死んでいますが、私が直面していたのとまったく同じ問題に対する有効な解決策を提案できますか? これは次のとおりです (日付は dd-mm-yyyy 形式の文字列です)。
def validatedate(date):
parts = date.strip().split('-')
if len(parts) == 3 and False not in [x.isdigit() for x in parts]:
birth = datetime.date(int(parts[2]), int(parts[1]), int(parts[0]))
today = datetime.date.today()
b = (birth.year * 10000) + (birth.month * 100) + (birth.day)
t = (today.year * 10000) + (today.month * 100) + (today.day)
if (t - 18 * 10000) >= b:
return True
return False
この関数は、2 つの日付の年の差を返します (ISO 形式の文字列として取得されますが、任意の形式で取得するように簡単に変更できます)
import time
def years(earlydateiso, laterdateiso):
"""difference in years between two dates in ISO format"""
ed = time.strptime(earlydateiso, "%Y-%m-%d")
ld = time.strptime(laterdateiso, "%Y-%m-%d")
#switch dates if needed
if ld < ed:
ld, ed = ed, ld
res = ld[0] - ed [0]
if res > 0:
if ld[1]< ed[1]:
res -= 1
elif ld[1] == ed[1]:
if ld[2]< ed[2]:
res -= 1
return res
私の目的のために、2 つのdatetime
オブジェクト (少なくとも Python 3.8 では)の減算は、属性timedelta
のみを持つ に変換されます。days
>>> admit = datetime.strptime('20130530', '%Y%m%d')
>>> birth = datetime.strptime('20010621', '%Y%m%d')
>>> age = (admit - birth).days/365.2425
>>> age
11.940012457476882
パーティーには遅れましたが、これで年齢 (年数) が正確かつ簡単にわかります。
b = birthday
today = datetime.datetime.today()
age = today.year - b.year + (today.month - b.month > 0 or
(today.month == b.month > 0 and
today.day - b.day > 0))