Saya perlu memeriksa apakah beberapa tahun telah berlalu sejak suatu tanggal. Saat ini saya mendapatkan timedeltadari datetimemodul dan saya tidak tahu bagaimana mengubahnya menjadi tahun.
Anda membutuhkan lebih dari satu timedeltauntuk mengetahui berapa tahun telah berlalu; Anda juga perlu mengetahui tanggal awal (atau akhir). (Ini adalah tahun kabisat.)
Taruhan terbaik Anda adalah menggunakan dateutil.relativedeltaobjek , tapi itu adalah modul pihak ke-3. Jika Anda ingin mengetahui datetimebahwa itu adalah ntahun dari suatu tanggal (default sekarang), Anda dapat melakukan hal berikut:
Jika Anda lebih suka menggunakan pustaka standar, jawabannya sedikit lebih rumit ::
from datetime import datetime
defyearsago(years, from_date=None):if from_date isNone:
from_date = datetime.now()
try:
return from_date.replace(year=from_date.year - years)
except ValueError:
# Must be 2/29!assert from_date.month == 2and from_date.day == 29# can be removedreturn from_date.replace(month=2, day=28,
year=from_date.year-years)
Jika 2/29, dan 18 tahun yang lalu tidak ada 2/29, fungsi ini akan mengembalikan 2/28. Jika Anda lebih suka mengembalikan 3/1, ubah saja returnpernyataan terakhir menjadi:
Pertanyaan Anda awalnya mengatakan Anda ingin tahu sudah berapa tahun sejak suatu tanggal. Dengan asumsi Anda menginginkan bilangan bulat tahun, Anda dapat menebak berdasarkan 365,25 hari per tahun dan kemudian memeriksa menggunakan salah satu yearsagofungsi yang ditentukan di atas ::
defnum_years(begin, end=None):if end isNone:
end = datetime.now()
num_years = int((end - begin).days / 365.25)
if begin > yearsago(num_years, end):
return num_years - 1else:
return num_years
lihat juga ini dan ini Keduanya adalah daftar yang sangat bagus tentang hal-hal yang tidak benar tentang waktu.
gvoysey
49
Jika Anda mencoba memeriksa apakah seseorang berusia 18 tahun, penggunaan timedeltatidak akan berfungsi dengan benar pada beberapa kasus edge karena tahun kabisat. Misalnya, seseorang yang lahir pada tanggal 1 Januari 2000, akan berusia 18 tahun tepat 6575 hari kemudian pada tanggal 1 Januari 2018 (termasuk 5 tahun kabisat), tetapi seseorang yang lahir pada tanggal 1 Januari 2001, akan berusia 18 tahun tepat 6574 hari kemudian pada tanggal 1 Januari, 2019 (termasuk 4 tahun kabisat). Jadi, jika seseorang berusia tepat 6574 hari, Anda tidak dapat menentukan apakah mereka berusia 17 atau 18 tahun tanpa mengetahui lebih banyak informasi tentang tanggal lahir mereka.
Cara yang benar untuk melakukan ini adalah menghitung usia secara langsung dari tanggal, dengan mengurangi dua tahun, dan kemudian menguranginya jika bulan / hari saat ini mendahului bulan / hari kelahiran.
Pertama, pada tingkat yang paling mendetail, masalah tidak dapat diselesaikan dengan tepat. Panjang tahun bervariasi, dan tidak ada "pilihan tepat" yang jelas untuk lamanya tahun.
Meskipun demikian, dapatkan perbedaan dalam satuan apa pun yang "alami" (mungkin detik) dan bagi dengan rasio antara satuan tersebut dan tahun. Misalnya
Itu BUKAN apa yang orang maksud atau gunakan ketika itu adalah pertanyaan tentang berapa tahun masa kerja atau sudahkah seseorang mencapai usia tertentu.
John Machin
3
365.25 Anda harus 365.2425 untuk memperhitungkan pengecualian 400 tahun dari kalender Gregorian.
Brianary
1
Nah, masalahnya bisa diselesaikan dengan benar - Anda bisa tahu sebelumnya tahun mana yang memiliki hari kabisat dan detik kabisat dan sebagainya. Hanya saja tidak ada cara yang sangat elegan untuk melakukannya tanpa mengurangi tahun, lalu bulan, lalu hari, dll ... dalam dua tanggal yang diformat
Litherum
8
Berikut adalah fungsi DOB yang diperbarui, yang menghitung ulang tahun dengan cara yang sama seperti manusia:
import datetime
import locale
# Source: https://en.wikipedia.org/wiki/February_29
PRE = [
'US',
'TW',
]
POST = [
'GB',
'HK',
]
defget_country():
code, _ = locale.getlocale()
try:
return code.split('_')[1]
except IndexError:
raise Exception('Country cannot be ascertained from locale.')
defget_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.')
defage(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 == 2and dob.day == 29:
birthday = get_leap_birthday(today.year)
else:
raise e
if today < birthday:
years -= 1return years
print(age(datetime.date(1988, 2, 29)))
Ini rusak ketika dob adalah 29 Februari dan tahun ini bukan tahun kabisat.
Trey Hunner
4
Dapatkan jumlah hari, lalu bagi dengan 365,2425 (tahun rata-rata Gregorian) untuk tahun. Bagilah dengan 30,436875 (rata-rata bulan Gregorian) untuk bulan.
Saya sangat khawatir tentang tahun kabisat. Ini harus memeriksa apakah seseorang berusia di atas 18 tahun.
Migol
Maka tidak ada satu kalimat yang mudah, Anda harus mengurai kedua tanggal tersebut dan mencari tahu apakah orang tersebut telah melewati ulang tahun ke-18 atau belum.
Seseorang yang lahir pada tanggal 29 Februari akan dianggap telah mencapai usia 1 pada tanggal 28 Februari berikutnya.
John Machin
Baik. Dikoreksi untuk mengakomodasi 0,08% populasi yang lahir pada tanggal 29 dengan membalik tes dari "adalah ulang tahun setelah hari ini" menjadi "ulang tahun sebelum hari ini". Apakah itu menyelesaikannya?
John Mee
Ini berfungsi dengan benar untuk contoh Anda!?! Jika saya menetapkan "hari ini" ke 28 Februari 2009, dan tanggal lahir ke 29 Februari 2008 mengembalikan Nol - setidaknya untuk saya; bukan 1 seperti yang Anda sarankan (Python 2.5.2). Tidak perlu kasar, Tuan Machin. Tepatnya pada dua tanggal apa Anda bermasalah?
John Mee
Saya akan coba lagi: Seseorang yang lahir pada tanggal 29 Februari akan diperlakukan oleh kebanyakan orang untuk sebagian besar tujuan hukum karena telah mencapai usia 1 pada tanggal 28 Februari berikutnya. Kode Anda menghasilkan 0, baik sebelum dan sesudah "koreksi" Anda. Faktanya, dua versi kode Anda menghasilkan output yang PERSIS sama untuk semua 9 kemungkinan input (bulan <==> X hari <==>). BTW, 100.0 / (4 * 365 + 1) menghasilkan 0,068, bukan 0,08.
John Machin
2
Mendesah. (0) Mengatasi masalah 29 Februari penting dalam aritmatika tanggal apa pun; Anda hanya mengabaikannya. (1) Kode Anda tidak lebih baik pertama kali; Apa yang tidak Anda pahami dalam "menghasilkan output yang PERSIS sama"? (2) Tiga kemungkinan hasil atom (<, ==,>) membandingkan hari ini. Bulan dan bulan bulan; tiga kemungkinan hasil atom membandingkan hari ini.hari dan dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
John Machin
1
Namun lib pihak ketiga lain yang tidak disebutkan di sini adalah mxDateTime (pendahulu python datetimedan pihak ketiga timeutil) dapat digunakan untuk tugas ini.
Dan ya, menambahkan ketergantungan untuk tugas tunggal ini pasti akan menjadi berlebihan dibandingkan dengan menggunakan timeutil (disarankan oleh Rick Copeland).
Pada akhirnya yang Anda miliki adalah masalah matematika. Jika setiap 4 tahun kita memiliki satu hari ekstra, mari kita selami timedelta dalam beberapa hari, bukan 365 tetapi 365 * 4 + 1, itu akan memberi Anda jumlah 4 tahun. Kemudian bagi lagi dengan 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
Hal tahun kabisat tidak berlaku bila tahun habis habis 100, kecuali habis habis 400. Jadi, untuk tahun 2000: - habis dibagi empat, jadi harus lompatan tapi ... - bisa habis juga oleh seratus, jadi itu tidak harus lompatan tapi ... - itu habis dibagi 400, jadi itu sebenarnya tahun kabisat. Untuk 1900: - habis dibagi 4 jadi harus lompatan. - dapat dibagi 100, jadi tidak boleh lompatan. - TIDAK habis dibagi 400, jadi aturan ini tidak berlaku. 1900 bukanlah tahun kabisat.
Jblasco
1
Ini adalah solusi yang saya berhasil, saya harap dapat membantu ;-)
defmenor_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:
returnTrueelse:
returnFalseexcept Exception, ex_edad:
logging.error('Error menor de edad: %s' % ex_edad)
returnTrue
Meskipun utas ini sudah mati, bolehkah saya menyarankan solusi yang berfungsi untuk masalah yang sama yang saya hadapi ini. Ini dia (tanggal adalah string dalam format dd-mm-yyyy):
defvalidatedate(date):
parts = date.strip().split('-')
if len(parts) == 3andFalsenotin [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:
returnTruereturnFalse
fungsi ini mengembalikan perbedaan tahun antara dua tanggal (diambil sebagai string dalam format ISO, tetapi dapat dengan mudah dimodifikasi untuk menggunakan format apa pun)
import time
defyears(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 neededif ld < ed:
ld, ed = ed, ld
res = ld[0] - ed [0]
if res > 0:
if ld[1]< ed[1]:
res -= 1elif ld[1] == ed[1]:
if ld[2]< ed[2]:
res -= 1return res
Mengingat tujuan Python untuk menjadi bahasa skrip yang kuat dan mudah digunakan, fitur-fiturnya untuk bekerja dengan tanggal dan waktu tidak semudah yang seharusnya. Tujuan pyfdate adalah untuk memperbaiki situasi tersebut dengan menyediakan fitur untuk bekerja dengan tanggal dan waktu yang sama kuat dan mudah digunakannya seperti Python lainnya.
Tapi tidak ada contoh python dari metodenya tapi inilah yang akhirnya saya gunakan.
input: objek datetime
keluaran: usia integer dalam satu tahun penuh
defage(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 - 1return years
Saya menyukai solusi John Mee karena kesederhanaannya, dan saya tidak terlalu peduli tentang bagaimana, pada 28 Februari atau 1 Maret ketika ini bukan tahun kabisat, untuk menentukan usia orang yang lahir pada 29 Februari. Namun berikut ini adalah perubahan kodenya yang menurut saya mengatasi keluhan:
defage(dob):import datetime
today = datetime.date.today()
age = today.year - dob.year
if ( today.month == dob.month == 2and
today.day == 28and dob.day == 29 ):
passelif today.month < dob.month or \
(today.month == dob.month and today.day < dob.day):
age -= 1return age
Jawaban:
Anda membutuhkan lebih dari satu
timedelta
untuk mengetahui berapa tahun telah berlalu; Anda juga perlu mengetahui tanggal awal (atau akhir). (Ini adalah tahun kabisat.)Taruhan terbaik Anda adalah menggunakan
dateutil.relativedelta
objek , tapi itu adalah modul pihak ke-3. Jika Anda ingin mengetahuidatetime
bahwa itu adalahn
tahun dari suatu tanggal (default sekarang), Anda dapat melakukan hal berikut: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)
Jika Anda lebih suka menggunakan pustaka standar, jawabannya sedikit lebih rumit ::
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)
Jika 2/29, dan 18 tahun yang lalu tidak ada 2/29, fungsi ini akan mengembalikan 2/28. Jika Anda lebih suka mengembalikan 3/1, ubah saja
return
pernyataan terakhir menjadi:return from_date.replace(month=3, day=1, year=from_date.year-years)
Pertanyaan Anda awalnya mengatakan Anda ingin tahu sudah berapa tahun sejak suatu tanggal. Dengan asumsi Anda menginginkan bilangan bulat tahun, Anda dapat menebak berdasarkan 365,25 hari per tahun dan kemudian memeriksa menggunakan salah satu
yearsago
fungsi yang ditentukan di atas ::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
sumber
datetime.now()
) lebih besar daripada perbedaan antara tahun Julian ( ) dan Gregorian ( ) rata - rata .365.25
365.2425
. Pendekatan yang benar disarankan dalam jawaban @Adam RosenfieldJika Anda mencoba memeriksa apakah seseorang berusia 18 tahun, penggunaan
timedelta
tidak akan berfungsi dengan benar pada beberapa kasus edge karena tahun kabisat. Misalnya, seseorang yang lahir pada tanggal 1 Januari 2000, akan berusia 18 tahun tepat 6575 hari kemudian pada tanggal 1 Januari 2018 (termasuk 5 tahun kabisat), tetapi seseorang yang lahir pada tanggal 1 Januari 2001, akan berusia 18 tahun tepat 6574 hari kemudian pada tanggal 1 Januari, 2019 (termasuk 4 tahun kabisat). Jadi, jika seseorang berusia tepat 6574 hari, Anda tidak dapat menentukan apakah mereka berusia 17 atau 18 tahun tanpa mengetahui lebih banyak informasi tentang tanggal lahir mereka.Cara yang benar untuk melakukan ini adalah menghitung usia secara langsung dari tanggal, dengan mengurangi dua tahun, dan kemudian menguranginya jika bulan / hari saat ini mendahului bulan / hari kelahiran.
sumber
Pertama, pada tingkat yang paling mendetail, masalah tidak dapat diselesaikan dengan tepat. Panjang tahun bervariasi, dan tidak ada "pilihan tepat" yang jelas untuk lamanya tahun.
Meskipun demikian, dapatkan perbedaan dalam satuan apa pun yang "alami" (mungkin detik) dan bagi dengan rasio antara satuan tersebut dan tahun. Misalnya
delta_in_days / (365.25) delta_in_seconds / (365.25*24*60*60)
...atau terserah. Hindari berbulan-bulan, karena mereka bahkan kurang didefinisikan dengan baik daripada tahun.
sumber
Berikut adalah fungsi DOB yang diperbarui, yang menghitung ulang tahun dengan cara yang sama seperti manusia:
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)))
sumber
Dapatkan jumlah hari, lalu bagi dengan 365,2425 (tahun rata-rata Gregorian) untuk tahun. Bagilah dengan 30,436875 (rata-rata bulan Gregorian) untuk bulan.
sumber
Seberapa tepat Anda membutuhkannya?
td.days / 365.25
akan membuat Anda cukup dekat, jika Anda khawatir tentang tahun kabisat.sumber
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
sumber
Namun lib pihak ketiga lain yang tidak disebutkan di sini adalah mxDateTime (pendahulu python
datetime
dan pihak ketigatimeutil
) dapat digunakan untuk tugas ini.Yang tersebut di atas
yearsago
adalah: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)
Parameter pertama diharapkan menjadi sebuah
DateTime
instance.Untuk mengonversi biasa
datetime
menjadiDateTime
Anda dapat menggunakan ini selama 1 detik presisi):def DT_from_dt_s(t): return DT.DateTimeFromTicks(time.mktime(t.timetuple()))
atau ini untuk presisi 1 mikrodetik:
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)
Dan ya, menambahkan ketergantungan untuk tugas tunggal ini pasti akan menjadi berlebihan dibandingkan dengan menggunakan timeutil (disarankan oleh Rick Copeland).
sumber
Pada akhirnya yang Anda miliki adalah masalah matematika. Jika setiap 4 tahun kita memiliki satu hari ekstra, mari kita selami timedelta dalam beberapa hari, bukan 365 tetapi 365 * 4 + 1, itu akan memberi Anda jumlah 4 tahun. Kemudian bagi lagi dengan 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
sumber
Ini adalah solusi yang saya berhasil, saya harap dapat membantu ;-)
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
sumber
Meskipun utas ini sudah mati, bolehkah saya menyarankan solusi yang berfungsi untuk masalah yang sama yang saya hadapi ini. Ini dia (tanggal adalah string dalam format 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
sumber
fungsi ini mengembalikan perbedaan tahun antara dua tanggal (diambil sebagai string dalam format ISO, tetapi dapat dengan mudah dimodifikasi untuk menggunakan format apa pun)
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
sumber
Saya akan menyarankan Pyfdate
yang tutorial
sumber
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()
Ini adalah kode yang dijalankan oleh operator Carrousel di film Logan's Run;)
https://en.wikipedia.org/wiki/Logan%27s_Run_(film)
sumber
Saya menemukan pertanyaan ini dan menemukan jawaban Adams https://stackoverflow.com/a/765862/2964689 yang paling membantu
Tapi tidak ada contoh python dari metodenya tapi inilah yang akhirnya saya gunakan.
input: objek datetime
keluaran: usia integer dalam satu tahun penuh
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
sumber
Saya menyukai solusi John Mee karena kesederhanaannya, dan saya tidak terlalu peduli tentang bagaimana, pada 28 Februari atau 1 Maret ketika ini bukan tahun kabisat, untuk menentukan usia orang yang lahir pada 29 Februari. Namun berikut ini adalah perubahan kodenya yang menurut saya mengatasi keluhan:
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
sumber