Подтвердить что ты не робот

Pythonic разница между двумя датами в годах?

Есть ли более эффективный способ сделать это ниже? Я хочу иметь разницу в годах между двумя датами как единый скаляр. Любые предложения приветствуются.

from datetime import datetime
start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference  = end_date - start_date
difference_in_years = (difference.days + difference.seconds/86400)/365.2425
4b9b3361

Ответ 1

Если вам нужны точные результаты, я рекомендую использовать библиотеку dateutil.

from dateutil.relativedelta import relativedelta
difference_in_years = relativedelta(end_date, start_date).years

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

Ответ 2

Я использую один из них для вычисления возраста человека:

import datetime
dob = datetime.date(1980, 10, 10)

def age():
    today = datetime.date.today()
    years = today.year - dob.year
    if today.month < dob.month or (today.month == dob.month and today.day < dob.day):
        years -= 1
    return years

def age2():
    today = datetime.date.today()
    this_year_birthday = datetime.date(today.year, dob.month, dob.day)
    if this_year_birthday < today:
        years = today.year - dob.year
    else:
        years = today.year - dob.year - 1
    return years

Ответ 3

Более эффективен? Нет, но правильнее, наверное. Но это зависит от того, насколько вы правы. Даты не тривиальные вещи.

Годы не имеют постоянной длины. Вы хотите разницу в високосные годы или нормальные годы?:-) Как вы рассчитываете, вы всегда получите немного неправильный ответ. И как долго проходит через год? Вы говорите 1/365.2425. Ну, да, усредненный за тысячу лет, да. Но в противном случае.

Таким образом, вопрос не имеет особого смысла.

Чтобы быть правдивым, вы должны сделать это:

from datetime import datetime
from calendar import isleap
start_date = datetime(2005,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
diffyears = end_date.year - start_date.year
difference  = end_date - start_date.replace(end_date.year)
days_in_year = isleap(end_date.year) and 366 or 365
difference_in_years = diffyears + (difference.days + difference.seconds/86400.0)/days_in_year

В этом случае разница составляет 0,0012322917425568528 лет, или 0,662 дня, учитывая, что это не високосный год.

(а затем мы игнорируем микросекунды. Хех.)

Ответ 4

Просто сделайте следующее:

from dateutil.relativedelta import relativedelta

myBirthday = datetime.datetime(1983,5,20,0,0,0,0)
now = datetime.datetime.now()



difference = relativedelta(now, myBirthday)
print("My years: "+str(difference.years))

Ответ 5

Чтобы понять смысл високосных лет, вы почти вынуждены разбить его на две части: целое число лет и дробную часть. Оба должны иметь дело с високосными годами, но по-разному - интеграл должен иметь дело со стартовой датой 29 февраля, а фракция должна иметь дело с разным количеством дней в году. Вы хотите, чтобы дробная часть увеличивалась в равных количествах до тех пор, пока она не станет равна 1.0 на следующую юбилейную дату, поэтому она должна основываться на количестве дней в году после даты окончания.

Вы хотите, чтобы ваш диапазон дат включал 1900 или 2100? Все становится немного легче, если вы этого не сделаете.


Изменить: Мне понадобилось много времени, чтобы прояснить это. Основная проблема заключается в том, что календарные годы не являются постоянным размером, но вы вынуждаете их быть постоянными, устанавливая их в 1.0. Из-за этого любое решение, которое вы придумали, будет иметь аномалии, и вам придется выбирать, с какими аномалиями вы можете жить. Джон Мачин был прав.

Какая разница между 2008-02-28 и 2009-02-28? Большинство людей согласятся, что это должно быть ровно 1,0 года. Как насчет разницы между 2008-03-01 и 2009-03-01? Опять же, большинство людей согласятся, что это должно быть ровно 1,0 года. Если вы решите представить дату как год плюс часть года в зависимости от дня, невозможно сделать оба этих утверждения истинными. Это относится к исходному коду, который предположил, что день был 1/365.2425 года, или даже для любого кода, который принимает постоянную долю года в день, даже если размер дня составляет годы, которые являются прыжками лет.

Мое утверждение, что вам нужно было разбить это на целые годы и дробные годы, было попыткой обойти эту проблему. Если вы рассматриваете каждое из предыдущих условий как целостный год, все, что вам нужно сделать, это решить, какую долю назначить на любое количество оставшихся дней. Проблема с этой схемой заключается в том, что вы все еще не можете понять (date2-date1) + date3, потому что фракция не может быть разрешена обратно в день с любой согласованностью.

Таким образом, я предлагаю еще одну кодировку, основанную на каждом году, содержащем 366 дней, будь то високосный год или нет. Аномалии, во-первых, будут заключаться в том, что с 29 февраля не может быть даты, которая составляет ровно год (или 2 или 3). "Извините Джонни, вы не получили день рождения в этом году, нет 29 февраля" Всегда приемлемо. Во-вторых, если вы попытаетесь принудить такой номер вернуться к дате, вам придется учитывать не-високосные годы и проверить специальный случай 29 февраля и преобразовать его, возможно, в 1 марта.

from datetime import datetime
from datetime import timedelta
from calendar import isleap

size_of_day = 1. / 366.
size_of_second = size_of_day / (24. * 60. * 60.)

def date_as_float(dt):
    days_from_jan1 = dt - datetime(dt.year, 1, 1)
    if not isleap(dt.year) and days_from_jan1.days >= 31+28:
        days_from_jan1 += timedelta(1)
    return dt.year + days_from_jan1.days * size_of_day + days_from_jan1.seconds * size_of_second

start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference_in_years = date_as_float(end_time) - date_as_float(start_time)

Я не предполагаю, что это решение, потому что я не думаю, что это идеальное решение. Но у него есть некоторые желательные свойства:

  • Разница между любыми датами с тем же месяцем и днем ​​и временем будет точным числом лет.
  • Добавление разницы к другой дате приведет к значению, которое может быть преобразовано обратно в полезную дату.

Ответ 6

Здесь выделяется то, что Костянтин опубликовал в своей функции "age2". Он немного короче/чище и использует традиционное/разговорное значение "возраста" или разницы в годах:

def ageInYears( d ):
    today = datetime.date.today()
    currentYrAnniversary = datetime.date( today.year, d.month, d.day )
    return (today.year - d.year) - (1 if today < currentYrAnniversary else 0)

Ответ 7

Я думаю, что вы ищете:

difference_in_years = difference.dt.days / 365.25

Ответ 8

Если вы имеете в виду эффективные с точки зрения кодового пространства, то нет, это самый эффективный способ сделать это.

Ответ 9

Вот что я придумал, не используя внешнюю зависимость:

def year_diff(d1, d2):
    """Returns the number of years between the dates as a positive integer."""
    later = max(d1, d2)
    earlier = min(d1, d2)

    result = later.year - earlier.year
    if later.month < earlier.month or (later.month == earlier.month and later.day < earlier.day):
        result -= 1

    return result

Ответ 10

Более надежная функция - вычисляет разницу в годах (возрасте) и днях:

def get_diff_in_years_and_days(from_date, to_date):
    try:
        from_in_this_year = date(to_date.year, from_date.month, from_date.day)
    except:
        from_in_this_year = date(to_date.year, from_date.month, from_date.day-1) # today is feb in leap year

    if from_in_this_year <= to_date:
        years = to_date.year - from_date.year
        days = (to_date - from_in_this_year).days
    else:
        years = to_date.year - from_date.year - 1
        try:
            from_in_prev_year = date(to_date.year-1, from_date.month, from_date.day)
        except:
            from_in_prev_year = date(to_date.year-1, from_date.month, from_date.day-1) # today is feb in leap year
        days = (to_date - from_in_prev_year).days

    assert days>=0 and days<=365, days
    assert years>=0, years

    return years, days

некоторые модульные тесты:

self.assertEqual((0,  0), get_diff_in_years_and_days(date(2018,1, 1), date(2018,1, 1)))
self.assertEqual((1,  0), get_diff_in_years_and_days(date(2017,1, 1), date(2018,1, 1)))
self.assertEqual((1,  1), get_diff_in_years_and_days(date(2017,1, 1), date(2018,1, 2)))
self.assertEqual((2,  0), get_diff_in_years_and_days(date(2016,2,29), date(2018,2,28)))
self.assertEqual((2,  1), get_diff_in_years_and_days(date(2014,2,28), date(2016,2,29)))
self.assertEqual((1,364), get_diff_in_years_and_days(date(2014,2,28), date(2016, 2,27)))
self.assertEqual((3,30) , get_diff_in_years_and_days(date(2015,10,1), date(2018,10,31)))
self.assertEqual((10,30), get_diff_in_years_and_days(date(2010,10,1), date(2020,10,31)))
self.assertEqual((3,31) , get_diff_in_years_and_days(date(2015,10,1), date(2018,11, 1)))
self.assertEqual((2,364), get_diff_in_years_and_days(date(2015,10,1), date(2018, 9,30)))

Ответ 11

Так как мы приближаемся к концу 2018 года...

from dateutil import parser
from dateutil.relativedelta import relativedelta

rip = [
    ["Tim Bergling\t\t",         " 8 Sep 1989", "20 Apr 2018"], # Avicii Swedish musician
    ["Stephen Hillenburg\t",     "21 Aug 1961", "26 Nov 2018"], # Creator of Spongebob
    ["Stephen Hawking\t\t",      " 8 Jan 1942", "14 Mar 2018"], # Theoretical physicist
    ["Stan Lee\t\t",             "28 Dec 1922", "12 Nov 2018"], # American comic book writer
    ["Stefán Karl Stefánsson\t", "10 Jul 1975", "21 Aug 2018"]  # Robbie Rotten from LazyTown
    ]

for name,born,died in rip:
    print("%s %s\t %s\t died at %i"%(name,born,died,relativedelta(parser.parse(died),parser.parse(born)).years))

выход

Tim Bergling              8 Sep 1989     20 Apr 2018     died at 28
Stephen Hillenburg       21 Aug 1961     26 Nov 2018     died at 57
Stephen Hawking           8 Jan 1942     14 Mar 2018     died at 76
Stan Lee                 28 Dec 1922     12 Nov 2018     died at 95
Stefán Karl Stefánsson   10 Jul 1975     21 Aug 2018     died at 43

Ответ 12

Перед установкой библиотеки:

choco upgrade Python -y
python pip install python-dateutil

Одна строка в Python в Cmder (windows):

python -c "import datetime; from dateutil.relativedelta import relativedelta; myBirthday = datetime.datetime(2019,2,6,11,0,0,0); now = datetime.datetime.utcnow(); diff = relativedelta(now, myBirthday); print ("'My'+'" "'+'year'+'" "'+':'+'" "'+'%d''" "''and''" "''%d''" "''microseconds'" % (diff.years, diff.microseconds))"

В пакетном escape-проценте с %%:

python -c "import datetime; from dateutil.relativedelta import relativedelta; myBirthday = datetime.datetime(2019,2,6,11,0,0,0); now = datetime.datetime.utcnow(); diff = relativedelta(now, myBirthday); print ("'My'+'" "'+'year'+'" "'+':'+'" "'+'%%d''" "''and''" "''%%d''" "''microseconds'" %% (diff.years, diff.microseconds))"