为了理解闰年,你几乎不得不将其分为两部分:整数年和小数部分。两者都需要处理闰年,但处理方式不同——整数部分需要处理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,或者对于任何假定每天恒定的一年的一部分的代码,即使一天的大小考虑了闰年。
我断言您需要将其分解为整数年和小数年,是试图解决此问题的尝试。如果您将每个先前的条件视为整数年,则只需决定要分配给任何剩余天数的分数即可。这种方案的问题在于,仍然无法理解(日期2-日期1)+日期3,因为分数无法以任何一致的方式解析回一天。
因此,我提出了另一种编码,基于每年包含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)
我并不是在说这是唯一的解决方案,因为我认为完美的解决方案是不存在的。但它具有一些可取之处:
- 任何相同月份、相同日期和时间的两个日期之间的差值将是一个确切的年数。
- 将一个差值添加到另一个日期将得到一个可以转换回有用日期的值。
difference_in_years = (difference.days + difference.seconds/86400.0)/365.2425
,以在 Python 2.X 中运行时得到预期的答案。 - John Machinhttps://pumas.nasa.gov/files/04_21_97_1.pdf
- Marichyasana