Python timedelta в годах - PullRequest
       40

Python timedelta в годах

115 голосов
/ 19 апреля 2009

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

Ответы [ 14 ]

136 голосов
/ 20 апреля 2009

Вам нужно больше, чем 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
44 голосов
/ 19 апреля 2009

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

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

9 голосов
/ 19 апреля 2009

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

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

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

... или что угодно. Держитесь подальше от месяцев, поскольку они даже менее четко определены, чем годы.

6 голосов
/ 10 августа 2012

Вот обновленная функция 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)))
3 голосов
/ 25 декабря 2009

Получите количество дней, затем разделите на 365,2425 (средний григорианский год) для лет. Разделите на 30,436875 (средний григорианский месяц) по месяцам.

1 голос
/ 10 марта 2011

Это решение, которое я разработал, надеюсь, поможет; -)

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 голос
/ 16 июля 2010

В конце концов, у вас есть математический вопрос. Если каждые 4 года у нас будет дополнительный день, то делим таймделту по дням, не на 365, а на 365 * 4 + 1, что даст вам 4 года. Затем разделите его снова на 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)

1 голос
/ 24 декабря 2009

Еще одна сторонняя библиотека, не упомянутая здесь, - это 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)

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

1 голос
/ 30 ноября 2009
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
0 голосов
/ 01 декабря 2009

Я предложу Pyfdate

Что такое пифдат?

Учитывая стремление Python стать мощным и простым в использовании сценарием язык, его особенности для работы с датами и временем не так дружественный к пользователю, как они должны быть. Цель Pyfdate состоит в том, чтобы исправить ситуация, предоставляя функции для работа с датами и временем столь же мощный и простой в использовании, как Остальная часть Python.

учебник

...