Python-Zeitdelta in Jahren


Antworten:


156

Sie brauchen mehr als eine timedelta, um zu sagen, wie viele Jahre vergangen sind; Sie müssen auch das Anfangs- (oder End-) Datum kennen. (Es ist eine Schaltjahrsache.)

Am besten verwenden Sie das dateutil.relativedelta Objekt , aber das ist ein Modul eines Drittanbieters. Wenn Sie wissen möchten, datetimedass nJahre von einem bestimmten Datum an vergangen sind (standardmäßig jetzt), können Sie Folgendes tun:

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)

Wenn Sie lieber bei der Standardbibliothek bleiben möchten, ist die Antwort etwas komplexer:

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)

Wenn es 2/29 ist und es vor 18 Jahren keine 2/29 gab, gibt diese Funktion 2/28 zurück. Wenn Sie lieber 3/1 zurückgeben möchten, ändern Sie einfach die letzte returnAnweisung in read ::

    return from_date.replace(month=3, day=1,
                             year=from_date.year-years)

Ihre Frage besagte ursprünglich, dass Sie wissen wollten, wie viele Jahre seit einem bestimmten Datum vergangen sind. Angenommen, Sie möchten eine ganzzahlige Anzahl von Jahren, können Sie anhand von 365,25 Tagen pro Jahr raten und dann mit einer der yearsagooben definierten Funktionen prüfen :

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
Mit 365,2425 (anstelle von 365,25) können Sie die volle Genauigkeit erreichen, wobei die 400-Jahres-Ausnahme für den Gregorianischen Kalender berücksichtigt wird.
Brian

3
Ihre Funktion ist für Länder wie Großbritannien und Hongkong rechtlich unzulässig, da sie für Schaltjahre bis zum 1. März "aufrunden".
Antiheld


3
siehe auch dies und das Beide sind ausgezeichnete Listen von Dingen, die über die Zeit nicht wahr sind.
Gvoysey

49

Wenn Sie versuchen zu überprüfen, ob jemand 18 Jahre alt ist, timedeltafunktioniert die Verwendung in einigen Randfällen aufgrund von Schaltjahren nicht richtig. Zum Beispiel wird jemand, der am 1. Januar 2000 geboren wurde, am 1. Januar 2018 genau 6575 Tage später 18 Jahre alt (einschließlich 5 Schaltjahre), aber jemand, der am 1. Januar 2001 geboren wurde, wird am 1. Januar genau 6574 Tage später 18 Jahre alt. 2019 (4 Schaltjahre inklusive). Wenn also jemand genau 6574 Tage alt ist, können Sie nicht feststellen, ob er 17 oder 18 Jahre alt ist, ohne ein wenig mehr Informationen über sein Geburtsdatum zu kennen.

Der richtige Weg, dies zu tun, besteht darin, das Alter direkt von den Daten zu berechnen, indem die zwei Jahre subtrahiert werden und dann eines subtrahiert wird, wenn der aktuelle Monat / Tag vor dem Geburtsmonat / -tag liegt.


9

Zunächst einmal kann das Problem auf der detailliertesten Ebene nicht genau gelöst werden. Die Jahre variieren in der Länge, und es gibt keine klare "richtige Wahl" für die Jahreslänge.

Das heißt, erhalten Sie den Unterschied in den Einheiten, die "natürlich" sind (wahrscheinlich Sekunden), und dividieren Sie durch das Verhältnis zwischen dieser und den Jahren. Z.B

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

...oder Wasauchimmer. Halten Sie sich von Monaten fern, da sie noch weniger gut definiert sind als Jahre.


2
Das ist NICHT das, was jemand meint oder benutzt, wenn es darum geht, wie viele Jahre Dienst oder ob eine Person ein bestimmtes Alter erreicht hat.
John Machin

3
Ihr 365,25 sollte 365,2425 sein, um die 400-jährige Ausnahme des Gregorianischen Kalenders zu berücksichtigen.
Brian

1
Nun, das Problem kann richtig gelöst werden - Sie können im Voraus erkennen, welche Jahre Schalttage und Schaltsekunden haben und all das. Es ist nur so, dass es keine äußerst elegante Möglichkeit gibt, dies zu tun, ohne die Jahre, dann die Monate, dann die Tage usw. in den beiden formatierten Daten
Litherum

7

Hier ist eine aktualisierte DOB-Funktion, die Geburtstage genauso berechnet wie Menschen:

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)))

Dies bricht ab, wenn dob der 29. Februar ist und das laufende Jahr kein Schaltjahr ist.
Trey Hunner

4

Ermitteln Sie die Anzahl der Tage und dividieren Sie diese durch 365,2425 (das mittlere Gregorianische Jahr) für Jahre. Teilen Sie durch 30.436875 (der mittlere gregorianische Monat) für Monate.


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

Eine am 29. Februar geborene Person wird am folgenden 28. Februar als volljährig behandelt behandelt.
John Machin

OK. Korrigiert, um die 0,08% der am 29. geborenen Bevölkerung aufzunehmen, indem der Test von "ist Geburtstag nach heute" auf "ist Geburtstag vor heute" invertiert wurde. Löst das das?
John Mee

Für Ihr Beispiel funktioniert es richtig!?! Wenn ich "heute" auf den 28. Februar 2009 und das Geburtsdatum auf den 29. Februar 2008 setze, wird zumindest für mich Null zurückgegeben. nicht 1, wie Sie vorschlagen (Python 2.5.2). Es ist nicht nötig, Mr. Machin unhöflich zu machen. Mit welchen zwei Daten haben Sie genau Probleme?
John Mee

Ich werde es noch einmal versuchen: Eine am 29. Februar geborene Person wird von den meisten Menschen für die meisten rechtlichen Zwecke so behandelt, als hätte sie am folgenden 28. Februar das 1. Lebensjahr erreicht. Ihr Code erzeugt vor und nach Ihrer "Korrektur" 0. Tatsächlich erzeugen die beiden Versionen Ihres Codes genau die gleiche Ausgabe für ALLE 9 Eingabemöglichkeiten (Monat <==> X Tag <==>). Übrigens ergibt 100,0 / (4 · 365 + 1) 0,068, nicht 0,08.
John Machin

2
Seufzer. (0) Die Behandlung von Problemen am 29. Februar ist für jede Datumsarithmetik von wesentlicher Bedeutung. du hast es einfach ignoriert. (1) Ihr Code war beim ersten Mal nicht besser dran. Was verstehen Sie nicht unter "GENAU die gleiche Ausgabe produzieren"? (2) Drei mögliche atomare Ergebnisse (<, ==,>), die heute.month und dob.month verglichen werden; drei mögliche atomare Ergebnisse im Vergleich von today.day und dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
John Machin

1

Wie genau muss es sein? td.days / 365.25Sie werden ziemlich nah dran sein, wenn Sie sich Sorgen um Schaltjahre machen.


Ich mache mir wirklich Sorgen um Schaltjahre. Es sollte überprüft werden, ob die Person über 18 Jahre alt ist.
Migol

Dann gibt es keinen einfachen Einzeiler. Sie müssen die beiden Daten analysieren und herausfinden, ob die Person ihren 18. Geburtstag überschritten hat oder nicht.
Eduffy

1

Eine weitere hier nicht erwähnte Drittanbieter-Bibliothek ist mxDateTime (Vorgänger von Python datetimeund Drittanbieter timeutil), die für diese Aufgabe verwendet werden könnte.

Das vorgenannte yearsagowäre:

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)

Der erste Parameter wird voraussichtlich eine DateTimeInstanz sein.

Um normal datetimezu konvertieren , können DateTimeSie dies für eine Genauigkeit von 1 Sekunde verwenden):

def DT_from_dt_s(t):
    return DT.DateTimeFromTicks(time.mktime(t.timetuple()))

oder dies für 1 Mikrosekunden Genauigkeit:

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)

Und ja, das Hinzufügen der Abhängigkeit für diese einzelne Aufgabe wäre definitiv ein Overkill im Vergleich zur Verwendung von timeutil (vorgeschlagen von Rick Copeland).


1

Am Ende haben Sie ein mathematisches Problem. Wenn wir alle 4 Jahre einen zusätzlichen Tag haben, können wir das Zeitdelta in Tagen tauchen, nicht um 365, sondern um 365 * 4 + 1, das ergibt einen Betrag von 4 Jahren. Teilen Sie es dann erneut durch 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)


Das Schaltjahr gilt nicht, wenn die Jahre durch 100 teilbar sind, außer wenn sie durch 400 teilbar sind. Für das Jahr 2000 gilt also: - Es ist durch vier teilbar, also sollte es ein Sprung sein, aber ... - es ist auch teilbar um hundert, also sollte es kein Sprung sein, aber ... - es ist teilbar durch 400, also war es tatsächlich ein Schaltjahr. Für 1900: - Es ist durch 4 teilbar, also sollte es ein Sprung sein. - Es ist durch 100 teilbar, daher sollte es kein Sprung sein. - Es ist NICHT durch 400 teilbar, daher gilt diese Regel nicht. 1900 war kein Schaltjahr.
Jblasco

1

Dies ist die Lösung, die ich ausgearbeitet habe, ich hoffe, sie kann helfen ;-)

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

Auch wenn dieser Thread bereits tot ist, könnte ich eine funktionierende Lösung für genau das Problem vorschlagen, mit dem ich konfrontiert war. Hier ist es (Datum ist eine Zeichenfolge im Format TT-MM-JJJJ):

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

Diese Funktion gibt die Differenz in Jahren zwischen zwei Daten zurück (als Zeichenfolgen im ISO-Format verwendet, kann jedoch leicht geändert werden, um jedes Format anzunehmen).

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

Ich werde Pyfdate vorschlagen

Was ist Pyfdate?

Angesichts des Ziels von Python, eine leistungsstarke und benutzerfreundliche Skriptsprache zu sein, sind die Funktionen zum Arbeiten mit Datum und Uhrzeit nicht so benutzerfreundlich, wie sie sein sollten. Der Zweck von pyfdate besteht darin, diese Situation zu beheben, indem Funktionen für die Arbeit mit Datums- und Uhrzeitangaben bereitgestellt werden, die genauso leistungsfähig und benutzerfreundlich sind wie der Rest von Python.

das Tutorial


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()

Dies ist der Code, den der Carrousel-Operator in Logans Run-Film ausgeführt hat;)

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


0

Ich bin auf diese Frage gestoßen und habe festgestellt, dass Adams die hilfreichste Antwort auf https://stackoverflow.com/a/765862/2964689 ist

Aber es gab kein Python-Beispiel für seine Methode, aber hier ist, was ich letztendlich verwendet habe.

Eingabe: Datum / Uhrzeit-Objekt

Ausgabe: ganzzahliges Alter in ganzen Jahren

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

Ich mochte John Mees Lösung wegen ihrer Einfachheit, und ich bin nicht so besorgt darüber, wie man am 28. Februar oder 1. März, wenn es kein Schaltjahr ist, das Alter der am 29. Februar geborenen Menschen bestimmen kann. Aber hier ist eine Optimierung seines Codes was meiner Meinung nach die Beschwerden anspricht:

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.