Тимедельта Пітона в роках


140

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


4
дивіться цю відповідь: stackoverflow.com/a/9754466/65387
Адам

Відповіді:


156

Вам потрібно більше ніж 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

26
Ви можете бути повністю точними з 365.2425 (замість 365.25), який враховує 400-річний виняток для григоріанського календаря.
бріанарія

3
Ваша функція легально порушується для таких країн, як Великобританія та Гонконг, оскільки вони "збираються" до 1 березня протягом високосних років.
antihero


3
дивіться також це і це Обидва - це чудові переліки речей, які не відповідають дійсності часу.
gvoysey

49

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

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


9

По-перше, на найбільш детальному рівні проблему неможливо вирішити точно. Роки відрізняються за довжиною, і немає чіткого "правильного вибору" для тривалості року.

Однак, отримайте різницю в тому, що одиниці є "природними" (ймовірно, секундами) і розділіть на співвідношення між цим і роком. Напр

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

... або що завгодно. Не тримайтеся подалі від місяців, оскільки вони ще менш чітко визначені, ніж роки.


2
Це НЕ, що хтось означає або використовує, коли мова йде про те, скільки років служби чи людина досягла певного віку.
Джон Махін

3
Ваш 365,25 має бути 365,2425, щоб врахувати 400-річний виняток з григоріанського календаря.
бріанарія

1
Що ж, проблему можна вирішити правильно - ви можете заздалегідь сказати, які роки мають високосні дні та високосні секунди та все таке. Просто не існує надзвичайно елегантного способу зробити це, не віднімаючи роки, потім місяці, потім дні тощо ... у двох форматованих датах
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)))

Ця ситуація перервана, коли доб є 29 лютого, а поточний рік не є високосним.
Мисливець Трей

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

Особа, яка народилася 29 лютого, вважатиметься такою, що досяг 28 років наступного 28 лютого.
Джон Махін

Гаразд. Виправлено для розміщення 0,08% населення, народженого 29 числа, шляхом перевернення тесту з "день народження після сьогоднішнього дня" до "є день народження раніше сьогоднішнього дня". Це вирішує це?
Джон Мей

Це працює правильно для вашого прикладу!?! Якщо я встановив "сьогодні" на 28 лютого 2009 року, а дата народження - на 29 лютого 2008 року, це поверне нульове значення, принаймні для мене; не 1, як ви пропонуєте (Python 2.5.2). Не треба грубити містера Мачіна. Саме з якими двома побаченнями у вас виникають проблеми?
Джон Мей

Я спробую ще раз: людину, народжену 29 лютого, більшість людей з більшості юридичних цілей вважатимуть такою, що досяг віку 1 наступного 28 лютого. Ваш код виробляє 0, як до, так і після "виправлення". Насправді, дві версії вашого коду дають ТОЧНО однаковий вихід для ВСІХ 9 можливостей введення (місяць <==> X день <==>). BTW, 100,0 / (4 * 365 + 1) дає 0,068, а не 0,08.
Джон Махін

2
Зітхнути. (0) Вирішення питань, що відбулися 29 лютого, є важливим для будь-якої арифметичної дати; ти просто проігнорував це. (1) Ваш код не був кращим з першого разу; що ти не розумієш у "ПРОДАЙТЕ ТИЙ той самий вихід"? (2) Три можливі атомні результати (<, ==,>), порівнюючи сьогоднішній місяць і доб.місяць; три можливі атомні результати, порівнюючи сьогодні.day та dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
Джон Махін

1

Наскільки точно вам це потрібно? td.days / 365.25приведе вас досить близько, якщо ви переживаєте за високосні роки.


Я дуже переживаю високосні роки. Він повинен перевірити, чи є людина старше 18 років.
Migol

Тоді немає легкого однолінійного руху, вам доведеться проаналізувати дві дати і з’ясувати, чи пройшов чоловік 18-й день народження чи ні.
eduffy

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)

І так, додавання залежності для цієї єдиної задачі, що йдеться, безумовно, було б надмірним у порівнянні навіть із використанням timeutil (запропонований Ріком Коуплендом).


1

Зрештою, у вас є питання з математики. Якщо кожні 4 роки у нас є додатковий день, то можна пірнати в таймеделу за днями, а не на 365, а на 365 * 4 + 1, що дасть вам суму 4 роки. Потім розділіть його ще раз на 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)


Річ високосного року не застосовується, коли роки ділиться на 100, за винятком випадків, коли вони діляться на 400. Отже, для 2000 року: - це ділиться на чотири, так що має бути високосним, але ... - це також ділиться на сотню, так що це не повинно бути високосним, але ... - воно ділиться на 400, тож насправді це був високосний рік. За 1900 рік: - воно ділиться на 4, тому воно повинно бути стрибковим. - воно ділиться на 100, тому не повинно бути високосним. - НЕ ділиться на 400, тому це правило не застосовується. 1900 рік не був високосним.
Jblasco

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

ця функція повертає різницю в роках між двома датами (приймається як рядки у форматі 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

Що таке піфдат?

Враховуючи, що 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()

Це код, який використовував оператор «Карусель» у фільмі Logan's Run;)

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


0

Я натрапив на це питання і знайшов Адамса відповідь на найкориснішу https://stackoverflow.com/a/765862/2964689

Але не було жодного прикладу пітону його методу, але ось що я в кінцевому підсумку використав.

вхід: об’єкт дати

вихід: цілий вік за цілі роки

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

Мені сподобалося рішення Джона Мей за його простоту, і мене не так хвилює те, як 28 лютого чи 1 березня, коли це не високосний рік, визначити вік людей, народжених 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.