Python timedelta trong năm


Câu trả lời:


156

Bạn cần nhiều hơn một timedeltađể nói bao nhiêu năm đã trôi qua; bạn cũng cần biết ngày bắt đầu (hoặc kết thúc). (Đó là một năm nhuận.)

Đặt cược tốt nhất của bạn là sử dụng dateutil.relativedelta đối tượng , nhưng đó là mô-đun của bên thứ 3. Nếu bạn muốn biết datetimeđó là nnăm kể từ ngày nào đó (mặc định là ngay bây giờ), bạn có thể làm như sau ::

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)

Nếu bạn muốn gắn bó với thư viện chuẩn, câu trả lời phức tạp hơn một chút ::

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)

Nếu đó là 29/2 và 18 năm trước không có 29/2, chức năng này sẽ trả về 28/11. Nếu bạn muốn trả về 3/1, chỉ cần thay đổi returncâu lệnh cuối cùng để đọc ::

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

Câu hỏi của bạn ban đầu cho biết bạn muốn biết đã bao nhiêu năm kể từ ngày nào đó. Giả sử bạn muốn có số nguyên năm, bạn có thể đoán dựa trên 365,25 ngày mỗi năm và sau đó kiểm tra bằng cách sử dụng một trong các yearsagohàm được xác định ở trên ::

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
Bạn có thể hoàn toàn chính xác với 365.2425 (thay vì 365.25), tính đến ngoại lệ 400 năm cho lịch Gregorian.
brianary

3
Chức năng của bạn bị phá vỡ hợp pháp đối với các quốc gia như Vương quốc Anh và Hồng Kông, vì họ "làm tròn" đến ngày 1 tháng 3 trong những năm nhuận.
phản diện


3
xem thêm cái nàycái này Cả hai đều là những danh sách tuyệt vời về những điều không đúng với thời gian.
gvoysey

49

Nếu bạn đang cố kiểm tra xem ai đó đủ 18 tuổi, việc sử dụng timedeltasẽ không hoạt động chính xác trong một số trường hợp cạnh vì năm nhuận. Ví dụ: một người sinh ngày 1 tháng 1 năm 2000, sẽ tròn 18 chính xác là 6575 ngày sau vào ngày 1 tháng 1 năm 2018 (bao gồm 5 năm nhuận), nhưng một người sinh vào ngày 1 tháng 1 năm 2001 sẽ quay lại 18 chính xác sau 6574 ngày vào ngày 1 tháng 1 năm 2019 (bao gồm 4 năm nhuận). Do đó, nếu bạn có ai đó chính xác là 6574 ngày, bạn không thể xác định họ 17 hay 18 tuổi mà không biết thêm một chút thông tin về ngày sinh của họ.

Cách chính xác để làm điều này là tính tuổi trực tiếp từ các ngày, bằng cách trừ hai năm, sau đó trừ đi một nếu tháng / ngày hiện tại trước tháng sinh / ngày.


9

Trước hết, ở cấp độ chi tiết nhất, vấn đề không thể được giải quyết chính xác. Năm có độ dài khác nhau và không có "lựa chọn đúng" rõ ràng cho độ dài năm.

Điều đó nói rằng, có được sự khác biệt trong bất kỳ đơn vị nào là "tự nhiên" (có thể là giây) và chia cho tỷ lệ giữa đó và năm. Ví dụ

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

...hay bất cứ cái gì. Tránh xa các tháng, vì chúng thậm chí còn ít được xác định rõ hơn so với năm.


2
Đó không phải là ý nghĩa của bất kỳ ai hoặc sử dụng khi đó là câu hỏi về dịch vụ bao nhiêu năm hoặc một người đạt được một độ tuổi cụ thể.
John Machin

3
365,25 của bạn phải là 365,2425 để tính đến ngoại lệ 400 năm của lịch Gregorian.
brianary

1
Chà, vấn đề có thể được giải quyết một cách chính xác - bạn có thể biết trước thời điểm năm nào có ngày nhuận và giây nhuận và tất cả điều đó. Chỉ là không có cách làm cực kỳ tao nhã mà không trừ đi năm tháng, rồi tháng, rồi ngày, v.v ... trong hai ngày được định dạng
Litherum

7

Đây là một chức năng DOB được cập nhật, tính toán ngày sinh nhật giống như cách con người làm:

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

Điều này phá vỡ khi dob là ngày 29 tháng 2 và năm hiện tại không phải là năm nhuận.
Trey Hunner

4

Lấy số ngày, sau đó chia cho 365.2425 (năm trung bình của năm Gregorian) trong nhiều năm. Chia cho 30.436875 (tháng có nghĩa là Gregorian) trong nhiều tháng.


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

Một người sinh ngày 29 tháng 2 sẽ được coi là đạt được 1 tuổi vào ngày 28 tháng 2 sau.
John Machin

Đồng ý. Được sửa chữa để phù hợp với 0,08% dân số sinh vào ngày 29 bằng cách đảo ngược bài kiểm tra từ "là sinh nhật sau hôm nay" thành "là sinh nhật trước ngày hôm nay". Điều đó có giải quyết được không?
John Mee

Nó không hoạt động chính xác cho ví dụ của bạn!?! Nếu tôi đặt "ngày hôm nay" thành ngày 28 tháng 2 năm 2009 và ngày sinh đến ngày 29 tháng 2 năm 2008, nó sẽ trả về số 0 - ít nhất là cho tôi; không phải 1 như bạn đề xuất (Python 2.5.2). Không cần phải thô lỗ ông Machin. Chính xác thì hai ngày bạn gặp rắc rối với?
John Mee

Tôi sẽ thử lại: Một người sinh ngày 29 tháng 2 sẽ được hầu hết mọi người đối xử vì hầu hết các mục đích hợp pháp là đã đạt được 1 tuổi vào ngày 28 tháng 2 sau. Mã của bạn tạo ra 0, cả trước và sau khi "hiệu chỉnh". Trên thực tế, hai phiên bản mã của bạn tạo ra CHÍNH XÁC cùng một đầu ra cho TẤT CẢ 9 khả năng đầu vào (tháng <==> X ngày <==>). BTW, 100.0 / (4 * 365 + 1) tạo ra 0,068, không phải 0,08.
John Machin

2
Thở dài. (0) Giải quyết các vấn đề ngày 29 tháng 2 là điều cần thiết trong bất kỳ số học ngày nào; bạn chỉ cần bỏ qua nó. (1) Lần đầu tiên mã của bạn không tốt hơn; bạn không hiểu gì trong "sản xuất chính xác cùng một đầu ra"? (2) Ba kết quả nguyên tử có thể (<, ==,>) so sánh hôm nay.month và dob.month; ba kết quả nguyên tử có thể so sánh hôm nay.day và dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/ấc
John Machin

1

Làm thế nào chính xác để bạn cần nó được? td.days / 365.25sẽ giúp bạn khá gần, nếu bạn lo lắng về những năm nhuận.


Tôi thực sự lo lắng về những năm nhuận. Nó nên kiểm tra nếu người trên 18 tuổi.
Migol

Sau đó, không có một lót dễ dàng, bạn sẽ phải phân tích hai ngày và tìm hiểu xem người đó đã qua sinh nhật thứ 18 của họ hay chưa.
eduffy

1

Tuy nhiên, một lib bên thứ 3 khác không được đề cập ở đây là mxDateTime (tiền thân của cả python datetimevà bên thứ 3 timeutil) có thể được sử dụng cho nhiệm vụ này.

Những điều đã nói ở trên yearsagosẽ là:

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)

Tham số đầu tiên dự kiến ​​là một DateTimeví dụ.

Để chuyển đổi thông thường datetimesang DateTimebạn có thể sử dụng điều này trong độ chính xác 1 giây):

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

hoặc điều này cho độ chính xác 1 micro giây:

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)

Và vâng, việc thêm sự phụ thuộc cho nhiệm vụ duy nhất này trong câu hỏi chắc chắn sẽ là một sự quá mức so với việc sử dụng timeutil (được đề xuất bởi Rick Copeland).


1

Cuối cùng, những gì bạn có là một vấn đề toán học. Nếu cứ sau 4 năm chúng ta lại có thêm một ngày thì hãy lặn theo thời gian theo ngày, không phải bằng 365 mà là 365 * 4 + 1, điều đó sẽ cung cấp cho bạn số tiền là 4 năm. Sau đó chia lại cho 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)


Điều năm nhuận không áp dụng khi số năm chia hết cho 100, trừ khi chúng chia hết cho 400. Vì vậy, trong năm 2000: - nó chia hết cho bốn, vì vậy nó nên chia cho nhưng ... - nó cũng chia hết bởi một trăm, vì vậy nó không nên là bước nhảy vọt nhưng ... - nó chia hết cho 400, vì vậy nó thực sự là một năm nhuận. Cho năm 1900: - nó chia hết cho 4 nên sẽ là bước nhảy vọt. - nó chia hết cho 100, vì vậy nó không nên nhảy. - nó KHÔNG chia hết cho 400, vì vậy quy tắc này không áp dụng. Năm 1900 không phải là một năm nhuận.
Jblasco

1

Đây là giải pháp tôi đã thực hiện, tôi hy vọng có thể giúp đỡ ;-)

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

Mặc dù chủ đề này đã chết, tôi có thể đề xuất một giải pháp hiệu quả cho vấn đề tương tự mà tôi đang gặp phải. Đây là (ngày là một chuỗi ở định dạng 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

hàm này trả về chênh lệch theo năm giữa hai ngày (được lấy dưới dạng chuỗi ở định dạng ISO, nhưng nó có thể dễ dàng sửa đổi để lấy ở bất kỳ định dạng nào)

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

Tôi sẽ đề nghị Pyfdate

Pyfdate là gì?

Do mục tiêu của Python là ngôn ngữ kịch bản mạnh mẽ và dễ sử dụng, các tính năng của nó để làm việc với ngày và giờ không thân thiện với người dùng như bình thường. Mục đích của pyfdate là khắc phục tình trạng đó bằng cách cung cấp các tính năng để làm việc với ngày và giờ mạnh mẽ và dễ sử dụng như phần còn lại của Python.

các hướng dẫn


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

Đây là mã mà nhà điều hành Carrousel đang chạy trong bộ phim Run của Logan;)

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


0

Tôi đã xem qua câu hỏi này và thấy Adams trả lời https://stackoverflow.com/a/765862/2964689 hữu ích nhất

Nhưng không có ví dụ nào về phương pháp của anh ta, nhưng đây là những gì tôi đã sử dụng.

đầu vào: đối tượng datetime

đầu ra: tuổi nguyên trong cả năm

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

Tôi thích giải pháp của John Mee vì sự đơn giản của nó và tôi không quan tâm đến việc làm thế nào, vào ngày 28 tháng 2 hoặc ngày 1 tháng 3 khi không phải là năm nhuận, để xác định tuổi của những người sinh vào ngày 29 tháng 2. Nhưng đây là một chỉnh sửa mã của anh ấy mà tôi nghĩ rằng giải quyết các khiếu nại:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.