多年来两个日期之间的Pythonic差异?

时间:2010-12-14 07:50:21

标签: python datetime

下面有更有效的方法吗?我希望将两个日期之间的年份差异作为单个标量。欢迎任何建议。

from datetime import datetime
start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference  = end_date - start_date
difference_in_years = (difference.days + difference.seconds/86400)/365.2425

15 个答案:

答案 0 :(得分:60)

如果您想要精确的结果,我建议使用dateutil库。

from dateutil.relativedelta import relativedelta
difference_in_years = relativedelta(end_date, start_date).years

这是完整的年份(例如一个人的年龄)。如果你想要小数年,那么加上几个月,几天,几小时......直到达到预期的精度。

答案 1 :(得分:11)

我使用其中一个来计算人的年龄:

uninsdeletekey

答案 2 :(得分:7)

效率更高?不,但更正确,可能。但这取决于你想要的正确程度。日期不是一件小事。

年份不长。你想要闰年或正常年份的差异吗? :-)按照你的计算,你总会得到一个稍微不正确的答案。多年的一天多长时间?你说1 / 365.2425。嗯,是的,平均超过一千年,是的。但不是。

所以问题并没有多大意义。

为了正确,你必须这样做:

from datetime import datetime
from calendar import isleap
start_date = datetime(2005,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
diffyears = end_date.year - start_date.year
difference  = end_date - start_date.replace(end_date.year)
days_in_year = isleap(end_date.year) and 366 or 365
difference_in_years = diffyears + (difference.days + difference.seconds/86400.0)/days_in_year

在这种情况下,这是相差0.0012322917425568528年,或0.662天,考虑到这不是闰年。

(然后我们忽略了微秒。嘿。)

答案 3 :(得分:7)

这样做:

from dateutil.relativedelta import relativedelta

myBirthday = datetime.datetime(1983,5,20,0,0,0,0)
now = datetime.datetime.now()



difference = relativedelta(now, myBirthday)
print("My years: "+str(difference.years))

答案 4 :(得分:5)

为了理解闰年,你几乎被迫把它分成两部分:整数年,一部分。两者都需要处理闰年,但需要以不同的方式 - 处理2月29日开始日期的整体需求,而小数必须处理一年中不同的天数。您希望小数部分以相等的数量递增,直到它在下一个周年日等于1.0,因此它应该基于结束日期之后的年份中的天数。

您希望您的日期范围包括1900或2100吗?如果你不这样做,事情会变得容易些。

<小时/> 编辑:我花了很长时间来解释这一点。基本问题是日历年不是一个恒定的大小,但你通过将它们设置为1.0来强迫它们保持不变。你提出的任何解决方案都会因此而出现异常,你将不得不选择你可以使用的异常。 John Machin是对的。

2008-02-28和2009-02-28之间有什么区别?大多数人都会同意这应该是1。0年。 2008-03-01和2009-03-01之间的区别怎么样?同样,大多数人都会同意它应该正好是1。0年。如果您选择将日期表示为年份加上基于当天的一年中的一小部分,则无法使这两个陈述成立。原始代码的情况就是这样,假设一天是一年的1/365.2425,或者实际上对于假设每天一年的常数分数的任何代码,即使一天的大小占了飞跃的年份年。

我断言你需要将其分解为整数年和分数年,这是试图解决这个问题。如果您将之前的每个条件都视为一个完整的年份,那么您所要做的就是决定分配给剩余天数的分数。这个方案的问题在于你仍然无法理解(date2-date1)+ date3,因为无法将分数解析回具有任何一致性的一天。

因此,我提议另一种编码,基于每年包含366天,无论是否是闰年。异常将首先是从2月29日开始的日期不是一年(或2或3) - “对不起约翰尼,你今年没有生日,没有2月29日”总是可以接受的。其次,如果你试图强迫这样的数字回到某个日期,你必须考虑非闰年并检查2月29日的特殊情况并将其转换,可能是3月1日。

from datetime import datetime
from datetime import timedelta
from calendar import isleap

size_of_day = 1. / 366.
size_of_second = size_of_day / (24. * 60. * 60.)

def date_as_float(dt):
    days_from_jan1 = dt - datetime(dt.year, 1, 1)
    if not isleap(dt.year) and days_from_jan1.days >= 31+28:
        days_from_jan1 += timedelta(1)
    return dt.year + days_from_jan1.days * size_of_day + days_from_jan1.seconds * size_of_second

start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference_in_years = date_as_float(end_time) - date_as_float(start_time)

我并不是说这是 解决方案,因为我认为不可能有完美的解决方案。但它有一些理想的属性:

  • 具有相同月份,日期和时间的任何日期之间的差异将是确切的年数。
  • 将差异添加到另一个日期将产生一个可以转换回有用日期的值。

答案 5 :(得分:2)

我认为您正在寻找的是

difference_in_years = difference.dt.days / 365.25

答案 6 :(得分:1)

这里是Kostyantyn在他的&#34; age2&#34;中发布的内容。功能。它略短/更清洁,并使用了&#34; age&#34;的传统/口语含义。或者多年的差异:

def ageInYears( d ):
    today = datetime.date.today()
    currentYrAnniversary = datetime.date( today.year, d.month, d.day )
    return (today.year - d.year) - (1 if today < currentYrAnniversary else 0)

答案 7 :(得分:0)

如果你的意思是代码空间有效,那么不,这是最有效的方法。

答案 8 :(得分:0)

这是我想出的,无需使用外部依赖项:

def year_diff(d1, d2):
    """Returns the number of years between the dates as a positive integer."""
    later = max(d1, d2)
    earlier = min(d1, d2)

    result = later.year - earlier.year
    if later.month < earlier.month or (later.month == earlier.month and later.day < earlier.day):
        result -= 1

    return result

答案 9 :(得分:0)

更强大的功能-计算年份(天数)和天数之间的差异:

def get_diff_in_years_and_days(from_date, to_date):
    try:
        from_in_this_year = date(to_date.year, from_date.month, from_date.day)
    except:
        from_in_this_year = date(to_date.year, from_date.month, from_date.day-1) # today is feb in leap year

    if from_in_this_year <= to_date:
        years = to_date.year - from_date.year
        days = (to_date - from_in_this_year).days
    else:
        years = to_date.year - from_date.year - 1
        try:
            from_in_prev_year = date(to_date.year-1, from_date.month, from_date.day)
        except:
            from_in_prev_year = date(to_date.year-1, from_date.month, from_date.day-1) # today is feb in leap year
        days = (to_date - from_in_prev_year).days

    assert days>=0 and days<=365, days
    assert years>=0, years

    return years, days

一些单元测试:

self.assertEqual((0,  0), get_diff_in_years_and_days(date(2018,1, 1), date(2018,1, 1)))
self.assertEqual((1,  0), get_diff_in_years_and_days(date(2017,1, 1), date(2018,1, 1)))
self.assertEqual((1,  1), get_diff_in_years_and_days(date(2017,1, 1), date(2018,1, 2)))
self.assertEqual((2,  0), get_diff_in_years_and_days(date(2016,2,29), date(2018,2,28)))
self.assertEqual((2,  1), get_diff_in_years_and_days(date(2014,2,28), date(2016,2,29)))
self.assertEqual((1,364), get_diff_in_years_and_days(date(2014,2,28), date(2016, 2,27)))
self.assertEqual((3,30) , get_diff_in_years_and_days(date(2015,10,1), date(2018,10,31)))
self.assertEqual((10,30), get_diff_in_years_and_days(date(2010,10,1), date(2020,10,31)))
self.assertEqual((3,31) , get_diff_in_years_and_days(date(2015,10,1), date(2018,11, 1)))
self.assertEqual((2,364), get_diff_in_years_and_days(date(2015,10,1), date(2018, 9,30)))

答案 10 :(得分:0)

由于我们即将到2018的结尾...

from dateutil import parser
from dateutil.relativedelta import relativedelta

rip = [
    ["Tim Bergling\t\t",         " 8 Sep 1989", "20 Apr 2018"], # Avicii Swedish musician
    ["Stephen Hillenburg\t",     "21 Aug 1961", "26 Nov 2018"], # Creator of Spongebob
    ["Stephen Hawking\t\t",      " 8 Jan 1942", "14 Mar 2018"], # Theoretical physicist
    ["Stan Lee\t\t",             "28 Dec 1922", "12 Nov 2018"], # American comic book writer
    ["Stefán Karl Stefánsson\t", "10 Jul 1975", "21 Aug 2018"]  # Robbie Rotten from LazyTown
    ]

for name,born,died in rip:
    print("%s %s\t %s\t died at %i"%(name,born,died,relativedelta(parser.parse(died),parser.parse(born)).years))

输出

Tim Bergling              8 Sep 1989     20 Apr 2018     died at 28
Stephen Hillenburg       21 Aug 1961     26 Nov 2018     died at 57
Stephen Hawking           8 Jan 1942     14 Mar 2018     died at 76
Stan Lee                 28 Dec 1922     12 Nov 2018     died at 95
Stefán Karl Stefánsson   10 Jul 1975     21 Aug 2018     died at 43

答案 11 :(得分:0)

在安装库之前:

php artisan queue:work

在Cmder中的Python的一行中(windows):

choco upgrade Python -y
python pip install python-dateutil

使用%%的批量转义百分比:

python -c "import datetime; from dateutil.relativedelta import relativedelta; myBirthday = datetime.datetime(2019,2,6,11,0,0,0); now = datetime.datetime.utcnow(); diff = relativedelta(now, myBirthday); print ("'My'+'" "'+'year'+'" "'+':'+'" "'+'%d''" "''and''" "''%d''" "''microseconds'" % (diff.years, diff.microseconds))"

答案 12 :(得分:0)

BuvinJ 解决方案的更新,这是对 Kostyantyn 解决方案 age2() 的更新。

如果出生日期在闰日并且当前年份不是闰年,则这些函数将失败。尝试创建“currentYrAnniversary”时,日期时间库将抛出异常。

不如试试,

from datetime import date

def ageInYears( d ):
    today = datetime.date.today()

    notQuiteYourBirthday = (today.month, today.day) < (born.month, born.day)
    return (today.year - d.year) - notQuiteYourBirthday

答案 13 :(得分:0)

如果您已经将 DOB 作为字符串,您可以这样做:

from datetime import datetime as dt

def get_age(dob_str):
    now_str = dt.strftime(dt.utcnow(), '%Y-%m-%d')
    return int(now_str[:4]) - int(dob_str[:4]) - int(dob_str[5:] > now_str[5:])

或者如果你想写一些单元测试,设置 now_str 作为命名参数:

from datetime import datetime as dt

def get_age(dob_str, now_str=dt.strftime(dt.utcnow(), '%Y-%m-%d')):
    return int(now_str[:4]) - int(dob_str[:4]) - int(dob_str[5:] > now_str[5:])

答案 14 :(得分:0)

最简单的解决方案

from datetime import datetime

my_birthday = datetime(2000, 1, 1, 0, 0, 0, 0 )

now = datetime.now()

age = now.year - my_birthday.year

print(age)