web-dev-qa-db-ja.com

Python年単位のタイムデルタ

ある日から何年か経っているかどうかを確認する必要があります。現在、timedeltaモジュールからdatetimeを取得していますが、それを年に変換する方法がわかりません。

120
Migol

何年経ったかを知るには、timedelta以上が必要です。また、開始(または終了)日付を知る必要があります。 (うるう年のことです。)

最善の策はdateutil.relativedeltaobject を使用することですが、それはサードパーティのモジュールです。ある日付から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
145
Rick Copeland

誰かが18歳かどうかを確認しようとしている場合、うるう年のためtimedeltaを使用しても一部のEdgeケースでは正しく機能しません。たとえば、2000年1月1日に生まれた人は、2018年1月1日(5うるう年を含む)に正確に6575日後に18歳になりますが、2001年1月1日に生まれた人は、1月1日にちょうど6574日後に18歳になります2019(うるう年を含む)。したがって、誰かが正確に6574日齢である場合、生年月日に関するもう少しの情報がなければ17歳か18歳かを判断することはできません。

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

45
Adam Rosenfield

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

そうは言っても、「自然」な単位(おそらく秒)の差を求め、その単位と年の比率で割ります。例えば。

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

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

9
MarkusQ

次に、更新された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)))
6
antihero

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

3
brianary

これは私が解決した解決策です、私は助けることができると思います;-)

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
1
pvilas

ここで言及していない別のサードパーティライブラリは、このタスクに使用できる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インスタンスであることが期待されています。

通常のdatetimeDateTimeに変換するには、これを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)

そして、はい、この単一のタスクに依存関係を追加することは、timeutil(Rick Copelandが推奨)を使用する場合と比較しても間違いなく過剰です。

1

最後にあなたが持っているのは数学の問題です。 4年ごとに余分な日がある場合、365ではなく365 * 4 + 1でタイムデルタを日単位で割ります。これにより、4年分が得られます。次に、4で再び割ります。timedelta/((365 * 4)+1)/ 4 = timedelta * 4 /(365 * 4 +1)

1
Norberto
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
1
John Mee

Pyfdate をお勧めします

pyfdateとは何ですか?

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

チュートリアル

0
twils

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

どのくらい正確に必要ですか?うるう年を心配しているなら、td.days / 365.25を使えばかなり親しくなるでしょう。

0
eduffy