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

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://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
2、https://stackoverflow.com/questions/43355292/replicating-yearfrac-function-from-excel-in-python
3、https://support.microsoft.com/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 :        //'atpmBasis30360
                tmpDiffDates = tmpdays360Nasd(startDate, endDate, 0, true) ;
                break;
            case 1 :
            case 2:
            case 3 :  //'atpmBasisActual atpmBasisActual360 atpmBasisActual365 -- use actual count of days
                tmpDiffDates = DateUtil.betweenDay( startDate, endDate ,true) ;
                break;
            case 4 :        //'atpmBasisE30360
                tmpDiffDates = 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 atpmBasisE30360
                tmpCalcAnnualBasis = 360.0 ;
                break;
            case 3: // 'atpmBasisActual365
                tmpCalcAnnualBasis = 365.0 ;
                break;
            case 1: // ' atpmBasisActual
                StartDay   = 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);
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值