Python timedelta ในปีที่ผ่านมา


140

ฉันต้องตรวจสอบว่ามีบางปีที่ผ่านมานับตั้งแต่บางวัน ขณะนี้ฉันได้รับtimedeltaจากdatetimeโมดูลและไม่รู้วิธีแปลงเป็นปี


4
ดูคำตอบนี้: stackoverflow.com/a/9754466/65387
อดัม

คำตอบ:


156

คุณต้องการมากกว่า a 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 ปียกเว้นในปฏิทินเกรกอเรียน
brianary

3
ฟังก์ชั่นของคุณถูกกฎหมายสำหรับประเทศต่าง ๆ เช่นสหราชอาณาจักรและฮ่องกงเพราะพวกเขา "รอบ" ถึง 1 มีนาคมสำหรับปีอธิกสุรทิน
antihero


3
ดูสิ่งนี้และสิ่งนี้ทั้งสองรายการที่ยอดเยี่ยมของสิ่งต่าง ๆ ที่ไม่เป็นความจริงเกี่ยวกับเวลา
gvoysey

49

หากคุณกำลังพยายามที่จะตรวจสอบว่ามีใครอายุ 18 ปีการใช้timedeltaจะไม่ทำงานอย่างถูกต้องในบางกรณีขอบเพราะปีอธิกสุรทิน ตัวอย่างเช่นคนที่เกิดในวันที่ 1 มกราคม 2000 จะมีอายุ 18 ปี 6575 วันต่อมาในวันที่ 1 มกราคม 2018 (รวม 5 ปีอธิกสุรทิน 5 ปี) แต่มีคนที่เกิดในวันที่ 1 มกราคม 2001 จะมีอายุ 18 ปี 6574 ในวันที่ 1 มกราคม 2019 (รวมการก้าวกระโดด 4 ปี) ดังนั้นหากคุณมีอายุ 6574 วันคุณไม่สามารถระบุได้ว่าพวกเขาอายุ 17 หรือ 18 ปีโดยไม่ทราบข้อมูลเพิ่มเติมเกี่ยวกับวันเกิดของพวกเขาหรือไม่

วิธีที่ถูกต้องในการทำเช่นนี้คือการคำนวณอายุโดยตรงจากวันที่โดยการลบสองปีแล้วลบหนึ่งถ้าเดือน / วันปัจจุบันนำหน้าเดือนเกิด / วัน


9

ก่อนอื่นในระดับรายละเอียดสูงสุดปัญหาจะไม่สามารถแก้ไขได้อย่างแน่นอน ปีมีความยาวแตกต่างกันและไม่มี "ทางเลือกที่ถูกต้อง" สำหรับความยาวปี

ที่กล่าวว่าได้รับความแตกต่างในสิ่งที่หน่วยเป็น "ธรรมชาติ" (อาจวินาที) และหารด้วยอัตราส่วนระหว่างที่และปี เช่น

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

... หรืออะไรก็ตาม อยู่ห่างจากเดือนเนื่องจากพวกเขายังนิยามน้อยกว่าปี


2
นั่นไม่ใช่สิ่งที่ทุกคนหมายถึงหรือใช้เมื่อมันเป็นคำถามของการบริการเป็นเวลากี่ปีหรือมีบุคคลที่บรรลุอายุโดยเฉพาะ
John Machin

3
365.25 ของคุณควรเป็น 365.2425 เพื่อพิจารณาข้อยกเว้น 400 ปีของปฏิทินเกรโกเรียน
brianary

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

สิ่งนี้จะหยุดเมื่อวันที่ dob คือ 29 กุมภาพันธ์และปีปัจจุบันไม่ใช่ปีอธิกสุรทิน
แต้ม Hunner

4

รับจำนวนวันแล้วหารด้วย 365.2425 (ค่าเฉลี่ยเกรกอเรียนปี) เป็นเวลาหลายปี หารด้วย 30.436875 (เดือน Gregorian เฉลี่ย) สำหรับเดือน


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 กุมภาพันธ์จะได้รับการปฏิบัติเหมือนเป็นผู้ที่มีอายุครบ 1 ปีในวันที่ 28 กุมภาพันธ์
John Machin

ตกลง. ถูกต้องเพื่อรองรับ 0.08% ของประชากรที่เกิดในวันที่ 29 โดยการคว่ำการทดสอบจาก "เป็นวันเกิดหลังจากวันนี้" เป็น "วันเกิดก่อนวันนี้" นั่นแก้ปัญหาเหรอ?
John Mee

มันทำงานได้อย่างถูกต้องสำหรับตัวอย่างของคุณ!?! ถ้าฉันตั้งค่า "วันนี้" เป็น 28 กุมภาพันธ์ 2009 และวันเดือนปีเกิดถึง 29 กุมภาพันธ์ 2008 มันจะคืนค่าศูนย์ - อย่างน้อยสำหรับฉัน ไม่ใช่ 1 ตามที่คุณแนะนำ (Python 2.5.2) ไม่จำเป็นต้องหยาบคาย Mr Machin คุณมีปัญหาอะไรกับวันที่สองวัน
John Mee

ฉันจะลองอีกครั้ง: คนที่เกิดในวันที่ 29 กุมภาพันธ์จะได้รับการปฏิบัติโดยคนส่วนใหญ่เพื่อจุดประสงค์ทางกฎหมายส่วนใหญ่เมื่ออายุครบ 1 ปีในวันที่ 28 กุมภาพันธ์ รหัสของคุณสร้าง 0 ทั้งก่อนและหลัง "การแก้ไข" ของคุณ ในความเป็นจริงแล้วโค้ดของคุณทั้งสองเวอร์ชันจะสร้างผลลัพธ์ที่เหมือนกันทั้งหมดสำหรับความเป็นไปได้ในการป้อนข้อมูลทั้งหมด 9 รายการ (เดือน <==> X day <==>) BTW, 100.0 / (4 * 365 + 1) สร้าง 0.068 ไม่ใช่ 0.08
John Machin

2
ถอนหายใจ (0) การจัดการกับปัญหาในวันที่ 29 กุมภาพันธ์เป็นสิ่งจำเป็นในทุก ๆ วันของเลขคณิต คุณไม่สนใจมัน (1) รหัสของคุณไม่ดีขึ้นในครั้งแรก สิ่งที่คุณไม่เข้าใจใน "ผลิตผลลัพธ์เดียวกันแน่นอน" (2) สามผลลัพธ์ที่เป็นไปได้ของอะตอม (<, ==,>) เปรียบเทียบ today.month และ dob.month; สามผลลัพธ์ที่เป็นไปได้ของอะตอมเมื่อเปรียบเทียบกับ today.day และ dob.day 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
John Machin

1

คุณต้องการให้มันแม่นยำแค่ไหน? td.days / 365.25จะทำให้คุณเข้ามาใกล้ถ้าคุณกังวลเกี่ยวกับการก้าวกระโดดปี


ฉันกังวลจริงๆเกี่ยวกับการก้าวกระโดดปี ควรตรวจสอบว่าบุคคลนั้นมีอายุมากกว่า 18 ปีหรือไม่
Migol

ถ้างั้นก็ไม่มีซับในง่ายๆคุณจะต้องแยกวันที่สองวันแล้วคิดออกถ้าคนที่ผ่านวันเกิดครบรอบ 18 ปีหรือไม่
eduffy

1

อีกหนึ่งบุคคลที่สามที่ไม่ได้กล่าวถึงในที่นี้คือ mxDateTime lib (ผู้บุกเบิกของ 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)

และใช่การเพิ่มการพึ่งพาสำหรับงานเดี่ยวที่เป็นปัญหานี้จะเป็นการ overkill เปรียบเทียบกับการใช้ timeutil (แนะนำโดย Rick Copeland)


1

ในที่สุดสิ่งที่คุณมีคือปัญหาคณิตศาสตร์ หากทุก ๆ 4 ปีเรามีวันพิเศษให้ดำดิ่ง timedelta เป็นวันไม่ใช่ 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

pyfdate คืออะไร

เป้าหมายของไพ ธ อนนั้นเป็นภาษาสคริปต์ที่ทรงพลังและง่ายต่อการใช้งานคุณสมบัติในการทำงานกับวันที่และเวลาไม่เป็นมิตรกับผู้ใช้เท่าที่ควร วัตถุประสงค์ของ 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()

นี่คือรหัสที่ผู้ประกอบการ Carrousel กำลังทำงานในภาพยนตร์ Run ของ Logan;)

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


0

ฉันเจอคำถามนี้และพบว่าอดัมส์ตอบคำถามที่เป็นประโยชน์ที่สุด https://stackoverflow.com/a/765862/2964689

แต่ไม่มีตัวอย่างไพ ธ อนเกี่ยวกับวิธีการของเขา แต่นี่คือสิ่งที่ฉันใช้

input: วัตถุวันที่และเวลา

เอาท์พุท: อายุจำนวนเต็มทั้งปี

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

ฉันชอบวิธีการแก้ปัญหาของ John Mee สำหรับความเรียบง่ายและฉันไม่กังวลเกี่ยวกับวิธีการในวันที่ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.