首页 - 新闻 -  YearFrac函数Java版实现(完整版)

YearFrac函数Java版实现(完整版)

2023-09-18 22:44

YEARFRAC 可计算两个日期(start_date 和 end_date)之间的天数(取整天数)占一年的比例。 例如,可使用 YEARFRAC 确定某一特定条件下全年效益或债务的比例。

语法
YEARFRAC(start_date, end_date, [basis])
YEARFRAC 函数语法具有下列参数:
◾ Start_date 必需。 一个代表开始日期的日期。
◾ End_date 必需。 一个代表终止日期的日期。
◾ Basis 可选。 要使用的日计数基准类型。
Basis 日计数基准
0 或省略 US (NASD) 30/360
1 实际/实际
2 实际/360
3 实际/365
4 欧洲 30/360

重要:
◾应使用 DATE 函数输入日期,或者将日期作为其他公式或函数的结果输入。 例如,使用函数 DATE(2018,5,23) 输入 2018 年 5 月 23 日。 如果日期以文本形式输入,则会出现问题。
◾如果使用 US (NASD) 30/360 基准,且 start_date 是二月的最后一天,则 YEARFRAC 函数可能会返回错误的结果。

备注
◾Excel 将日期存储为可用于计算的序列号。 默认情况下,1900 年 1 月 1 日的序列号是 1,而 2018 年 1 月 1 日的序列号是 43101,这是因为它距 1900 年 1 月 1 日有 43101 天。
◾所有参数都将被截尾取整。
◾如果 start_date 或 end_date 不是有效日期,YEARFRAC 会返回错误值 #VALUE! 。
◾如果 basis < 0 或 basis > 4,函数 YEARFRAC 会返回错误值 #NUM!。 。

JAVA版本的实现参考了以下资料:

1、https://www.gsm-guard.net/archives/office-formula/200806/msg00039.html
2、https://www.gsm-guard.net/questions/43355292/replicating-yearfrac-function-from-excel-in-python
3、https://www.gsm-guard.net/zh-cn/office/yearfrac-%E5%87%BD%E6%95%B0-3844141e-c76d-4143-82b6-208454ddc6a8?ns=excel&version=90&ui=zh-cn&rs=zh-cn&ad=cn

参照vba的实现逻辑,实现了java版本,仅供参考。

import cn.hutool.core.date.DateUtil;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;public class YearFracFunction {private static final Integer BIG_MONTH [] = {1 ,3 ,5 ,7 ,8 ,10 ,12 };private static final Integer SMALL_MONTH [] = {4 ,6 ,9 ,11  };/**** 是否闰年* @param year* @return*/public static boolean isLeapYear(Integer year) {return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;}/**** 判断是否是当前月份最后一天* @param day* @param month* @param year* @return*/public static boolean isEndOfMonth(Integer day, Integer month, Integer year) {for (Integer x : BIG_MONTH) {if( x.equals(month)) {return day == 31;}}for (Integer y : SMALL_MONTH) {if( y.equals(month)) {return day == 30;}}if( isLeapYear(year)) {return day == 29;}return day == 28;}/**** 360天情况下天数的计算方式* @param startYear* @param endYear* @param startMonth* @param endMonth* @param startDay* @param endDay* @return*/public static Long days360(Integer startYear, Integer endYear, Integer startMonth, Integer endMonth, Integer startDay, Integer endDay) {return (endYear - startYear) * 360 + (endMonth - startMonth) * 30 + (endDay - startDay) *1L ;}public static Long tmpdays360Nasd(Date startDate, Date endDate, Integer method, Boolean useEom ) {Integer StartDay = DateUtil.dayOfMonth(startDate) ;Integer StartMonth = DateUtil.month(startDate) ;Integer StartYear = DateUtil.year(startDate) ;Integer EndDay = DateUtil.dayOfMonth(endDate) ;Integer EndMonth = DateUtil.month(endDate) ;Integer EndYear = DateUtil.year(endDate) ;if( (EndMonth == 2 && isEndOfMonth(EndDay, EndMonth, EndYear))  &&( (StartMonth == 2  && isEndOfMonth(StartDay, StartMonth, StartYear)) || method == 3) ){EndDay = 30 ;}if( EndDay == 31  && (StartDay >= 30 || method == 3) ) {EndDay = 30 ;}if( StartDay == 31 ) {StartDay = 30 ;}if( useEom  && StartMonth == 2  && isEndOfMonth(StartDay, StartMonth, StartYear) ) {StartDay = 30 ;}return days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay) ;}public static Long tmpdays360Euro(Date startDate, Date endDate) {Integer StartDay   = DateUtil.dayOfMonth(startDate) ;Integer StartMonth = DateUtil.month(startDate) ;Integer StartYear  = DateUtil.year(startDate) ;Integer EndDay     = DateUtil.dayOfMonth(endDate) ;Integer EndMonth   = DateUtil.month(endDate) ;Integer EndYear    = DateUtil.year(endDate) ;if( StartDay == 31) {StartDay = 30 ;}if( EndDay == 31) {EndDay = 30 ;}return days360(StartYear, EndYear, StartMonth, EndMonth, StartDay, EndDay) ;}public static Long tmpDiffDates(Date startDate, Date endDate, Integer Basis)  {Long tmpDiffDates = 0L ;switch( Basis) {case 0 :        //'atpmBasis30360tmpDiffDates = tmpdays360Nasd(startDate, endDate, 0, true) ;break;case 1 :case 2:case 3 :  //'atpmBasisActual atpmBasisActual360 atpmBasisActual365 -- use actual count of daystmpDiffDates = DateUtil.betweenDay( startDate, endDate ,true) ;break;case 4 :        //'atpmBasisE30360tmpDiffDates = tmpdays360Euro(startDate, endDate) ;break;}return tmpDiffDates ;}public static Double tmpCalcAnnualBasis(Date startDate, Date endDate,Integer Basis ) {Integer StartDay  = 0 ;Integer StartMonth= 0 ;Integer StartYear= 0 ;Integer EndDay= 0 ;Integer EndMonth= 0 ;Integer EndYear= 0 ;Integer iYear  = 0 ;Double tmpCalcAnnualBasis = 0.0 ;switch ( Basis) {case 0:case 2:case 4: //  'atpmBasis30360 atpmBasisActual360 atpmBasisE30360tmpCalcAnnualBasis = 360.0 ;break;case 3: // 'atpmBasisActual365tmpCalcAnnualBasis = 365.0 ;break;case 1: // ' atpmBasisActualStartDay   = DateUtil.dayOfMonth(startDate) ;StartMonth = DateUtil.month(startDate) ;StartYear  = DateUtil.year(startDate) ;EndDay     = DateUtil.dayOfMonth(endDate) ;EndMonth   = DateUtil.month(endDate) ;EndYear    = DateUtil.year(endDate) ;if(StartYear == EndYear) {if(isLeapYear(StartYear)){tmpCalcAnnualBasis = 366.0 ;}else{tmpCalcAnnualBasis = 365.0 ;}}else if (  EndYear - 1 == StartYear&& ( (StartMonth > EndMonth) || ( (StartMonth == EndMonth) && StartDay >= EndDay) ) ) {if ( isLeapYear (StartYear) ){if( StartMonth <2 || (StartMonth == 2 && StartDay <= 29) ){tmpCalcAnnualBasis = 366.0 ;}else{tmpCalcAnnualBasis = 365.0 ;}}else if(isLeapYear (EndYear) ){if( EndMonth >2 || (EndMonth == 2 && EndDay == 29) ){tmpCalcAnnualBasis = 366.0 ;}else{tmpCalcAnnualBasis = 365.0 ;}}else{tmpCalcAnnualBasis = 365.0 ;}}else{for( iYear = StartYear ; iYear <= EndYear ; iYear ++){if(isLeapYear(iYear) ){tmpCalcAnnualBasis = tmpCalcAnnualBasis + 366 ;}else{tmpCalcAnnualBasis = tmpCalcAnnualBasis + 365 ;}}tmpCalcAnnualBasis = tmpCalcAnnualBasis / (EndYear - StartYear + 1) ;}break;}return tmpCalcAnnualBasis ;}public static Double tmpYearFrac(Date startDate, Date endDate,  Integer Basis) {Long nNumerator = 0L ;Double  nDenom = 0.0;nNumerator = tmpDiffDates(startDate, endDate, Basis) ;nDenom = tmpCalcAnnualBasis(startDate, endDate, Basis) ;Double result = nNumerator / nDenom ;BigDecimal t_result = new BigDecimal( result) ;return t_result.setScale( 14, RoundingMode.HALF_UP).doubleValue() ;}public static void main(String[] args) {Date startDate = DateUtil.parseDate( "2020-03-25") ;Date endDate = DateUtil.parseDate( "2022-03-31") ;Double yearfrac = tmpYearFrac( startDate ,endDate , 4) ;System.out.println(yearfrac);}
}