所以我使用 python 来自动化一些我必须在 excel 中完成的重复性任务。我需要做的计算之一需要使用yearfrac()。这在Python中被复制了吗?
我找到了这个 但对于我尝试的值来说这是不正确的。
(从 12/19/2011 到 3/31/17,yearfrac() 给出 5.2833333333333300000,但链接中的 python 函数给出 5.2807978099335156。)
我发现 来自实际 Office 开发人员的电子邮件线程提供了
YEARFRAC
算法的 VBA 实现。
Public Function FIsLeapYear(Year As Integer) As Boolean
If (Year Mod 4) > 0 Then
FIsLeapYear = False
ElseIf (Year Mod 100) > 0 Then
FIsLeapYear = True
ElseIf (Year Mod 400) = 0 Then
FIsLeapYear = True
Else
FIsLeapYear = False
End If
End Function
Public Function FIsEndOfMonth(Day As Integer, Month As Integer, Year As Integer) As Boolean
Select Case Month
Case 1, 3, 5, 7, 8, 10, 12
FIsEndOfMonth = (Day = 31)
Case 4, 6, 9, 11
FIsEndOfMonth = (Day = 30)
Case 2
If FIsLeapYear(Year) Then
FIsEndOfMonth = (Day = 29)
Else
FIsEndOfMonth = (Day = 28)
End If
End Select
End Function
Public Function Days360(StartYear As Integer, EndYear As Integer, StartMonth As Integer, EndMonth As Integer, StartDay As Integer, EndDay As Integer) As Integer
Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay)
End Function
Public Function TmpDays360Nasd(StartDate As Date, EndDate As Date, Method As Integer, UseEom As Boolean) As Integer
Dim StartDay As Integer
Dim StartMonth As Integer
Dim StartYear As Integer
Dim EndDay As Integer
Dim EndMonth As Integer
Dim EndYear As Integer
StartDay = Day(StartDate)
StartMonth = Month(StartDate)
StartYear = Year(StartDate)
EndDay = Day(EndDate)
EndMonth = Month(EndDate)
EndYear = Year(EndDate)
If (EndMonth = 2 And FIsEndOfMonth(EndDay, EndMonth, EndYear)) And ((StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Or Method = 3) Then
EndDay = 30
End If
If EndDay = 31 And (StartDay >= 30 Or Method = 3) Then
EndDay = 30
End If
If StartDay = 31 Then
StartDay = 30
End If
If (UseEom And StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Then
StartDay = 30
End If
TmpDays360Nasd = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
End Function
Public Function TmpDays360Euro(StartDate As Date, EndDate As Date)
Dim StartDay As Integer
Dim StartMonth As Integer
Dim StartYear As Integer
Dim EndDay As Integer
Dim EndMonth As Integer
Dim EndYear As Integer
StartDay = Day(StartDate)
StartMonth = Month(StartDate)
StartYear = Year(StartDate)
EndDay = Day(EndDate)
EndMonth = Month(EndDate)
EndYear = Year(EndDate)
If (StartDay = 31) Then
StartDay = 30
End If
If (EndDay = 31) Then
EndDay = 30
End If
TmpDays360Euro = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
End Function
Public Function TmpDiffDates(StartDate As Date, EndDate As Date, Basis As Integer) As Integer
Select Case Basis
Case 0 'atpmBasis30360
TmpDiffDates = TmpDays360Nasd(StartDate, EndDate, 0, True)
Case 1, 2, 3 'atpmBasisActual atpmBasisActual360 atpmBasisActual365 -- use actual count of days
TmpDiffDates = DateDiff("d", StartDate, EndDate)
Case 4 'atpmBasisE30360
TmpDiffDates = TmpDays360Euro(StartDate, EndDate)
End Select
End Function
Public Function TmpCalcAnnualBasis(StartDate As Date, EndDate As Date, Basis As Integer) As Double
Dim StartDay As Integer
Dim StartMonth As Integer
Dim StartYear As Integer
Dim EndDay As Integer
Dim EndMonth As Integer
Dim EndYear As Integer
Dim iYear As Integer
Select Case Basis
Case 0, 2, 4 'atpmBasis30360 atpmBasisActual360 atpmBasisE30360
TmpCalcAnnualBasis = 360
Case 3 'atpmBasisActual365
TmpCalcAnnualBasis = 365
Case 1 ' atpmBasisActual
StartDay = Day(StartDate)
StartMonth = Month(StartDate)
StartYear = Year(StartDate)
EndDay = Day(EndDate)
EndMonth = Month(EndDate)
EndYear = Year(EndDate)
If (StartYear = EndYear) Then
If FIsLeapYear(StartYear) Then
TmpCalcAnnualBasis = 366
Else
TmpCalcAnnualBasis = 365
End If
ElseIf ((EndYear - 1) = StartYear) And ((StartMonth > EndMonth) Or ((StartMonth = EndMonth) And StartDay >= EndDay)) Then
If FIsLeapYear(StartYear) Then
If StartMonth < 2 Or (StartMonth = 2 And StartDay <= 29) Then
TmpCalcAnnualBasis = 366
Else
TmpCalcAnnualBasis = 365
End If
ElseIf FIsLeapYear(EndYear) Then
If EndMonth > 2 Or (EndMonth = 2 And EndDay = 29) Then
TmpCalcAnnualBasis = 366
Else
TmpCalcAnnualBasis = 365
End If
Else
TmpCalcAnnualBasis = 365
End If
Else
For iYear = StartYear To EndYear
If FIsLeapYear(iYear) Then
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
Else
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
End If
Next iYear
TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
End If
End Select
End Function
Public Function TmpYearFrac(StartDate As Date, EndDate As Date, Basis As Integer)
Dim nNumerator As Integer
Dim nDenom As Double
nNumerator = TmpDiffDates(StartDate, EndDate, Basis)
nDenom = TmpCalcAnnualBasis(StartDate, EndDate, Basis)
TmpYearFrac = nNumerator / nDenom
End Function
=end VBA source code for YearFrac
#-----------------------------------------------------------------------------
# Ruby version starts here, with VBA code in comment blocks for comparison ...
#-----------------------------------------------------------------------------
Public Function FIsLeapYear(Year As Integer) As Boolean
If (Year Mod 4) > 0 Then
FIsLeapYear = False
ElseIf (Year Mod 100) > 0 Then
FIsLeapYear = True
ElseIf (Year Mod 400) = 0 Then
FIsLeapYear = True
Else
FIsLeapYear = False
End If
End Function
Public Function FIsEndOfMonth(Day As Integer, Month As Integer, Year As Integer) As Boolean
Select Case Month
Case 1, 3, 5, 7, 8, 10, 12
FIsEndOfMonth = (Day = 31)
Case 4, 6, 9, 11
FIsEndOfMonth = (Day = 30)
Case 2
If FIsLeapYear(Year) Then
FIsEndOfMonth = (Day = 29)
Else
FIsEndOfMonth = (Day = 28)
End If
End Select
End Function
Public Function Days360(StartYear As Integer, EndYear As Integer, StartMonth As Integer, EndMonth As Integer, StartDay As Integer, EndDay As Integer) As Integer
Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay)
End Function
Public Function TmpDays360Nasd(StartDate As Date, EndDate As Date, Method As Integer, UseEom As Boolean) As Integer
Dim StartDay As Integer
Dim StartMonth As Integer
Dim StartYear As Integer
Dim EndDay As Integer
Dim EndMonth As Integer
Dim EndYear As Integer
StartDay = Day(StartDate)
StartMonth = Month(StartDate)
StartYear = Year(StartDate)
EndDay = Day(EndDate)
EndMonth = Month(EndDate)
EndYear = Year(EndDate)
If (EndMonth = 2 And FIsEndOfMonth(EndDay, EndMonth, EndYear)) And ((StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Or Method = 3) Then
EndDay = 30
End If
If EndDay = 31 And (StartDay >= 30 Or Method = 3) Then
EndDay = 30
End If
If StartDay = 31 Then
StartDay = 30
End If
If (UseEom And StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Then
StartDay = 30
End If
TmpDays360Nasd = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
End Function
Public Function TmpDays360Euro(StartDate As Date, EndDate As Date)
Dim StartDay As Integer
Dim StartMonth As Integer
Dim StartYear As Integer
Dim EndDay As Integer
Dim EndMonth As Integer
Dim EndYear As Integer
StartDay = Day(StartDate)
StartMonth = Month(StartDate)
StartYear = Year(StartDate)
EndDay = Day(EndDate)
EndMonth = Month(EndDate)
EndYear = Year(EndDate)
If (StartDay = 31) Then
StartDay = 30
End If
If (EndDay = 31) Then
EndDay = 30
End If
TmpDays360Euro = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
End Function
Public Function TmpDiffDates(StartDate As Date, EndDate As Date, Basis As Integer) As Integer
Select Case Basis
Case 0 'atpmBasis30360
TmpDiffDates = TmpDays360Nasd(StartDate, EndDate, 0, True)
Case 1, 2, 3 'atpmBasisActual atpmBasisActual360 atpmBasisActual365 -- use actual count of days
TmpDiffDates = DateDiff("d", StartDate, EndDate)
Case 4 'atpmBasisE30360
TmpDiffDates = TmpDays360Euro(StartDate, EndDate)
End Select
End Function
Public Function TmpCalcAnnualBasis(StartDate As Date, EndDate As Date, Basis As Integer) As Double
Dim StartDay As Integer
Dim StartMonth As Integer
Dim StartYear As Integer
Dim EndDay As Integer
Dim EndMonth As Integer
Dim EndYear As Integer
Dim iYear As Integer
Select Case Basis
Case 0, 2, 4 'atpmBasis30360 atpmBasisActual360 atpmBasisE30360
TmpCalcAnnualBasis = 360
Case 3 'atpmBasisActual365
TmpCalcAnnualBasis = 365
Case 1 ' atpmBasisActual
StartDay = Day(StartDate)
StartMonth = Month(StartDate)
StartYear = Year(StartDate)
EndDay = Day(EndDate)
EndMonth = Month(EndDate)
EndYear = Year(EndDate)
If (StartYear = EndYear) Then
If FIsLeapYear(StartYear) Then
TmpCalcAnnualBasis = 366
Else
TmpCalcAnnualBasis = 365
End If
ElseIf ((EndYear - 1) = StartYear) And ((StartMonth > EndMonth) Or ((StartMonth = EndMonth) And StartDay >= EndDay)) Then
If FIsLeapYear(StartYear) Then
If StartMonth < 2 Or (StartMonth = 2 And StartDay <= 29) Then
TmpCalcAnnualBasis = 366
Else
TmpCalcAnnualBasis = 365
End If
ElseIf FIsLeapYear(EndYear) Then
If EndMonth > 2 Or (EndMonth = 2 And EndDay = 29) Then
TmpCalcAnnualBasis = 366
Else
TmpCalcAnnualBasis = 365
End If
Else
TmpCalcAnnualBasis = 365
End If
Else
For iYear = StartYear To EndYear
If FIsLeapYear(iYear) Then
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
Else
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
End If
Next iYear
TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
End If
End Select
End Function
Public Function TmpYearFrac(StartDate As Date, EndDate As Date, Basis As Integer)
Dim nNumerator As Integer
Dim nDenom As Double
nNumerator = TmpDiffDates(StartDate, EndDate, Basis)
nDenom = TmpCalcAnnualBasis(StartDate, EndDate, Basis)
TmpYearFrac = nNumerator / nDenom
End Function
更深入地挖掘,我发现了一篇文章,它提供了一个看起来非常像 Python 的伪代码实现。没有时间测试它,下面是伪代码:
def appears_le_year(date1, date2):
# Returns True if date1 and date2 "appear" to be 1 year or less apart.
# This compares the values of year, month, and day directly to each other.
# Requires date1 <= date2; returns boolean. Used by basis 1.
if date1.year == date2.year:
return True
if (((date1.year + 1) == date2.year) and
((date1.month > date2.month) or
((date1.month == date2.month) and (date1.day >= date2.day)))):
return True
return False
def basis0(date1,date2):
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
if date1 == date2:
return 0.0
# Change day-of-month for purposes of calculation.
date1day, date1month, date1year = date1.day, date1.month, date1.year
date2day, date2month, date2year = date2.day, date2.month, date2.year
if (date1day == 31 and date2day == 31):
date1day = 30
date2day = 30
elif date1day == 31:
date1day = 30
elif (date1day == 30 and date2day == 31):
date2day = 30
# Note: If date2day==31, it STAYS 31 if date1day < 30.
# Special fixes for February:
elif (date1month == 2 and date2month == 2 and
last_day_of_month(date1) and
last_day_of_month(date2)):
date1day = 30 # Set the day values to be equal
date2day = 30
elif date1month == 2 and last_day_of_month(date1):
date1day = 30 # "Illegal" Feb 30 date.
daydiff360 = ((date2day + date2month * 30 + date2year * 360) -
(date1day + date1month * 30 + date1year * 360))
return daydiff360 / 360
def basis1(date1,date2):
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
if date1 == date2:
return 0.0
if appears_le_year(date1, date2):
if (date1.year == date2.year and is_leap_year(date1.year)):
year_length = 366.
elif (feb29_between(date1, date2) or
(date2.month == 2 and date2.day == 29)): # fixed, 2008-04-18
year_length = 366.
else:
year_length = 365.
return diffdays(date1, date2) / year_length
else:
num_years = (date2.year - date1.year) + 1
days_in_years = diffdays(date(date1.year, 1, 1), date(date2.year+1, 1, 1))
average_year_length = days_in_years / num_years
return diffdays(date1, date2) / average_year_length
def basis2(date1,date2):
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
return diffdays(date1, date2) / 360.
def basis3(date1,date2):
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
return diffdays(date1, date2) / 365.
def basis4(date1,date2):
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
if date1 == date2:
return 0.0
# Change day-of-month for purposes of calculation.
date1day, date1month, date1year = date1.day, date1.month, date1.year
date2day, date2month, date2year = date2.day, date2.month, date2.year
if date1day == 31:
date1day = 30
if date2day == 31:
date2day = 30
# Remarkably, do NOT change Feb. 28 or 29 at ALL.
daydiff360 = ( (date2day + date2month * 30 + date2year * 360) -
(date1day + date1month * 30 + date1year * 360))
return daydiff360 / 360
import datetime
def IsLeapYear(year):
if year % 4 > 0:
IsLeapYear = False
elif year % 100 > 0:
IsLeapYear = True
elif year % 400 == 0:
IsLeapYear = True
else:
IsLeapYear = False
return IsLeapYear
def IsEndOfMonth(day, month, year):
if month in [1,3,5,7,8,10,12]:
IsEndOfMonth = (day == 31)
if month in [4,6,9,11]:
IsEndOfMonth = (day == 30)
if month in [2]:
if IsLeapYear(year):
IsEndOfMonth = (day == 29)
else:
IsEndOfMonth = (day == 28)
return IsEndOfMonth
def Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay):
Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay)
return Days360
def TmpDays360Nasd(StartDate, EndDate, Method, UseEom):
StartDay = StartDate.day
StartMonth = StartDate.month
StartYear = StartDate.year
EndDay = EndDate.day
EndMonth = EndDate.month
EndYear = EndDate.year
if (EndMonth == 2 and IsEndOfMonth(EndDay, EndMonth, EndYear)) and ((StartMonth == 2 and IsEndOfMonth(StartDay, StartMonth, StartYear)) or Method == 3):
EndDay = 30
if EndDay == 31 and (StartDay >= 30 or Method == 3):
EndDay = 30
if StartDay == 31:
StartDay = 30
if (UseEom and StartMonth == 2 and IsEndOfMonth(StartDay, StartMonth, StartYear)):
StartDay = 30
return Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
def TmpDays360Euro(StartDate, EndDate):
StartDay = StartDate.day
StartMonth = StartDate.month
StartYear = StartDate.year
EndDay = EndDate.day
EndMonth = EndDate.month
EndYear = EndDate.year
if StartDay == 31:
StartDay = 30
if EndDay == 31:
EndDay = 30
return Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
def DateDiff(StartDate, EndDate):
return abs((StartDate - EndDate).days)
def TmpDiffDates(StartDate, EndDate, Basis):
if Basis in [0]:
TmpDiffDates = TmpDays360Nasd(StartDate, EndDate, 0, True)
elif Basis in [1,2,3]:
TmpDiffDates = DateDiff(StartDate, EndDate)
elif Basis in [4]:
TmpDiffDates = TmpDays360Euro(StartDate, EndDate)
return TmpDiffDates
def TmpCalcAnnualBasis(StartDate, EndDate, Basis):
if Basis in [0,2,4]:
TmpCalcAnnualBasis = 360
elif Basis in [3]:
TmpCalcAnnualBasis = 365
elif Basis in [1]:
StartDay = StartDate.day
StartMonth = StartDate.month
StartYear = StartDate.year
EndDay = EndDate.day
EndMonth = EndDate.month
EndYear = EndDate.year
if StartYear == EndYear:
if IsLeapYear(StartYear):
TmpCalcAnnualBasis = 366
else:
TmpCalcAnnualBasis = 365
elif (EndYear - 1) == StartYear and (StartMonth > EndMonth or (StartMonth == EndMonth and StartDay >= EndDay)):
if IsLeapYear(StartYear):
if StartMonth < 2 or (StartMonth == 2 and StartDay <= 29):
TmpCalcAnnualBasis = 366
else:
TmpCalcAnnualBasis = 365
elif IsLeapYear(EndYear):
if EndMonth > 2 or (EndMonth == 2 and EndDay == 29):
TmpCalcAnnualBasis = 366
else:
TmpCalcAnnualBasis = 365
else:
TmpCalcAnnualBasis = 365
else:
TmpCalcAnnualBasis = 0
for iYear in range(StartYear, EndYear + 1 ):
if IsLeapYear(iYear):
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
else:
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
return TmpCalcAnnualBasis
def YearFrac(StartDate, EndDate, Basis):
Numerator = TmpDiffDates(StartDate, EndDate, Basis)
Denom = TmpCalcAnnualBasis(StartDate, EndDate, Basis)
YearFrac = Numerator/Denom
return YearFrac
# TESTING
start = datetime.datetime(1978, 2, 28)
end = datetime.datetime(2020, 5, 17)
x0 = YearFrac(start, end, 0)
x1 = YearFrac(start, end, 1)
x2 = YearFrac(start, end, 2)
x3 = YearFrac(start, end, 3)
x4 = YearFrac(start, end, 4)
Excel 的 YEARFRAC 有多种模式 (
basis
)。如果您将 basis
设置为 0 或省略,则默认使用 US NASD 30/360 计算。
您可以在 python 中执行此操作,因为该公式很容易遵循下面的等式
这是一个例子
In [1]: import datetime as dt
In [2]: d1 = dt.date(2015, 12, 19)
In [3]: d2 = dt.date(2020, 10, 19)
In [4]: def date_factor(date1, date2):
...: """
...: This formula follows the US (NASD) calculation
...: that is used in Excel's YEARFRAC by default
...: https://www.fundmanagersoftware.com/help/def_accrued_interest.html
...: """
...: num = (360 * (date2.year - date1.year) + (30*(date2.month - date1.month) + (date2.day - date1.day)))
...: return num / 360
...:
In [5]: date_factor(d1, d2)
Out[5]: 4.833333333333333
In [6]:
与 Excel 匹配
这里是来自所提供的伪代码的 Python 代码的完整实现。它需要日期时间库。输入是日期时间对象。
### YEARFRAC() ###
from datetime import datetime
def appears_le_year(date1, date2):
## Used by basis 1 ##
# Returns True if date1 and date2 "appear" to be 1 year or less apart.
# This compares the values of year, month, and day directly to each other.
# Requires date1 <= date2; returns boolean.
if date1.year == date2.year:
return True
if (((date1.year + 1) == date2.year) and
((date1.month > date2.month) or
((date1.month == date2.month) and (date1.day >= date2.day)))):
return True
return False
def is_leap_year(year):
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def diffdays(date1, date2):
return abs((date2 - date1).days)
def feb29_between(date1, date2):
year1 = date1.year
year2 = date2.year
if not year1 < year2:
year1, year2 = year2, year1
if year1 == year2:
if not is_leap_year(year1):
return False
else:
feb29 = datetime.strptime(str(year1) + '-02-29', '%Y-%m-%d')
return (date1 <= feb29 and date2 >= feb29)
else:
month1 = date1.month
month2 = date2.month
if month1 > 2:
if month2 < 2:
return False
elif month2 == 2:
return date2.day == 29
else:
return True
else:
return is_leap_year(year1)
def last_day_of_month(date):
next_month = date.replace(day=28) + timedelta(days=4)
last_day = next_month - timedelta(days=next_month.day)
return date == last_day
def basis0(date1, date2): #US 30360
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
if date1 == date2:
return 0.0
# Change day-of-month for purposes of calculation.
date1day, date1month, date1year = date1.day, date1.month, date1.year
date2day, date2month, date2year = date2.day, date2.month, date2.year
if (date1day == 31 and date2day == 31):
date1day = 30
date2day = 30
elif date1day == 31:
date1day = 30
elif (date1day == 30 and date2day == 31):
date2day = 30
# Note: If date2day==31, it STAYS 31 if date1day < 30.
# Special fixes for February:
elif (date1month == 2 and date2month == 2 and last_day_of_month(date1) and last_day_of_month(date2)):
date1day = 30 # Set the day values to be equal
date2day = 30
elif date1month == 2 and last_day_of_month(date1):
date1day = 30 # "Illegal" Feb 30 date.
daydiff360 = ((date2day + date2month * 30 + date2year * 360) - (date1day + date1month * 30 + date1year * 360))
return daydiff360 / 360
def basis1(date1,date2): #Actual/actual
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
if date1 == date2:
return 0.0
if appears_le_year(date1, date2):
if (date1.year == date2.year and is_leap_year(date1.year)):
year_length = 366.
elif (feb29_between(date1, date2) or (date2.month == 2 and date2.day == 29)): # fixed, 2008-04-18
year_length = 366.
else:
year_length = 365.
return diffdays(date1, date2) / year_length
else:
num_years = (date2.year - date1.year) + 1
days_in_years = diffdays(date(date1.year, 1, 1), date(date2.year+1, 1, 1))
average_year_length = days_in_years / num_years
return diffdays(date1, date2) / average_year_length
def basis2(date1,date2): #Actual/360
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
return diffdays(date1, date2) / 360.
def basis3(date1,date2): #Actual/365
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
return diffdays(date1, date2) / 365.
def basis4(date1,date2): #EU 30360
# Swap so date1 <= date2 in all cases:
if date1 > date2:
date1, date2 = date2, date1
if date1 == date2:
return 0.0
# Change day-of-month for purposes of calculation.
date1day, date1month, date1year = date1.day, date1.month, date1.year
date2day, date2month, date2year = date2.day, date2.month, date2.year
if date1day == 31:
date1day = 30
if date2day == 31:
date2day = 30
# Remarkably, do NOT change Feb. 28 or 29 at ALL.
daydiff360 = ( (date2day + date2month * 30 + date2year * 360) - (date1day + date1month * 30 + date1year * 360))
return daydiff360 / 360
使用示例:
date1 = datetime.strptime('2000-01-01', '%Y-%m-%d')
date2 = datetime.strptime('2000-06-06', '%Y-%m-%d')
print(basis0(date1, date2),
basis1(date1, date2),
basis2(date1, date2),
basis3(date1, date2),
basis4(date1, date2))
`appears_le_year <- function(date1, date2) {
if (year(date1) == year(date2)) {
return(TRUE)
}
if (((year(date1) + 1) == year(date2)) &&
((month(date1) > month(date2)) ||
((month(date1) == month(date2)) && (mday(date1) >= mday(date2))))) {
return(TRUE)
}
return(FALSE)
}
is_leap_year <- function(year) {
return(year %% 4 == 0 && (year %% 100 != 0 || year %% 400 == 0))
}
diffdays <- function(date1, date2) {
return(abs(as.numeric(date2 - date1, units = "days")))
}
feb29_between <- function(date1, date2) {
year1 <- year(date1)
year2 <- year(date2)
if (!(year1 < year2)) {
year1 <- year2
year2 <- year(date1)
}
if (year1 == year2) {
if (!is_leap_year(year1)) {
return(FALSE)
} else {
feb29 <- as.Date(paste(year1, '-02-29'))
return(date1 <= feb29 & date2 >= feb29)
}
} else {
month1 <- month(date1)
month2 <- month(date2)
if (month1 > 2) {
if (month2 < 2) {
return(FALSE)
} else if (month2 == 2) {
return(mday(date2) == 29)
} else {
return(TRUE)
}
} else {
return(is_leap_year(year1))
}
}
}
last_day_of_month <- function(date) {
next_month <- date + months(1)
last_day <- next_month - days(mday(next_month))
return(date == last_day)
}
YearFracBasis0 <- function(date1, date2) {
if (date1 > date2) {
temp <- date1
date1 <- date2
date2 <- temp
}
if (date1 == date2) {
return(0.0)
}
date1day <- mday(date1)
date1month <- month(date1)
date1year <- year(date1)
date2day <- mday(date2)
date2month <- month(date2)
date2year <- year(date2)
if ((date1day == 31 & date2day == 31)) {
date1day <- 30
date2day <- 30
} else if (date1day == 31) {
date1day <- 30
} else if ((date1day == 30 & date2day == 31)) {
date2day <- 30
}
if ((month(date1) == 2 & month(date2) == 2 & last_day_of_month(date1) & last_day_of_month(date2))) {
date1day <- 30
date2day <- 30
} else if (month(date1) == 2 & last_day_of_month(date1)) {
date1day <- 30
}
daydiff360 <- ((date2day + date2month * 30 + date2year * 360) -
(date1day + date1month * 30 + date1year * 360))
return(daydiff360 / 360)
}
YearFracBasis1 <- function(date1, date2) {
if (date1 > date2) {
temp <- date1
date1 <- date2
date2 <- temp
}
if (date1 == date2) {
return(0.0)
}
if (appears_le_year(date1, date2)) {
if ((year(date1) == year(date2) & is_leap_year(year(date1)))) {
year_length <- 366
} else if (feb29_between(date1, date2) || (month(date2) == 2 & mday(date2) == 29)) {
year_length <- 366
} else {
year_length <- 365
}
return(diffdays(date1, date2) / year_length)
} else {
num_years <- (year(date2) - year(date1)) + 1
days_in_years <- diffdays(as.Date(paste(year(date1), '01-01')), as.Date(paste(year(date2) + 1, '01-01')))
average_year_length <- days_in_years / num_years
return(diffdays(date1, date2) / average_year_length)
}
}
YearFracBasis2 <- function(date1, date2) {
if (date1 > date2) {
temp <- date1
date1 <- date2
date2 <- temp
}
return(diffdays(date1, date2) / 360)
}
YearFracBasis3 <- function(date1, date2) {
if (date1 > date2) {
temp <- date1
date1 <- date2
date2 <- temp
}
return(diffdays(date1, date2) / 365)
}
YearFracBasis4 <- function(date1, date2) {
if (date1 > date2) {
temp <- date1
date1 <- date2
date2 <- temp
}
if (date1 == date2) {
return(0.0)
}
date1day <- mday(date1)
date1month <- month(date1)
date1year <- year(date1)
date2day <- mday(date2)
date2month <- month(date2)
date2year <- year(date2)
if (date1day == 31) {
date1day <- 30
}
if (date2day == 31) {
date2day <- 30
}
daydiff360 <- ((date2day + date2month * 30 + date2year * 360) -
(date1day + date1month * 30 + date1year * 360))
return(daydiff360 / 360)
}`