按月、按天计算失效日期的代码实现

简介: 【背景】:在我们计算截止日期、失效时期的时候,可能存在按年、按月、按天统计失效的情况。比如:当前日期是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可以留言,一起改进完善,谢谢!


相关文章
|
3月前
|
JavaScript 前端开发 Serverless
函数计算产品使用问题之打印日志时间与实际时间相差8小时,是什么原因
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
3月前
|
运维 Serverless 网络安全
函数计算产品使用问题之函数运行时间大于24小时该如何解决
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
C语言 C++
C++ 如果设置日期 & 时间基础篇
C++ 如果设置日期 & 时间基础篇
|
Java BI
Java 某个起始时间,固定的累加天数,计算周期
Java 某个起始时间,固定的累加天数,计算周期
158 1
|
存储 Java
java 计算多个时间段中重复的天数
工作中遇到这个需求,自己写了一下,测试了几种情况都没有问题。 这里的时间段我是用的map存储的,map里面是一个key为st的开始时间,一个key为et的结束时间。
【SQL开发实战技巧】系列(十七):数据仓库中时间类型操作(初级)确定两个日期之间的工作天数、计算—年中周内各日期出现次数、确定当前记录和下一条记录之间相差的天数
如何确定两个日期之间的工作日有多少天、计算—年中每周内各日期出现次数、确定当前记录和下一条记录之间相差的天数【SQL开发实战技巧】这一系列博主当作复习旧知识来进行写作,毕竟SQL开发在数据分析场景非常重要且基础,面试也会经常问SQL开发和调优经验,相信当我写完这一系列文章,也能再有所收获,未来面对SQL面试也能游刃有余~。本章节的三个需求:确定两个日期之间的工作天数、计算—年中周内各日期出现次数、确定当前记录和下一条记录之间相差的天数有些许难度,不过建议还是学会比较好。
【SQL开发实战技巧】系列(十七):数据仓库中时间类型操作(初级)确定两个日期之间的工作天数、计算—年中周内各日期出现次数、确定当前记录和下一条记录之间相差的天数
|
C语言 C++
C++ 如果设置日期 & 时间基础篇
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 <ctime> 头文件。
使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
255 0
使用lambda去重、map排序、按任意时间间隔(小时、半小时、分钟)进行结果统计
Java 将两个日期的时间段按照一定天数进行周期切割
Java 将两个日期的时间段按照一定天数进行周期切割
732 0
Java 将两个日期的时间段按照一定天数进行周期切割
|
存储 Unix 关系型数据库
关于日期及时间字段的查询
在项目开发中,一些业务表字段经常使用日期和时间类型,而且后续还会牵涉到这类字段的查询。关于日期及时间的查询等各类需求也很多,本篇文章简单讲讲日期及时间字段的规范化查询方法。
292 0
关于日期及时间字段的查询