所以我使用python来自动化一些我必须在excel中完成的重复任务。我需要做的一个计算需要使用yearfrac()。这在python中被复制了吗?我找到了this,但它与我尝试的值不正确。(From 2011年12月19日至2017年3月31日,yearfrac()给出5.283333333333300000,但链接中的python函数给出5.2807978099335156。
zzlelutf1#
我发现an e-mail thread from actual Office developers提供了YEARFRAC算法的一个可扩展实现。
YEARFRAC
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 IfEnd FunctionPublic 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 SelectEnd FunctionPublic 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 FunctionPublic 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 FunctionPublic 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 FunctionPublic 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 SelectEnd FunctionPublic 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 SelectEnd FunctionPublic 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 / nDenomEnd 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 IfEnd FunctionPublic 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 SelectEnd FunctionPublic 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 FunctionPublic 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 FunctionPublic 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 FunctionPublic 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 SelectEnd FunctionPublic 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 SelectEnd FunctionPublic 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 / nDenomEnd Function
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
Else
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)
FIsEndOfMonth = (Day = 28)
End Select
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)
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
If EndDay = 31 And (StartDay >= 30 Or Method = 3) Then
If StartDay = 31 Then
StartDay = 30
If (UseEom And StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Then
TmpDays360Nasd = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
Public Function TmpDays360Euro(StartDate As Date, EndDate As Date)
If (StartDay = 31) Then
If (EndDay = 31) Then
TmpDays360Euro = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
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)
Public Function TmpCalcAnnualBasis(StartDate As Date, EndDate As Date, Basis As Integer) As Double
Dim iYear As Integer
Case 0, 2, 4 'atpmBasis30360 atpmBasisActual360 atpmBasisE30360
TmpCalcAnnualBasis = 360
Case 3 'atpmBasisActual365
TmpCalcAnnualBasis = 365
Case 1 ' atpmBasisActual
If (StartYear = EndYear) Then
If FIsLeapYear(StartYear) Then
TmpCalcAnnualBasis = 366
ElseIf ((EndYear - 1) = StartYear) And ((StartMonth > EndMonth) Or ((StartMonth = EndMonth) And StartDay >= EndDay)) Then
If StartMonth < 2 Or (StartMonth = 2 And StartDay <= 29) Then
ElseIf FIsLeapYear(EndYear) Then
If EndMonth > 2 Or (EndMonth = 2 And EndDay = 29) Then
For iYear = StartYear To EndYear
If FIsLeapYear(iYear) Then
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
Next iYear
TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
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 VBA source code for YearFrac
#-----------------------------------------------------------------------------
# Ruby version starts here, with VBA code in comment blocks for comparison ...
字符串深入挖掘,我found an article提供了一个伪代码实现,看起来很像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 Falsedef 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 / 360def 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_lengthdef 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
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 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:
elif (date1day == 30 and date2day == 31):
# 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
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):
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
else:
year_length = 365.
return diffdays(date1, date2) / year_length
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):
return diffdays(date1, date2) / 360.
def basis3(date1,date2):
return diffdays(date1, date2) / 365.
def basis4(date1,date2):
if date1day == 31:
if date2day == 31:
# Remarkably, do NOT change Feb. 28 or 29 at ALL.
daydiff360 = ( (date2day + date2month * 30 + date2year * 360) -
型
dvtswwa32#
import datetimedef IsLeapYear(year): if year % 4 > 0: IsLeapYear = False elif year % 100 > 0: IsLeapYear = True elif year % 400 == 0: IsLeapYear = True else: IsLeapYear = False return IsLeapYeardef 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 IsEndOfMonthdef Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay): Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay) return Days360def 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 TmpDiffDatesdef 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 TmpCalcAnnualBasisdef 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)
import datetime
def IsLeapYear(year):
if year % 4 > 0:
IsLeapYear = False
elif year % 100 > 0:
IsLeapYear = True
elif year % 400 == 0:
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)
IsEndOfMonth = (day == 28)
return IsEndOfMonth
def Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay):
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):
if EndDay == 31 and (StartDay >= 30 or Method == 3):
if StartDay == 31:
if (UseEom and StartMonth == 2 and IsEndOfMonth(StartDay, StartMonth, StartYear)):
return Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
def TmpDays360Euro(StartDate, EndDate):
if EndDay == 31:
def DateDiff(StartDate, EndDate):
return abs((StartDate - EndDate).days)
def TmpDiffDates(StartDate, EndDate, Basis):
if Basis in [0]:
elif Basis in [1,2,3]:
TmpDiffDates = DateDiff(StartDate, EndDate)
elif Basis in [4]:
return TmpDiffDates
def TmpCalcAnnualBasis(StartDate, EndDate, Basis):
if Basis in [0,2,4]:
elif Basis in [3]:
elif Basis in [1]:
if StartYear == EndYear:
if IsLeapYear(StartYear):
elif (EndYear - 1) == StartYear and (StartMonth > EndMonth or (StartMonth == EndMonth and StartDay >= EndDay)):
if StartMonth < 2 or (StartMonth == 2 and StartDay <= 29):
elif IsLeapYear(EndYear):
if EndMonth > 2 or (EndMonth == 2 and EndDay == 29):
TmpCalcAnnualBasis = 0
for iYear in range(StartYear, EndYear + 1 ):
if IsLeapYear(iYear):
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)
字符串
qybjjes13#
Excel的YEARFRAC有多种模式(basis)。如果您将basis设置为0或省略,则默认使用US NASD 30/360计算。你可以在python中这样做,因为这个公式很容易遵循下面的公式
basis
的数据这里有一个例子
In [1]: import datetime as dtIn [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.833333333333333In [6]:
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匹配
的
cyej8jka4#
下面是Python代码的完整实现,来自提供的伪代码。它需要datetime库。输入是datetime对象。
### YEARFRAC() ###from datetime import datetimedef 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 Falsedef 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_daydef 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 / 360def 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_lengthdef 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
### YEARFRAC() ###
from datetime import datetime
## Used by basis 1 ##
# Requires date1 <= date2; returns boolean.
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):
feb29 = datetime.strptime(str(year1) + '-02-29', '%Y-%m-%d')
return (date1 <= feb29 and date2 >= feb29)
month1 = date1.month
month2 = date2.month
if month1 > 2:
if month2 < 2:
elif month2 == 2:
return date2.day == 29
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
elif (date1month == 2 and date2month == 2 and last_day_of_month(date1) and last_day_of_month(date2)):
daydiff360 = ((date2day + date2month * 30 + date2year * 360) - (date1day + date1month * 30 + date1year * 360))
def basis1(date1,date2): #Actual/actual
elif (feb29_between(date1, date2) or (date2.month == 2 and date2.day == 29)): # fixed, 2008-04-18
def basis2(date1,date2): #Actual/360
def basis3(date1,date2): #Actual/365
def basis4(date1,date2): #EU 30360
daydiff360 = ( (date2day + date2month * 30 + date2year * 360) - (date1day + date1month * 30 + date1year * 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))
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))
ar5n3qh55#
`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)}`
`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(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)) {
} else {
feb29 <- as.Date(paste(year1, '-02-29'))
return(date1 <= feb29 & date2 >= feb29)
month1 <- month(date1)
month2 <- month(date2)
if (month1 > 2) {
if (month2 < 2) {
} else if (month2 == 2) {
return(mday(date2) == 29)
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) {
} else if ((date1day == 30 & date2day == 31)) {
if ((month(date1) == 2 & month(date2) == 2 & last_day_of_month(date1) & last_day_of_month(date2))) {
} else if (month(date1) == 2 & last_day_of_month(date1)) {
daydiff360 <- ((date2day + date2month * 30 + date2year * 360) -
return(daydiff360 / 360)
YearFracBasis1 <- function(date1, date2) {
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 <- 365
return(diffdays(date1, date2) / year_length)
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) {
return(diffdays(date1, date2) / 360)
YearFracBasis3 <- function(date1, date2) {
return(diffdays(date1, date2) / 365)
YearFracBasis4 <- function(date1, date2) {
if (date1day == 31) {
if (date2day == 31) {
}`
5条答案
按热度按时间zzlelutf1#
我发现an e-mail thread from actual Office developers提供了
YEARFRAC
算法的一个可扩展实现。字符串
深入挖掘,我found an article提供了一个伪代码实现,看起来很像Python。没有时间测试它,下面是伪代码:
型
dvtswwa32#
字符串
qybjjes13#
Excel的YEARFRAC有多种模式(
basis
)。如果您将basis
设置为0或省略,则默认使用US NASD 30/360计算。你可以在python中这样做,因为这个公式很容易遵循下面的公式
的数据
这里有一个例子
字符串
与Excel匹配
的
cyej8jka4#
下面是Python代码的完整实现,来自提供的伪代码。它需要datetime库。输入是datetime对象。
字符串
使用示例:
型
ar5n3qh55#
字符串