Python timedelta в годах



мне нужно проверить, было ли какое-то количество лет с какой-то даты. В настоящее время у меня есть timedelta с datetime модуль, и я не знаю, как преобразовать его в лет.

575   14  

14 ответов:

вам нужно больше, чем timedelta чтобы сказать, сколько лет прошло; вы также должны знать дату начала (или окончания). (Это високосный год.)

лучше всего использовать 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

Если вы пытаетесь проверить, если кто-то 18 лет, используя timedelta не будет работать правильно на некоторых случаях края из-за високосных лет. Например, кому-то, родившемуся 1 января 2000 года, исполнится 18 ровно 6575 дней спустя 1 января 2018 года (5 високосных лет включительно), а кому-то, родившемуся 1 января 2001 года, исполнится 18 ровно 6574 дня позже 1 января 2019 года (4 високосных года включительно). Таким образом, вы, если кому-то ровно 6574 дня, вы не можете определить, являются ли они 17 или 18, не зная немного больше информации о дате их рождения.

правильный способ сделать это-вычислить возраст непосредственно из дат, вычитая два года, а затем вычитая один, если текущий месяц/день предшествует месяцу/дню рождения.

во-первых, на самом детальном уровне проблема не может быть решена точно. Годы различаются по длине, и нет четкого "правильного выбора" для длины года.

тем не менее, получите разницу в любых единицах "естественного" (вероятно, секунд) и разделите на соотношение между этим и годами. Е. Г.

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 (средний Григорианский месяц) в течение нескольких месяцев.

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

еще одна сторонняя lib, не упомянутая здесь, - mxDateTime (предшественник обоих python datetime и 3-й партии 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)

и да, добавление зависимости для этой единственной задачи, о которой идет речь, определенно было бы излишним по сравнению даже с использованием timeutil (предложенного Риком Коуплендом).

В конце концов, что у вас есть-это вопрос математики. Если каждые 4 года у нас есть дополнительный день позволяет затем нырнул timedelta в днях, а не на 365, но 365*4 + 1 это даст вам сумму в 4 года. Затем разделить еще на 4. timedelta / ((365*4) +1) / 4 = timedelta * 4 / (365*4 +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

насколько точно вам это нужно? td.days / 365.25 будет вам довольно близко, если вы беспокоитесь о високосных годах.

хотя этот поток уже мертв, могу ли я предложить рабочее решение для этой самой проблемы, с которой я столкнулся. Вот она (дата-строка в формате ДД-ММ-гггг):

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

эта функция возвращает разницу в годах между двумя датами (взятыми в виде строк в формате 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

Я предлагаю Pyfdate

что такое pyfdate?

учитывая цель Python, чтобы быть мощным и простым в использовании сценариев язык, его особенности для работы с датами и временем все не так удобный, как они должны быть. Этот цель pyfdate состоит в том, чтобы исправить это ситуации, предоставляя возможности для работа с датами и временем, которые как мощный и легкий в использовании как остальное Питон.

the учебник

ну, вопрос кажется довольно легко. Вам нужно проверить количество "полных" лет, и только если оно равно 18, вам нужно беспокоиться о месяцах и днях. Крайний случай:endDate.year - startDate.year == 18 и он разбивается на два случая:startDate.month != endDate.month и startDate.month == endDate.month, когда вы просто должны проверить дней:

 def isOfAge(birthDate, age=18):
     endDate = date.today()
     years = endDate.year - birthDate.year
     if years == age:
         return (birthDate.month < endDate.month or 
                  (birthDate.month == endDate.month and birthDate.day < endDate.day))         
     return years > age

Это все еще больше, чем один лайнер-лямбда, но он все еще довольно короткий и кажется быстрым в исполнении.

Comments

    Ничего не найдено.