年単位のPython timedelta


回答:


156

あなたはtimedelta何年が経過したかを知るのに1 つ以上必要です。また、開始(または終了)日付も知っておく必要があります。(うるう年です)

あなたの最善の策はdateutil.relativedelta オブジェクトを使用することですが、それはサードパーティのモジュールです。ある日付から数年経過した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)

あなたの質問はもともと、ある日から何年経ったか知りたいと言っていました。整数の年数が必要であると仮定すると、年間365.25日に基づいて推測し、yearsago上記で定義された関数のいずれかを使用して確認できます。

def num_years(begin, end=None):
    if end is None:
        end = datetime.now()
    num_years = int((end - begin).days / 365.25)
    if begin > yearsago(num_years, end):
        return num_years - 1
    else:
        return num_years

26
グレゴリオ暦では400年の例外を考慮に入れて、365.25ではなく365.2425で完全に正確にすることができます。
brianary 2009

3
英国や香港などの国では、うるう年の3月1日まで「切り上げ」されるため、関数は合法的に破られます。
アンチヒーロー


3
これこれも参照してください両方とも、時間について正しくない事柄の優れたリストです。
gvoysey

49

誰かが18歳かどうかを確認しようとしtimedeltaている場合、うるう年のため、一部のエッジケースでは正しく使用できません。たとえば、2000年1月1日に生まれた人は、2018年1月1日に正確に6575日後(5うるう年を含む)になりますが、2001年1月1日に生まれた人は、1月1日に正確に6574日後に18歳になります。 2019(4うるう年を含む)。したがって、誰かが正確に6574日経過している場合、誕生日についての詳細情報を知らないと、17歳か18歳かを判断できません。

これを行う正しい方法は、2つの年を減算し、現在の月/日が誕生月/日の前にある場合は1を減算することにより、日付から直接年齢を計算することです。


9

まず、最も詳細なレベルでは、問題を正確に解決することはできません。年は長さが異なり、年の長さについて明確な「正しい選択」はありません。

とはいえ、「自然な」単位(おそらく数秒)の違いを取得し、その単位と年数の比率で割ります。例えば

delta_in_days / (365.25)
delta_in_seconds / (365.25*24*60*60)

...または何でも。それらは何年よりも明確に定義されていないので、数か月は避けてください。


2
それは何年の勤続期間の問題であるか、または人が特定の年齢に達したかどうかの問題であるとき、誰もが意味または使用することではありません。
John Machin、

3
グレゴリオ暦の400年の例外を考慮すると、365.25は365.2425になります。
brianary 2009

1
まあ、問題正しく解決できます。うるう日とうるう秒がある年を事前に知ることができます。それは、2つのフォーマットされた日付で
Litherum

7

これは、人間と同じように誕生日を計算する更新された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)))

これは、ドブが2月29日で、現在の年がうるう年ではない場合に破られます。
Trey Hunner 2013年

4

日数を取得し、365.2425(平均グレゴリオ年)で何年か割る。数か月間の30.436875(平均グレゴリオ月)で割ります。


2
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

2月29日に生まれた人は、翌2月28日に1歳に達したものとして扱われます。
John Machin、

OK。「今日の誕生日です」から「今日の誕生日です」にテストを反転させることにより、29日に生まれた人口の0.08%に対応するように修正されました。それで解決しましたか?
John Mee、

あなたの例では正しく動作します!?!「今日」を2009年2月28日に設定し、生年月日を2008年2月29日に設定すると、少なくとも私にとってはゼロが返されます。あなたが提案するように1ではありません(Python 2.5.2)。マチン氏を失礼にする必要はありません。正確にどの2つの日付で問題がありますか?
John Mee、

再試行します。2月29日に生まれた人は、ほとんどの法的目的のために、次の2月28日に1歳に達したとして扱われます。コードは、「修正」の前と後の両方で0を生成します。実際、コードの2つのバージョンは、9つすべての入力の可能性(月<==> X日<==>)に対してまったく同じ出力を生成します。ところで、100.0 /(4 * 365 + 1)は0.068ではなく0.068を生成します。
John Machin

2
はぁ。(0)2月29日の問題に対処することは、日付の計算で不可欠です。あなたはそれを無視しました。(1)あなたのコードは初めてより良くなりませんでした。「まったく同じ出力を生成する」で何が分からないのですか?(2)today.monthとdob.monthを比較する3つの可能なアトミック結果(<、==、>)。today.dayとdob.dayを比較する3つの可能なアトミック結果。3×3 == 9(3)stackoverflow.com/questions/2217488/...
ジョン・マチン

1

あなたはそれがどれほど正確である必要がありますか? td.days / 365.25あなたがうるう年を心配しているなら、あなたはあなたをかなり近づけます。


うるう年が本当に心配です。18歳以上かどうかを確認する必要があります。
Migol

次に、簡単なワンライナーはありません。2つの日付を解析して、人物が18歳の誕生日を過ぎているかどうかを判断する必要があります。
eduffy 2009

1

ここで言及されていないさらに別のサードパーティライブラリは、このタスクに使用できるmxDateTime(Python datetimeとサードパーティの両方の前身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)

そして、はい、問題のこの単一のタスクの依存関係を追加することは、(Rick Copelandによって提案された)timeutilを使用する場合と比較しても、明らかにやり過ぎです。


1

結局あなたが持っているのは数学の問題です。4年ごとに1日余分な日がある場合、timedeltaを365日ではなく、365 * 4 + 1で日数で割ります。これにより、4年の量が得られます。次に、4で再度除算します。timedelta/((365 * 4)+1)/ 4 = timedelta * 4 /(365 * 4 +1)


うるう年は、400で割り切れる場合を除いて、年が100で割り切れる場合には適用されません。したがって、2000年の場合:-4で割り切れるので、うるうであるはずですが...-また、割り切れる100であるので、うるう年ではありませんが...-400で割り切れるので、実際にはうるう年でした。1900の場合:-4で割り切れるので、飛躍的なはずです。-100で割り切れるので、跳躍しないようにしてください。-400で割り切れないため、このルールは適用されません。1900年はうるう年ではありませんでした。
Jblasco 2013

1

これは私が解決した解決策です、私が助けてくれることを願っています;-)

def menor_edad_legal(birthday):
    """ returns true if aged<18 in days """ 
    try:

        today = time.localtime()                        

        fa_divuit_anys=date(year=today.tm_year-18, month=today.tm_mon, day=today.tm_mday)

        if birthday>fa_divuit_anys:
            return True
        else:
            return False            

    except Exception, ex_edad:
        logging.error('Error menor de edad: %s' % ex_edad)
        return True

0

このスレッドはすでに死んでいますが、私が直面していたこの同じ問題の実用的な解決策を提案できますか?ここにあります(日付は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

0

この関数は、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

0

Pyfdateを提案します

pyfdateとは何ですか?

強力で使いやすいスクリプト言語になることをPythonの目標にすると、日付と時刻を操作するためのその機能は、本来のユーザーフレンドリーではありません。pyfdateの目的は、Pythonの他の部分と同じように強力で使いやすい日付と時刻を操作する機能を提供することで、この状況を改善することです。

チュートリアル


0
import datetime

def check_if_old_enough(years_needed, old_date):

    limit_date = datetime.date(old_date.year + years_needed,  old_date.month, old_date.day)

    today = datetime.datetime.now().date()

    old_enough = False

    if limit_date <= today:
        old_enough = True

    return old_enough



def test_ages():

    years_needed = 30

    born_date_Logan = datetime.datetime(1988, 3, 5)

    if check_if_old_enough(years_needed, born_date_Logan):
        print("Logan is old enough")
    else:
        print("Logan is not old enough")


    born_date_Jessica = datetime.datetime(1997, 3, 6)

    if check_if_old_enough(years_needed, born_date_Jessica):
        print("Jessica is old enough")
    else:
        print("Jessica is not old enough")


test_ages()

これは、カルーセルオペレーターがローガンのラン映画で実行していたコードです;)

https://en.wikipedia.org/wiki/Logan%27s_Run_(film)


0

私はこの質問に出くわし、Adamsが最も役立つ答えを見つけましたhttps://stackoverflow.com/a/765862/2964689

しかし、彼の方法のpythonの例はありませんでしたが、これが私が最終的に使用したものです。

入力:日時オブジェクト

出力:整数年数

def age(birthday):
    birthday = birthday.date()
    today = date.today()

    years = today.year - birthday.year

    if (today.month < birthday.month or
       (today.month == birthday.month and today.day < birthday.day)):

        years = years - 1

    return years

0

私はJohn Meeのソリューションが単純であることを気に入っていました。うるう年ではない2月28日または3月1日で、2月29日に生まれた人の年齢をどのように決定するかについてはそれほど心配していません。しかし、ここに彼のコードの微調整がありますこれは苦情に対処すると思います:

def age(dob):
    import datetime
    today = datetime.date.today()
    age = today.year - dob.year
    if ( today.month == dob.month == 2 and
         today.day == 28 and dob.day == 29 ):
         pass
    elif today.month < dob.month or \
      (today.month == dob.month and today.day < dob.day):
        age -= 1
    return age
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.