Excel Days360 的确切算法是什么?

问题描述 投票:0回答:5

我正在将一些计算从 Excel 移植到使用 Days360 函数(默认/美国方法)的 C#。使用维基百科页面作为指导,我想出了以下代码:

    public static int Days360(DateTime a, DateTime b)
    {
        var dayA = a.Day;
        var dayB = b.Day;

        if (IsLastDayOfFebruary(a) && IsLastDayOfFebruary(b))
            dayB = 30;

        if (dayA == 31 || IsLastDayOfFebruary(a))
            dayA = 30;

        if (dayA == 30 && dayB == 31)
            dayB = 30;

        return ((b.Year - a.Year) * 12 + b.Month - a.Month) * 30 + dayB - dayA;
    }

    private static bool IsLastDayOfFebruary(DateTime date)
    {
        if (date.Month != 2)
            return false;

        int lastDay = DateTime.DaysInMonth(date.Year, 2);
        return date.Day == lastDay;
    }

我用(小)范围的输入对其进行了测试,结果与 Excel 的本机函数基本一致,除非我对 a 和 b 都使用 2015-02-28。我的代码返回 0 和 Excel -2。

我的结果看起来更合理,但此时,我更愿意计算出与 Excel 完全相同的结果。他们可能有其他不同意的输入,因此我不想仅针对该日期制作特殊情况。

有谁知道Excel使用的确切算法吗?

编辑:我发布的原始代码中有一个与问题无关的明显错误。我已经修复了这个问题,但在发布问题时我从错误的文件中复制了。

c# algorithm excel vba
5个回答
3
投票

根据这篇维基百科文章,Microsoft Excel

Days360
功能相当于 30/360 BMA/PSA。因此,为了获得 MS Excel 的精确结果,我们需要实施 BMA/PSA 方法。我已经实现了该方法。

private double Days360(DateTime StartDate, DateTime EndDate)
{
    int StartDay = StartDate.Day;
    int StartMonth = StartDate.Month;
    int StartYear = StartDate.Year;
    int EndDay = EndDate.Day;
    int EndMonth = EndDate.Month;
    int EndYear = EndDate.Year;

    if (StartDay == 31 || IsLastDayOfFebruary(StartDate))
    {
        StartDay = 30;
    }

    if (StartDay == 30 && EndDay == 31)
    {
        EndDay = 30;
    }

    return ((EndYear - StartYear) * 360) + ((EndMonth - StartMonth) * 30) + (EndDay - StartDay);
}

private bool IsLastDayOfFebruary(DateTime date)
{
    return date.Month == 2 && date.Day == DateTime.DaysInMonth(date.Year, date.Month);
}

1
投票

我有同样的需求,我在这个phpexcel库的第51行函数中找到了解决方案 日期Diff360

这是计算类代码的一部分

    /**
 * Identify if a year is a leap year or not
 *
 * @param    integer    $year    The year to test
 * @return    boolean            TRUE if the year is a leap year, otherwise FALSE
 */
public static function isLeapYear($year)
{
    return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
}
/**
 * Return the number of days between two dates based on a 360 day calendar
 *
 * @param    integer    $startDay        Day of month of the start date
 * @param    integer    $startMonth        Month of the start date
 * @param    integer    $startYear        Year of the start date
 * @param    integer    $endDay            Day of month of the start date
 * @param    integer    $endMonth        Month of the start date
 * @param    integer    $endYear        Year of the start date
 * @param    boolean $methodUS        Whether to use the US method or the European method of calculation
 * @return    integer    Number of days between the start date and the end date
 */
private static function dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS)
{
    if ($startDay == 31) {
        --$startDay;
    } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
        $startDay = 30;
    }
    if ($endDay == 31) {
        if ($methodUS && $startDay != 30) {
            $endDay = 1;
            if ($endMonth == 12) {
                ++$endYear;
                $endMonth = 1;
            } else {
                ++$endMonth;
            }
        } else {
            $endDay = 30;
        }
    }
    return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
}

0
投票

该算法还包括可选参数

method
:

int startMonthDays = 0; 
int endMonthDays = 0; 
double diff = 0;
if(method.Equals("TRUE"))
{

    if(dtStartDate.getDay() < 30)
    {
        startMonthDays = (30 - dtStartDate.getDay());
    }
    else
    {
        startMonthDays = 0; 
    }

    if(dtEndDate.getDay() < 30)
    {
        endMonthDays = dtEndDate.getDay();
    }
    else
    {
        endMonthDays = 30;  
    }

    diff =  (dtEndDate.getYear() - dtStartDate.getYear())*360 +
                    (dtEndDate.getMonth() - dtStartDate.getMonth() - 1)*30 +
                    startMonthDays + endMonthDays;
}
else
    {
        if(DateCalendar.daysInMonth(dtStartDate.getYear(), dtStartDate.getMonth()) == dtStartDate.getDay())
        {
            startMonthDays = 0; 
        }
        else
        {
            startMonthDays = (30 - dtStartDate.getDay());
        }

        if(DateCalendar.daysInMonth(dtEndDate.getYear(), dtEndDate.getMonth()) == dtEndDate.getDay())
        {
            if(dtStartDate.getDay() < DateCalendar.daysInMonth(dtStartDate.getYear(), dtStartDate.getMonth()) - 1)
            {
                if(DateCalendar.daysInMonth(dtEndDate.getYear(), dtEndDate.getMonth()) > 30)
                {
                    endMonthDays = DateCalendar.daysInMonth(dtEndDate.getYear(), dtEndDate.getMonth());
                }
                else
                {
                    endMonthDays = dtEndDate.getDay();
                }
            }
            else
            {
                if(DateCalendar.daysInMonth(dtEndDate.getYear(), dtEndDate.getMonth()) > 30)
                {
                    endMonthDays = DateCalendar.daysInMonth(dtEndDate.getYear(), dtEndDate.getMonth()) - 1;
                }
                else
                {
                    endMonthDays = dtEndDate.getDay();
                }
            }
        }
        else
        {
            endMonthDays = dtEndDate.getDay();

        }

        diff =  (dtEndDate.getYear() - dtStartDate.getYear())*360 +
                    (dtEndDate.getMonth() - dtStartDate.getMonth() - 1)*30 +
                    startMonthDays + endMonthDays;
    }

public static int daysInMonth (int year, int month) 
{      
    if (DateTime.IsLeapYear(year) && month == 2) 
    {
        return 29;   
    }   
    else
    {
        return table[month - 1];  
    }
}

private static readonly int[] table = new int[]{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

0
投票

测试下一个

public static int Days360(DateTime a, DateTime b)
{
    var dayA = a.Day;
    var dayB = b.Day;

    if (IsLastDayOfMonth(a) && IsLastDayOfMonth(b)) {
        dayB = Math.min(30, dayB);
    } else if (dayA == 30 && dayB ==31) {
        DayB = 30;
    }

    if (IsLastDayOfMonth(a))
        dayA = 30;

    return ((b.Year - a.Year) * 360 + b.Month - a.Month) * 30 + dayB - dayA;
}

private static bool IsLastDayOfMonth(DateTime date)
{
    int lastDay = DateTime.DaysInMonth(date.Year, date.Month);
    return date.Day == lastDay;
}

0
投票

尝试这个 C++ Days360 算法。

bool isLastDayOfFebruary(const boost::posix_time::ptime& date) { return date.date().month() == 2 && date.date().day() == date.date().end_of_month().day(); }

    int days360(const boost::posix_time::ptime& startDate, const boost::posix_time::ptime& endDate) {
            int startDay = startDate.date().day();
            int startMonth = startDate.date().month();
            int startYear = startDate.date().year();
            int endDay = endDate.date().day();
            int endMonth = endDate.date().month();
            int endYear = endDate.date().year();

            if (startDay == 31 || isLastDayOfFebruary(startDate)) {
                startDay = 30;
            }

            if (startDay == 30 && (endDay == 31 || isLastDayOfFebruary(endDate))) {
                endDay = 30;
            }

            return ((endYear - startYear) * 360) + ((endMonth - startMonth) * 30) + (endDay - startDay);
        }
© www.soinside.com 2019 - 2024. All rights reserved.