天天看點

按月、按天計算失效日期的代碼實作

背景

在我們計算截止日期、失效時期的時候,可能存在按年、按月、按天統計失效的情況。比如:目前日期是2014-12-22,900天後失效,失效日期是多少?17個月後失效,失效日期是多少。通過本文源碼,你都可以得到答案。

為驗證程式的正确性,本文對每個接口函數都做了大量的測試用例。

// sn_ctrl.cpp : 定義控制台應用程式的入口點。
//
 
#include "stdafx.h"
#include <assert.h>
#include <iostream>
using namespace std;
 
const int MAX_MONTH_CNTS_IN_YEAR = 12;
 
/*
**@brief:判定年份是否為閏年.
**@param: iYear目前年份.
**@return:true,閏年; false,平年.
*/
bool IsLeapYear(unsigned int iYear)
{
    if ( (iYear%4 == 0 && iYear%100 != 0) || (iYear%400 == 0) )
    {
        return true;
    }
    else
    {
        return false;
    }
}
 
/*
**@brief:判定某年某月的天數.
**@param: iYear目前年份,iMonth目前月份;
**@return:目前年、月的天數.
*/
unsigned int daysInMonth(unsigned int iYear, unsigned int iMonth)
{
    assert(iMonth >=1 && iMonth <= MAX_MONTH_CNTS_IN_YEAR);
    int iDaysInMonth = 0;
    switch(iMonth)
    {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            iDaysInMonth = 31;
        break;
        case 4:
        case 6:
        case 9:
        case 11:
            iDaysInMonth = 30;
        break;
        case 2:
            if (IsLeapYear(iYear))
            {
                iDaysInMonth = 29;
            }
            else
            {
                iDaysInMonth = 28;
            }
        break;
        default:
            cout << "Error Month!!" << endl;
            break;
    }
    return iDaysInMonth;
}
 
 
struct Date
{
    unsigned int date_year;
    unsigned int date_month;
    unsigned int date_day;
};
 
class DateOp
{
public:
    DateOp(){}
    DateOp(Date thisDate):theDate(thisDate){}
    virtual ~DateOp(){}
    Date DataAddmonths(Date date_init, unsigned int month_cnts);
    Date DateAddDays(Date date_init, unsigned int day_cnts);
 
private:
    Date theDate;
};
 
/*
**@brief:初始月份加幾個月後的日期.
**@param: dateInit初始日期,iMonthCnts過幾個月;
**@return:計算後的新的日期.
*/
Date DateOp::DataAddmonths(Date dateInit, unsigned int iMonthCnts)
{
    assert (iMonthCnts >= 1);
    Date dateAfterAdd = dateInit;
 
    unsigned int iMonthNewPos = dateInit.date_month + iMonthCnts;
    if (iMonthNewPos > MAX_MONTH_CNTS_IN_YEAR)  //求和後超過12
    {
        unsigned int iYearCnts = (iMonthNewPos)/(MAX_MONTH_CNTS_IN_YEAR);
        unsigned int iMonthNew = (iMonthNewPos)%(MAX_MONTH_CNTS_IN_YEAR);
        if (0 == iMonthNew) //月份為0特殊處理
        {
            dateAfterAdd.date_year = dateInit.date_year + iYearCnts -1;
            iMonthNew = 12;
        }
        else
        {
            dateAfterAdd.date_year = dateInit.date_year + iYearCnts;
        }    
        dateAfterAdd.date_month = iMonthNew;
    
    }
    else  //求和後不超過12
    {
        dateAfterAdd.date_year = dateInit.date_year;
        dateAfterAdd.date_month = dateInit.date_month + iMonthCnts;
    }
 
    //2月份特殊處理
    if ( (30 == dateInit.date_day || 31 == dateInit.date_day) && (2 == dateAfterAdd.date_month))
    {
        dateAfterAdd.date_day = daysInMonth(dateAfterAdd.date_year, dateAfterAdd.date_month);
    }
    //30天的月份特殊處理
    else if ((31 == dateInit.date_day) && ((4 == dateAfterAdd.date_month) || (6 == dateAfterAdd.date_month) ||
             (9 == dateAfterAdd.date_month) || (11 == dateAfterAdd.date_month)))
    {
        dateAfterAdd.date_day = 30;
    }
    else
    {
        dateAfterAdd.date_day = dateInit.date_day;
    }
    return dateAfterAdd;
}
 
/*
**@brief:初始月份加*天後的日期.
**@param: dateInit初始日期,iMonthCnts過多少天;
**@return:計算後的新的日期.
*/
Date DateOp::DateAddDays(Date dateInit, unsigned int day_cnts)
{
    assert(day_cnts >= 1);
    cout << "+ " << day_cnts << endl;
    Date dateAfterAdd = dateInit;
 
    unsigned int iCurDaysInMonth = daysInMonth(dateInit.date_year, dateInit.date_month); //目前月的總天數.
    //cout << "iCurDaysInMonth = " << iCurDaysInMonth << endl;
    int iDayTotal = day_cnts;
    if (dateInit.date_day + day_cnts <= iCurDaysInMonth)
    {
        dateAfterAdd.date_day = dateInit.date_day + day_cnts;
        return dateAfterAdd;
    }
 
    int iLeftDaysInCurMonth = iCurDaysInMonth - dateInit.date_day; //目前月剩餘天數.
    //cout << endl << "iDayTotal = " << iDayTotal << "\t iLeftDayInCurmonth = " << iLeftDaysInCurMonth << endl << endl;
    //cout << "iAvgMonthCnts = " << iAvgMonthCnts << endl << endl;
 
    unsigned int iDaysInMonth = 0;
    int iLeftDaysTotal = 0;  //統計大約幾個月的實際總天數.
    unsigned int iMonthCnts = 0;
    while (iLeftDaysTotal <= iDayTotal - iLeftDaysInCurMonth)
    {
        ++iMonthCnts;
        unsigned int iCurMonth = dateInit.date_month + iMonthCnts;
    // cout << "iCurMonth = " << iCurMonth << endl;
        unsigned int iYearCnts = 0;
        if (iCurMonth > 12)
        {
            iYearCnts = (iCurMonth)/(MAX_MONTH_CNTS_IN_YEAR);
        }
    
        //cout << "iYearCnts = " << iYearCnts << endl;
 
        if (0 == iYearCnts)
        {
            dateAfterAdd.date_year = dateInit.date_year;
            dateAfterAdd.date_month = iCurMonth;
            iDaysInMonth = daysInMonth(dateInit.date_year, iCurMonth);
            iLeftDaysTotal += iDaysInMonth;
        }
        else
        {
            unsigned int iMonthNew = (iCurMonth)%(MAX_MONTH_CNTS_IN_YEAR);
            if (0 == iMonthNew) //月份為0特殊處理
            {
                dateAfterAdd.date_year = dateInit.date_year + iYearCnts -1;
                iMonthNew = 12;
            }
            else
            {
                dateAfterAdd.date_year = dateInit.date_year + iYearCnts;
            }
            dateAfterAdd.date_month = iMonthNew;
            iDaysInMonth = daysInMonth(dateAfterAdd.date_year, iMonthNew);
            iLeftDaysTotal += iDaysInMonth;
            
        }//end else
        //對于超出的做特殊處理.
        if (iLeftDaysTotal > (iDayTotal - iLeftDaysInCurMonth))
        {
            iLeftDaysTotal -= iDaysInMonth;
            break;
        }
    }
 
    //cout << "iDayTotal  =" << iDayTotal << "\tiLeftDaysInCurMonth = " << iLeftDaysInCurMonth  //bug??
        //<< "\tiLeftDaysTotal = " << iLeftDaysTotal << endl;
    if (iDayTotal - iLeftDaysInCurMonth - iLeftDaysTotal < 0)
    {
        dateAfterAdd.date_day = iDayTotal - iLeftDaysInCurMonth;
    }
    else
    {
        dateAfterAdd.date_day = iDayTotal - iLeftDaysInCurMonth - iLeftDaysTotal;
    }
    
    if (dateAfterAdd.date_month > 12)
    {
        dateAfterAdd.date_year += 1;
        dateAfterAdd.date_month = 1;
    }
    if (dateAfterAdd.date_day == 0)
    {
        if (1 == dateAfterAdd.date_month)
        {
            dateAfterAdd.date_month = 12;
            dateAfterAdd.date_year -= 1;
        }
        else
        {
            dateAfterAdd.date_month -= 1;
        }
 
        dateAfterAdd.date_day = daysInMonth(dateAfterAdd.date_year, dateAfterAdd.date_month);
    }
    assert((dateAfterAdd.date_month >= 1) && (dateAfterAdd.date_month <= 12));
    assert((dateAfterAdd.date_day >= 1) && (dateAfterAdd.date_day <= 31));
    return dateAfterAdd;
}
 
/*
**@brief:列印日期格式.
**@param: theDate提供列印的日期
**@return:空.
*/
void DisplayDate(Date& theDate)
{
    cout << theDate.date_year << "/" << theDate.date_month 
         << "/" << theDate.date_day << "\t";
    cout << endl << endl;
}
 
 
/*
**@brief:測試用例:測試某年是否為閏年.
**@param: 空.
**@return:空.
*/
void testLeapYearOrNot()
{
    unsigned int iBeginYear=1900;
    unsigned int iEndYear=3000;
 
    cout << "From " << iBeginYear << " To " << iEndYear << endl;
 
    for (unsigned int iYear = iBeginYear; iYear <= iEndYear; ++iYear)
    {
        if (IsLeapYear(iYear))
        {
            cout << iYear << "\t";
        }
    }
    cout << endl;
}
 
 
/*
**@brief:單元測試用例,某年某月有多少天.
**@param: 空.
**@return:空.
*/
void testDaysInMonth()
{
    unsigned int iBeginYear = 1980;
    unsigned int iEndYear = 2020;
    unsigned int iBeginMonth = 1;
    unsigned int iEndMonth = 12;
    unsigned int iDaysOfYear = 0;
 
    for (unsigned int iYear = iBeginYear; iYear <= iEndYear; ++iYear)
    {
        iDaysOfYear = 0;
        cout << "Year:" << iYear << endl;
        for (unsigned int iMonth = iBeginMonth; iMonth <= iEndMonth; ++iMonth)
        {
 
            unsigned int iDaysInmonth = daysInMonth(iYear,iMonth);
            iDaysOfYear += iDaysInmonth;
            cout << iDaysInmonth << "\t";
        }
 
        cout << endl;
        cout << iYear << " has " << iDaysOfYear << " days!" << endl << endl;
    }
}
 
/*
**@brief:單元測試用例,測試幾個月後日期.
**@param: 空.
**@return:空.
*/
void testDataAddmonths()
{
    unsigned int iBeginYear = 2014;
    unsigned int iEndYear = 2015;
 
    unsigned int iBeginMonths = 1;
    unsigned int iEndMonths = 30;
 
    Date beginDate;
    beginDate.date_year = 1980;
    beginDate.date_month = 1;
    beginDate.date_day = 31;
    Date endDate = beginDate;
    DateOp dateOp;
 
    for (unsigned int iYear = iBeginYear; iYear <= iEndYear; ++iYear)
    {
        beginDate.date_year = iYear;
        (void)DisplayDate(beginDate);
        cout << endl;
        for (unsigned int iMonth = 1; iMonth <= MAX_MONTH_CNTS_IN_YEAR; ++iMonth)
        {
            beginDate.date_month = iMonth;
            cout << iMonth << "\t";
            for (unsigned int iMonthCnts = iBeginMonths; iMonthCnts <= iEndMonths; ++iMonthCnts)
            {
                endDate = dateOp.DataAddmonths(beginDate, iMonthCnts);
                cout << " +" << iMonthCnts << " ";
                (void)DisplayDate(endDate);
            }//end for iMonthCnts;
            cout << endl << endl;
        }//end for iMonth
        cout << endl << endl << endl;
    }//end for iYear
}
 
/*
**@brief:單元測試用例,測試給定天後的日期.
**@param: 空.
**@return:空.
*/
void testDataAddDays()
{
    Date beginDate;
    beginDate.date_year = 1988;
    beginDate.date_month = 7;
    beginDate.date_day = 19;
    
    DateOp dateOp;
    cout << "Init Date is :" << endl;
    (void)DisplayDate(beginDate);
 
    Date endDate = beginDate;
 
 
 
    endDate = dateOp.DateAddDays(beginDate, 1); //1988-7-20
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 12); //1988-7-31
    (void)DisplayDate(endDate); 
 
 
    endDate = dateOp.DateAddDays(beginDate, 13); //1988-8-1
    (void)DisplayDate(endDate); 
 
 
    endDate = dateOp.DateAddDays(beginDate, 100); //1988-10-27
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 200); //1989-2-4
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 500); //1989-12-1
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 954); //1991-2-28
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 955); //1991-3-1
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 1000); //1991年4月15日星期一
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 2000); //1994年1月9日星期日
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 5000); //2002年3月28日星期四
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 10000); //2015年12月5日星期六 
    (void)DisplayDate(endDate);
 
    endDate = dateOp.DateAddDays(beginDate, 20000); //2043年4月22日星期三 
    (void)DisplayDate(endDate);
 
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    Date thisDate;
    thisDate.date_year = 2014;
    thisDate.date_month = 2;
    thisDate.date_day = 21;
 
    Date endDate = thisDate;
 
    DateOp thisDateOp(thisDate);
    (void)DisplayDate(thisDate);
 
    //不同類型用例測試.
    (void)testLeapYearOrNot();
    (void)testDaysInMonth();
    (void)testDataAddmonths();
    (void)testDataAddDays();
    return 0;
}           

【按天計算失效日期流程描述】

第一步:确認目前月份到月末還剩餘幾天(記作A),如果傳入的失效天數<剩餘天數,則傳回原有年份、原有月份、新增天數即可;

如果傳入的失效天數>剩餘天數,則記錄下失效天數-剩餘天數A的內插補點(記作B),進入第二步;

第二步:循環判定下個月、下下個月....下Next N個月的總天數之和是否>第一步的內插補點,如果小于,則繼續循環判定;

如果大于,則統計記錄下目前月份、年份,并将月份減去1,并統計出循環的總天數C,進入第三步;

第三步:計算失效月的具體哪一天,根據B-C的值就是失效對應的那一天。

【注意事項】:

需要對月份、天數進行特殊判定,如1<=月份<=12;1<=天<=31;特定月份2月的平年、閏年處理;以及4,6,9.11 30天的處理。

代碼通過VS2010編譯通過,測試案例都沒有bug。有發現bug可以留言,一起改進完善,謝謝!

作者:銘毅天下

轉載請标明出處,原文位址:

http://blog.csdn.net/laoyang360/article/details/42090599

繼續閱讀