在Python中从Excel复制YEARFRAC()函数

w9apscun  于 2023-11-20  发布在  Python
关注(0)|答案(5)|浏览(155)

所以我使用python来自动化一些我必须在excel中完成的重复任务。我需要做的一个计算需要使用yearfrac()。这在python中被复制了吗?
我找到了this,但它与我尝试的值不正确。
(From 2011年12月19日至2017年3月31日,yearfrac()给出5.283333333333300000,但链接中的python函数给出5.2807978099335156。

zzlelutf

zzlelutf1#

我发现an e-mail thread from actual Office developers提供了YEARFRAC算法的一个可扩展实现。

  1. Public Function FIsLeapYear(Year As Integer) As Boolean
  2. If (Year Mod 4) > 0 Then
  3. FIsLeapYear = False
  4. ElseIf (Year Mod 100) > 0 Then
  5. FIsLeapYear = True
  6. ElseIf (Year Mod 400) = 0 Then
  7. FIsLeapYear = True
  8. Else
  9. FIsLeapYear = False
  10. End If
  11. End Function
  12. Public Function FIsEndOfMonth(Day As Integer, Month As Integer, Year As Integer) As Boolean
  13. Select Case Month
  14. Case 1, 3, 5, 7, 8, 10, 12
  15. FIsEndOfMonth = (Day = 31)
  16. Case 4, 6, 9, 11
  17. FIsEndOfMonth = (Day = 30)
  18. Case 2
  19. If FIsLeapYear(Year) Then
  20. FIsEndOfMonth = (Day = 29)
  21. Else
  22. FIsEndOfMonth = (Day = 28)
  23. End If
  24. End Select
  25. End Function
  26. Public Function Days360(StartYear As Integer, EndYear As Integer, StartMonth As Integer, EndMonth As Integer, StartDay As Integer, EndDay As Integer) As Integer
  27. Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay)
  28. End Function
  29. Public Function TmpDays360Nasd(StartDate As Date, EndDate As Date, Method As Integer, UseEom As Boolean) As Integer
  30. Dim StartDay As Integer
  31. Dim StartMonth As Integer
  32. Dim StartYear As Integer
  33. Dim EndDay As Integer
  34. Dim EndMonth As Integer
  35. Dim EndYear As Integer
  36. StartDay = Day(StartDate)
  37. StartMonth = Month(StartDate)
  38. StartYear = Year(StartDate)
  39. EndDay = Day(EndDate)
  40. EndMonth = Month(EndDate)
  41. EndYear = Year(EndDate)
  42. If (EndMonth = 2 And FIsEndOfMonth(EndDay, EndMonth, EndYear)) And ((StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Or Method = 3) Then
  43. EndDay = 30
  44. End If
  45. If EndDay = 31 And (StartDay >= 30 Or Method = 3) Then
  46. EndDay = 30
  47. End If
  48. If StartDay = 31 Then
  49. StartDay = 30
  50. End If
  51. If (UseEom And StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Then
  52. StartDay = 30
  53. End If
  54. TmpDays360Nasd = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
  55. End Function
  56. Public Function TmpDays360Euro(StartDate As Date, EndDate As Date)
  57. Dim StartDay As Integer
  58. Dim StartMonth As Integer
  59. Dim StartYear As Integer
  60. Dim EndDay As Integer
  61. Dim EndMonth As Integer
  62. Dim EndYear As Integer
  63. StartDay = Day(StartDate)
  64. StartMonth = Month(StartDate)
  65. StartYear = Year(StartDate)
  66. EndDay = Day(EndDate)
  67. EndMonth = Month(EndDate)
  68. EndYear = Year(EndDate)
  69. If (StartDay = 31) Then
  70. StartDay = 30
  71. End If
  72. If (EndDay = 31) Then
  73. EndDay = 30
  74. End If
  75. TmpDays360Euro = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
  76. End Function
  77. Public Function TmpDiffDates(StartDate As Date, EndDate As Date, Basis As Integer) As Integer
  78. Select Case Basis
  79. Case 0 'atpmBasis30360
  80. TmpDiffDates = TmpDays360Nasd(StartDate, EndDate, 0, True)
  81. Case 1, 2, 3 'atpmBasisActual atpmBasisActual360 atpmBasisActual365 -- use actual count of days
  82. TmpDiffDates = DateDiff("d", StartDate, EndDate)
  83. Case 4 'atpmBasisE30360
  84. TmpDiffDates = TmpDays360Euro(StartDate, EndDate)
  85. End Select
  86. End Function
  87. Public Function TmpCalcAnnualBasis(StartDate As Date, EndDate As Date, Basis As Integer) As Double
  88. Dim StartDay As Integer
  89. Dim StartMonth As Integer
  90. Dim StartYear As Integer
  91. Dim EndDay As Integer
  92. Dim EndMonth As Integer
  93. Dim EndYear As Integer
  94. Dim iYear As Integer
  95. Select Case Basis
  96. Case 0, 2, 4 'atpmBasis30360 atpmBasisActual360 atpmBasisE30360
  97. TmpCalcAnnualBasis = 360
  98. Case 3 'atpmBasisActual365
  99. TmpCalcAnnualBasis = 365
  100. Case 1 ' atpmBasisActual
  101. StartDay = Day(StartDate)
  102. StartMonth = Month(StartDate)
  103. StartYear = Year(StartDate)
  104. EndDay = Day(EndDate)
  105. EndMonth = Month(EndDate)
  106. EndYear = Year(EndDate)
  107. If (StartYear = EndYear) Then
  108. If FIsLeapYear(StartYear) Then
  109. TmpCalcAnnualBasis = 366
  110. Else
  111. TmpCalcAnnualBasis = 365
  112. End If
  113. ElseIf ((EndYear - 1) = StartYear) And ((StartMonth > EndMonth) Or ((StartMonth = EndMonth) And StartDay >= EndDay)) Then
  114. If FIsLeapYear(StartYear) Then
  115. If StartMonth < 2 Or (StartMonth = 2 And StartDay <= 29) Then
  116. TmpCalcAnnualBasis = 366
  117. Else
  118. TmpCalcAnnualBasis = 365
  119. End If
  120. ElseIf FIsLeapYear(EndYear) Then
  121. If EndMonth > 2 Or (EndMonth = 2 And EndDay = 29) Then
  122. TmpCalcAnnualBasis = 366
  123. Else
  124. TmpCalcAnnualBasis = 365
  125. End If
  126. Else
  127. TmpCalcAnnualBasis = 365
  128. End If
  129. Else
  130. For iYear = StartYear To EndYear
  131. If FIsLeapYear(iYear) Then
  132. TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
  133. Else
  134. TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
  135. End If
  136. Next iYear
  137. TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
  138. End If
  139. End Select
  140. End Function
  141. Public Function TmpYearFrac(StartDate As Date, EndDate As Date, Basis As Integer)
  142. Dim nNumerator As Integer
  143. Dim nDenom As Double
  144. nNumerator = TmpDiffDates(StartDate, EndDate, Basis)
  145. nDenom = TmpCalcAnnualBasis(StartDate, EndDate, Basis)
  146. TmpYearFrac = nNumerator / nDenom
  147. End Function
  148. =end VBA source code for YearFrac
  149. #-----------------------------------------------------------------------------
  150. # Ruby version starts here, with VBA code in comment blocks for comparison ...
  151. #-----------------------------------------------------------------------------
  152. Public Function FIsLeapYear(Year As Integer) As Boolean
  153. If (Year Mod 4) > 0 Then
  154. FIsLeapYear = False
  155. ElseIf (Year Mod 100) > 0 Then
  156. FIsLeapYear = True
  157. ElseIf (Year Mod 400) = 0 Then
  158. FIsLeapYear = True
  159. Else
  160. FIsLeapYear = False
  161. End If
  162. End Function
  163. Public Function FIsEndOfMonth(Day As Integer, Month As Integer, Year As Integer) As Boolean
  164. Select Case Month
  165. Case 1, 3, 5, 7, 8, 10, 12
  166. FIsEndOfMonth = (Day = 31)
  167. Case 4, 6, 9, 11
  168. FIsEndOfMonth = (Day = 30)
  169. Case 2
  170. If FIsLeapYear(Year) Then
  171. FIsEndOfMonth = (Day = 29)
  172. Else
  173. FIsEndOfMonth = (Day = 28)
  174. End If
  175. End Select
  176. End Function
  177. Public Function Days360(StartYear As Integer, EndYear As Integer, StartMonth As Integer, EndMonth As Integer, StartDay As Integer, EndDay As Integer) As Integer
  178. Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay)
  179. End Function
  180. Public Function TmpDays360Nasd(StartDate As Date, EndDate As Date, Method As Integer, UseEom As Boolean) As Integer
  181. Dim StartDay As Integer
  182. Dim StartMonth As Integer
  183. Dim StartYear As Integer
  184. Dim EndDay As Integer
  185. Dim EndMonth As Integer
  186. Dim EndYear As Integer
  187. StartDay = Day(StartDate)
  188. StartMonth = Month(StartDate)
  189. StartYear = Year(StartDate)
  190. EndDay = Day(EndDate)
  191. EndMonth = Month(EndDate)
  192. EndYear = Year(EndDate)
  193. If (EndMonth = 2 And FIsEndOfMonth(EndDay, EndMonth, EndYear)) And ((StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Or Method = 3) Then
  194. EndDay = 30
  195. End If
  196. If EndDay = 31 And (StartDay >= 30 Or Method = 3) Then
  197. EndDay = 30
  198. End If
  199. If StartDay = 31 Then
  200. StartDay = 30
  201. End If
  202. If (UseEom And StartMonth = 2 And FIsEndOfMonth(StartDay, StartMonth, StartYear)) Then
  203. StartDay = 30
  204. End If
  205. TmpDays360Nasd = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
  206. End Function
  207. Public Function TmpDays360Euro(StartDate As Date, EndDate As Date)
  208. Dim StartDay As Integer
  209. Dim StartMonth As Integer
  210. Dim StartYear As Integer
  211. Dim EndDay As Integer
  212. Dim EndMonth As Integer
  213. Dim EndYear As Integer
  214. StartDay = Day(StartDate)
  215. StartMonth = Month(StartDate)
  216. StartYear = Year(StartDate)
  217. EndDay = Day(EndDate)
  218. EndMonth = Month(EndDate)
  219. EndYear = Year(EndDate)
  220. If (StartDay = 31) Then
  221. StartDay = 30
  222. End If
  223. If (EndDay = 31) Then
  224. EndDay = 30
  225. End If
  226. TmpDays360Euro = Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
  227. End Function
  228. Public Function TmpDiffDates(StartDate As Date, EndDate As Date, Basis As Integer) As Integer
  229. Select Case Basis
  230. Case 0 'atpmBasis30360
  231. TmpDiffDates = TmpDays360Nasd(StartDate, EndDate, 0, True)
  232. Case 1, 2, 3 'atpmBasisActual atpmBasisActual360 atpmBasisActual365 -- use actual count of days
  233. TmpDiffDates = DateDiff("d", StartDate, EndDate)
  234. Case 4 'atpmBasisE30360
  235. TmpDiffDates = TmpDays360Euro(StartDate, EndDate)
  236. End Select
  237. End Function
  238. Public Function TmpCalcAnnualBasis(StartDate As Date, EndDate As Date, Basis As Integer) As Double
  239. Dim StartDay As Integer
  240. Dim StartMonth As Integer
  241. Dim StartYear As Integer
  242. Dim EndDay As Integer
  243. Dim EndMonth As Integer
  244. Dim EndYear As Integer
  245. Dim iYear As Integer
  246. Select Case Basis
  247. Case 0, 2, 4 'atpmBasis30360 atpmBasisActual360 atpmBasisE30360
  248. TmpCalcAnnualBasis = 360
  249. Case 3 'atpmBasisActual365
  250. TmpCalcAnnualBasis = 365
  251. Case 1 ' atpmBasisActual
  252. StartDay = Day(StartDate)
  253. StartMonth = Month(StartDate)
  254. StartYear = Year(StartDate)
  255. EndDay = Day(EndDate)
  256. EndMonth = Month(EndDate)
  257. EndYear = Year(EndDate)
  258. If (StartYear = EndYear) Then
  259. If FIsLeapYear(StartYear) Then
  260. TmpCalcAnnualBasis = 366
  261. Else
  262. TmpCalcAnnualBasis = 365
  263. End If
  264. ElseIf ((EndYear - 1) = StartYear) And ((StartMonth > EndMonth) Or ((StartMonth = EndMonth) And StartDay >= EndDay)) Then
  265. If FIsLeapYear(StartYear) Then
  266. If StartMonth < 2 Or (StartMonth = 2 And StartDay <= 29) Then
  267. TmpCalcAnnualBasis = 366
  268. Else
  269. TmpCalcAnnualBasis = 365
  270. End If
  271. ElseIf FIsLeapYear(EndYear) Then
  272. If EndMonth > 2 Or (EndMonth = 2 And EndDay = 29) Then
  273. TmpCalcAnnualBasis = 366
  274. Else
  275. TmpCalcAnnualBasis = 365
  276. End If
  277. Else
  278. TmpCalcAnnualBasis = 365
  279. End If
  280. Else
  281. For iYear = StartYear To EndYear
  282. If FIsLeapYear(iYear) Then
  283. TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
  284. Else
  285. TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
  286. End If
  287. Next iYear
  288. TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
  289. End If
  290. End Select
  291. End Function
  292. Public Function TmpYearFrac(StartDate As Date, EndDate As Date, Basis As Integer)
  293. Dim nNumerator As Integer
  294. Dim nDenom As Double
  295. nNumerator = TmpDiffDates(StartDate, EndDate, Basis)
  296. nDenom = TmpCalcAnnualBasis(StartDate, EndDate, Basis)
  297. TmpYearFrac = nNumerator / nDenom
  298. End Function

字符串
深入挖掘,我found an article提供了一个伪代码实现,看起来很像Python。没有时间测试它,下面是伪代码:

  1. def appears_le_year(date1, date2):
  2. # Returns True if date1 and date2 "appear" to be 1 year or less apart.
  3. # This compares the values of year, month, and day directly to each other.
  4. # Requires date1 <= date2; returns boolean. Used by basis 1.
  5. if date1.year == date2.year:
  6. return True
  7. if (((date1.year + 1) == date2.year) and
  8. ((date1.month > date2.month) or
  9. ((date1.month == date2.month) and (date1.day >= date2.day)))):
  10. return True
  11. return False
  12. def basis0(date1,date2):
  13. # Swap so date1 <= date2 in all cases:
  14. if date1 > date2:
  15. date1, date2 = date2, date1
  16. if date1 == date2:
  17. return 0.0
  18. # Change day-of-month for purposes of calculation.
  19. date1day, date1month, date1year = date1.day, date1.month, date1.year
  20. date2day, date2month, date2year = date2.day, date2.month, date2.year
  21. if (date1day == 31 and date2day == 31):
  22. date1day = 30
  23. date2day = 30
  24. elif date1day == 31:
  25. date1day = 30
  26. elif (date1day == 30 and date2day == 31):
  27. date2day = 30
  28. # Note: If date2day==31, it STAYS 31 if date1day < 30.
  29. # Special fixes for February:
  30. elif (date1month == 2 and date2month == 2 and
  31. last_day_of_month(date1) and
  32. last_day_of_month(date2)):
  33. date1day = 30 # Set the day values to be equal
  34. date2day = 30
  35. elif date1month == 2 and last_day_of_month(date1):
  36. date1day = 30 # "Illegal" Feb 30 date.
  37. daydiff360 = ((date2day + date2month * 30 + date2year * 360) -
  38. (date1day + date1month * 30 + date1year * 360))
  39. return daydiff360 / 360
  40. def basis1(date1,date2):
  41. # Swap so date1 <= date2 in all cases:
  42. if date1 > date2:
  43. date1, date2 = date2, date1
  44. if date1 == date2:
  45. return 0.0
  46. if appears_le_year(date1, date2):
  47. if (date1.year == date2.year and is_leap_year(date1.year)):
  48. year_length = 366.
  49. elif (feb29_between(date1, date2) or
  50. (date2.month == 2 and date2.day == 29)): # fixed, 2008-04-18
  51. year_length = 366.
  52. else:
  53. year_length = 365.
  54. return diffdays(date1, date2) / year_length
  55. else:
  56. num_years = (date2.year - date1.year) + 1
  57. days_in_years = diffdays(date(date1.year, 1, 1), date(date2.year+1, 1, 1))
  58. average_year_length = days_in_years / num_years
  59. return diffdays(date1, date2) / average_year_length
  60. def basis2(date1,date2):
  61. # Swap so date1 <= date2 in all cases:
  62. if date1 > date2:
  63. date1, date2 = date2, date1
  64. return diffdays(date1, date2) / 360.
  65. def basis3(date1,date2):
  66. # Swap so date1 <= date2 in all cases:
  67. if date1 > date2:
  68. date1, date2 = date2, date1
  69. return diffdays(date1, date2) / 365.
  70. def basis4(date1,date2):
  71. # Swap so date1 <= date2 in all cases:
  72. if date1 > date2:
  73. date1, date2 = date2, date1
  74. if date1 == date2:
  75. return 0.0
  76. # Change day-of-month for purposes of calculation.
  77. date1day, date1month, date1year = date1.day, date1.month, date1.year
  78. date2day, date2month, date2year = date2.day, date2.month, date2.year
  79. if date1day == 31:
  80. date1day = 30
  81. if date2day == 31:
  82. date2day = 30
  83. # Remarkably, do NOT change Feb. 28 or 29 at ALL.
  84. daydiff360 = ( (date2day + date2month * 30 + date2year * 360) -
  85. (date1day + date1month * 30 + date1year * 360))
  86. return daydiff360 / 360

展开查看全部
dvtswwa3

dvtswwa32#

  1. import datetime
  2. def IsLeapYear(year):
  3. if year % 4 > 0:
  4. IsLeapYear = False
  5. elif year % 100 > 0:
  6. IsLeapYear = True
  7. elif year % 400 == 0:
  8. IsLeapYear = True
  9. else:
  10. IsLeapYear = False
  11. return IsLeapYear
  12. def IsEndOfMonth(day, month, year):
  13. if month in [1,3,5,7,8,10,12]:
  14. IsEndOfMonth = (day == 31)
  15. if month in [4,6,9,11]:
  16. IsEndOfMonth = (day == 30)
  17. if month in [2]:
  18. if IsLeapYear(year):
  19. IsEndOfMonth = (day == 29)
  20. else:
  21. IsEndOfMonth = (day == 28)
  22. return IsEndOfMonth
  23. def Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay):
  24. Days360 = ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay)
  25. return Days360
  26. def TmpDays360Nasd(StartDate, EndDate, Method, UseEom):
  27. StartDay = StartDate.day
  28. StartMonth = StartDate.month
  29. StartYear = StartDate.year
  30. EndDay = EndDate.day
  31. EndMonth = EndDate.month
  32. EndYear = EndDate.year
  33. if (EndMonth == 2 and IsEndOfMonth(EndDay, EndMonth, EndYear)) and ((StartMonth == 2 and IsEndOfMonth(StartDay, StartMonth, StartYear)) or Method == 3):
  34. EndDay = 30
  35. if EndDay == 31 and (StartDay >= 30 or Method == 3):
  36. EndDay = 30
  37. if StartDay == 31:
  38. StartDay = 30
  39. if (UseEom and StartMonth == 2 and IsEndOfMonth(StartDay, StartMonth, StartYear)):
  40. StartDay = 30
  41. return Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
  42. def TmpDays360Euro(StartDate, EndDate):
  43. StartDay = StartDate.day
  44. StartMonth = StartDate.month
  45. StartYear = StartDate.year
  46. EndDay = EndDate.day
  47. EndMonth = EndDate.month
  48. EndYear = EndDate.year
  49. if StartDay == 31:
  50. StartDay = 30
  51. if EndDay == 31:
  52. EndDay = 30
  53. return Days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay)
  54. def DateDiff(StartDate, EndDate):
  55. return abs((StartDate - EndDate).days)
  56. def TmpDiffDates(StartDate, EndDate, Basis):
  57. if Basis in [0]:
  58. TmpDiffDates = TmpDays360Nasd(StartDate, EndDate, 0, True)
  59. elif Basis in [1,2,3]:
  60. TmpDiffDates = DateDiff(StartDate, EndDate)
  61. elif Basis in [4]:
  62. TmpDiffDates = TmpDays360Euro(StartDate, EndDate)
  63. return TmpDiffDates
  64. def TmpCalcAnnualBasis(StartDate, EndDate, Basis):
  65. if Basis in [0,2,4]:
  66. TmpCalcAnnualBasis = 360
  67. elif Basis in [3]:
  68. TmpCalcAnnualBasis = 365
  69. elif Basis in [1]:
  70. StartDay = StartDate.day
  71. StartMonth = StartDate.month
  72. StartYear = StartDate.year
  73. EndDay = EndDate.day
  74. EndMonth = EndDate.month
  75. EndYear = EndDate.year
  76. if StartYear == EndYear:
  77. if IsLeapYear(StartYear):
  78. TmpCalcAnnualBasis = 366
  79. else:
  80. TmpCalcAnnualBasis = 365
  81. elif (EndYear - 1) == StartYear and (StartMonth > EndMonth or (StartMonth == EndMonth and StartDay >= EndDay)):
  82. if IsLeapYear(StartYear):
  83. if StartMonth < 2 or (StartMonth == 2 and StartDay <= 29):
  84. TmpCalcAnnualBasis = 366
  85. else:
  86. TmpCalcAnnualBasis = 365
  87. elif IsLeapYear(EndYear):
  88. if EndMonth > 2 or (EndMonth == 2 and EndDay == 29):
  89. TmpCalcAnnualBasis = 366
  90. else:
  91. TmpCalcAnnualBasis = 365
  92. else:
  93. TmpCalcAnnualBasis = 365
  94. else:
  95. TmpCalcAnnualBasis = 0
  96. for iYear in range(StartYear, EndYear + 1 ):
  97. if IsLeapYear(iYear):
  98. TmpCalcAnnualBasis = TmpCalcAnnualBasis + 366
  99. else:
  100. TmpCalcAnnualBasis = TmpCalcAnnualBasis + 365
  101. TmpCalcAnnualBasis = TmpCalcAnnualBasis / (EndYear - StartYear + 1)
  102. return TmpCalcAnnualBasis
  103. def YearFrac(StartDate, EndDate, Basis):
  104. Numerator = TmpDiffDates(StartDate, EndDate, Basis)
  105. Denom = TmpCalcAnnualBasis(StartDate, EndDate, Basis)
  106. YearFrac = Numerator/Denom
  107. return YearFrac
  108. # TESTING
  109. start = datetime.datetime(1978, 2, 28)
  110. end = datetime.datetime(2020, 5, 17)
  111. x0 = YearFrac(start, end, 0)
  112. x1 = YearFrac(start, end, 1)
  113. x2 = YearFrac(start, end, 2)
  114. x3 = YearFrac(start, end, 3)
  115. x4 = YearFrac(start, end, 4)

字符串

展开查看全部
qybjjes1

qybjjes13#

Excel的YEARFRAC有多种模式(basis)。如果您将basis设置为0或省略,则默认使用US NASD 30/360计算。
你可以在python中这样做,因为这个公式很容易遵循下面的公式


的数据
这里有一个例子

  1. In [1]: import datetime as dt
  2. In [2]: d1 = dt.date(2015, 12, 19)
  3. In [3]: d2 = dt.date(2020, 10, 19)
  4. In [4]: def date_factor(date1, date2):
  5. ...: """
  6. ...: This formula follows the US (NASD) calculation
  7. ...: that is used in Excel's YEARFRAC by default
  8. ...: https://www.fundmanagersoftware.com/help/def_accrued_interest.html
  9. ...: """
  10. ...: num = (360 * (date2.year - date1.year) + (30*(date2.month - date1.month) + (date2.day - date1.day)))
  11. ...: return num / 360
  12. ...:
  13. In [5]: date_factor(d1, d2)
  14. Out[5]: 4.833333333333333
  15. In [6]:

字符串
与Excel匹配


展开查看全部
cyej8jka

cyej8jka4#

下面是Python代码的完整实现,来自提供的伪代码。它需要datetime库。输入是datetime对象。

  1. ### YEARFRAC() ###
  2. from datetime import datetime
  3. def appears_le_year(date1, date2):
  4. ## Used by basis 1 ##
  5. # Returns True if date1 and date2 "appear" to be 1 year or less apart.
  6. # This compares the values of year, month, and day directly to each other.
  7. # Requires date1 <= date2; returns boolean.
  8. if date1.year == date2.year:
  9. return True
  10. if (((date1.year + 1) == date2.year) and
  11. ((date1.month > date2.month) or
  12. ((date1.month == date2.month) and (date1.day >= date2.day)))):
  13. return True
  14. return False
  15. def is_leap_year(year):
  16. return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
  17. def diffdays(date1, date2):
  18. return abs((date2 - date1).days)
  19. def feb29_between(date1, date2):
  20. year1 = date1.year
  21. year2 = date2.year
  22. if not year1 < year2:
  23. year1, year2 = year2, year1
  24. if year1 == year2:
  25. if not is_leap_year(year1):
  26. return False
  27. else:
  28. feb29 = datetime.strptime(str(year1) + '-02-29', '%Y-%m-%d')
  29. return (date1 <= feb29 and date2 >= feb29)
  30. else:
  31. month1 = date1.month
  32. month2 = date2.month
  33. if month1 > 2:
  34. if month2 < 2:
  35. return False
  36. elif month2 == 2:
  37. return date2.day == 29
  38. else:
  39. return True
  40. else:
  41. return is_leap_year(year1)
  42. def last_day_of_month(date):
  43. next_month = date.replace(day=28) + timedelta(days=4)
  44. last_day = next_month - timedelta(days=next_month.day)
  45. return date == last_day
  46. def basis0(date1, date2): #US 30360
  47. # Swap so date1 <= date2 in all cases:
  48. if date1 > date2:
  49. date1, date2 = date2, date1
  50. if date1 == date2:
  51. return 0.0
  52. # Change day-of-month for purposes of calculation.
  53. date1day, date1month, date1year = date1.day, date1.month, date1.year
  54. date2day, date2month, date2year = date2.day, date2.month, date2.year
  55. if (date1day == 31 and date2day == 31):
  56. date1day = 30
  57. date2day = 30
  58. elif date1day == 31:
  59. date1day = 30
  60. elif (date1day == 30 and date2day == 31):
  61. date2day = 30
  62. # Note: If date2day==31, it STAYS 31 if date1day < 30.
  63. # Special fixes for February:
  64. elif (date1month == 2 and date2month == 2 and last_day_of_month(date1) and last_day_of_month(date2)):
  65. date1day = 30 # Set the day values to be equal
  66. date2day = 30
  67. elif date1month == 2 and last_day_of_month(date1):
  68. date1day = 30 # "Illegal" Feb 30 date.
  69. daydiff360 = ((date2day + date2month * 30 + date2year * 360) - (date1day + date1month * 30 + date1year * 360))
  70. return daydiff360 / 360
  71. def basis1(date1,date2): #Actual/actual
  72. # Swap so date1 <= date2 in all cases:
  73. if date1 > date2:
  74. date1, date2 = date2, date1
  75. if date1 == date2:
  76. return 0.0
  77. if appears_le_year(date1, date2):
  78. if (date1.year == date2.year and is_leap_year(date1.year)):
  79. year_length = 366.
  80. elif (feb29_between(date1, date2) or (date2.month == 2 and date2.day == 29)): # fixed, 2008-04-18
  81. year_length = 366.
  82. else:
  83. year_length = 365.
  84. return diffdays(date1, date2) / year_length
  85. else:
  86. num_years = (date2.year - date1.year) + 1
  87. days_in_years = diffdays(date(date1.year, 1, 1), date(date2.year+1, 1, 1))
  88. average_year_length = days_in_years / num_years
  89. return diffdays(date1, date2) / average_year_length
  90. def basis2(date1,date2): #Actual/360
  91. # Swap so date1 <= date2 in all cases:
  92. if date1 > date2:
  93. date1, date2 = date2, date1
  94. return diffdays(date1, date2) / 360.
  95. def basis3(date1,date2): #Actual/365
  96. # Swap so date1 <= date2 in all cases:
  97. if date1 > date2:
  98. date1, date2 = date2, date1
  99. return diffdays(date1, date2) / 365.
  100. def basis4(date1,date2): #EU 30360
  101. # Swap so date1 <= date2 in all cases:
  102. if date1 > date2:
  103. date1, date2 = date2, date1
  104. if date1 == date2:
  105. return 0.0
  106. # Change day-of-month for purposes of calculation.
  107. date1day, date1month, date1year = date1.day, date1.month, date1.year
  108. date2day, date2month, date2year = date2.day, date2.month, date2.year
  109. if date1day == 31:
  110. date1day = 30
  111. if date2day == 31:
  112. date2day = 30
  113. # Remarkably, do NOT change Feb. 28 or 29 at ALL.
  114. daydiff360 = ( (date2day + date2month * 30 + date2year * 360) - (date1day + date1month * 30 + date1year * 360))
  115. return daydiff360 / 360

字符串
使用示例:

  1. date1 = datetime.strptime('2000-01-01', '%Y-%m-%d')
  2. date2 = datetime.strptime('2000-06-06', '%Y-%m-%d')
  3. print(basis0(date1, date2),
  4. basis1(date1, date2),
  5. basis2(date1, date2),
  6. basis3(date1, date2),
  7. basis4(date1, date2))

展开查看全部
ar5n3qh5

ar5n3qh55#

  1. `appears_le_year <- function(date1, date2) {
  2. if (year(date1) == year(date2)) {
  3. return(TRUE)
  4. }
  5. if (((year(date1) + 1) == year(date2)) &&
  6. ((month(date1) > month(date2)) ||
  7. ((month(date1) == month(date2)) && (mday(date1) >= mday(date2))))) {
  8. return(TRUE)
  9. }
  10. return(FALSE)
  11. }
  12. is_leap_year <- function(year) {
  13. return(year %% 4 == 0 && (year %% 100 != 0 || year %% 400 == 0))
  14. }
  15. diffdays <- function(date1, date2) {
  16. return(abs(as.numeric(date2 - date1, units = "days")))
  17. }
  18. feb29_between <- function(date1, date2) {
  19. year1 <- year(date1)
  20. year2 <- year(date2)
  21. if (!(year1 < year2)) {
  22. year1 <- year2
  23. year2 <- year(date1)
  24. }
  25. if (year1 == year2) {
  26. if (!is_leap_year(year1)) {
  27. return(FALSE)
  28. } else {
  29. feb29 <- as.Date(paste(year1, '-02-29'))
  30. return(date1 <= feb29 & date2 >= feb29)
  31. }
  32. } else {
  33. month1 <- month(date1)
  34. month2 <- month(date2)
  35. if (month1 > 2) {
  36. if (month2 < 2) {
  37. return(FALSE)
  38. } else if (month2 == 2) {
  39. return(mday(date2) == 29)
  40. } else {
  41. return(TRUE)
  42. }
  43. } else {
  44. return(is_leap_year(year1))
  45. }
  46. }
  47. }
  48. last_day_of_month <- function(date) {
  49. next_month <- date + months(1)
  50. last_day <- next_month - days(mday(next_month))
  51. return(date == last_day)
  52. }
  53. YearFracBasis0 <- function(date1, date2) {
  54. if (date1 > date2) {
  55. temp <- date1
  56. date1 <- date2
  57. date2 <- temp
  58. }
  59. if (date1 == date2) {
  60. return(0.0)
  61. }
  62. date1day <- mday(date1)
  63. date1month <- month(date1)
  64. date1year <- year(date1)
  65. date2day <- mday(date2)
  66. date2month <- month(date2)
  67. date2year <- year(date2)
  68. if ((date1day == 31 & date2day == 31)) {
  69. date1day <- 30
  70. date2day <- 30
  71. } else if (date1day == 31) {
  72. date1day <- 30
  73. } else if ((date1day == 30 & date2day == 31)) {
  74. date2day <- 30
  75. }
  76. if ((month(date1) == 2 & month(date2) == 2 & last_day_of_month(date1) & last_day_of_month(date2))) {
  77. date1day <- 30
  78. date2day <- 30
  79. } else if (month(date1) == 2 & last_day_of_month(date1)) {
  80. date1day <- 30
  81. }
  82. daydiff360 <- ((date2day + date2month * 30 + date2year * 360) -
  83. (date1day + date1month * 30 + date1year * 360))
  84. return(daydiff360 / 360)
  85. }
  86. YearFracBasis1 <- function(date1, date2) {
  87. if (date1 > date2) {
  88. temp <- date1
  89. date1 <- date2
  90. date2 <- temp
  91. }
  92. if (date1 == date2) {
  93. return(0.0)
  94. }
  95. if (appears_le_year(date1, date2)) {
  96. if ((year(date1) == year(date2) & is_leap_year(year(date1)))) {
  97. year_length <- 366
  98. } else if (feb29_between(date1, date2) || (month(date2) == 2 & mday(date2) == 29)) {
  99. year_length <- 366
  100. } else {
  101. year_length <- 365
  102. }
  103. return(diffdays(date1, date2) / year_length)
  104. } else {
  105. num_years <- (year(date2) - year(date1)) + 1
  106. days_in_years <- diffdays(as.Date(paste(year(date1), '01-01')), as.Date(paste(year(date2) + 1, '01-01')))
  107. average_year_length <- days_in_years / num_years
  108. return(diffdays(date1, date2) / average_year_length)
  109. }
  110. }
  111. YearFracBasis2 <- function(date1, date2) {
  112. if (date1 > date2) {
  113. temp <- date1
  114. date1 <- date2
  115. date2 <- temp
  116. }
  117. return(diffdays(date1, date2) / 360)
  118. }
  119. YearFracBasis3 <- function(date1, date2) {
  120. if (date1 > date2) {
  121. temp <- date1
  122. date1 <- date2
  123. date2 <- temp
  124. }
  125. return(diffdays(date1, date2) / 365)
  126. }
  127. YearFracBasis4 <- function(date1, date2) {
  128. if (date1 > date2) {
  129. temp <- date1
  130. date1 <- date2
  131. date2 <- temp
  132. }
  133. if (date1 == date2) {
  134. return(0.0)
  135. }
  136. date1day <- mday(date1)
  137. date1month <- month(date1)
  138. date1year <- year(date1)
  139. date2day <- mday(date2)
  140. date2month <- month(date2)
  141. date2year <- year(date2)
  142. if (date1day == 31) {
  143. date1day <- 30
  144. }
  145. if (date2day == 31) {
  146. date2day <- 30
  147. }
  148. daydiff360 <- ((date2day + date2month * 30 + date2year * 360) -
  149. (date1day + date1month * 30 + date1year * 360))
  150. return(daydiff360 / 360)
  151. }`

字符串

展开查看全部

相关问题