本文主要是笔者在工作当中遇到的问题进行处理过程中的一些心得和思考,希望对各位看官有所帮助。
背景
有一个测量某个量测量(可以是温度、电压等)的测量点,当它所测的量测量超过(或低于)某个限值的时候,称为越限,此时测量点向控制端返回值“1”,否则返回“0”,在平面直角坐标系中的表示如下图所示:
如上图所示,当量测量由“0”变为“1”时,记此时时刻为越限的起始时间,标记为“StartTime”,当持续的量测量值由“1”变为“0”时,记此时时刻为越限的终止时间,标记为“EndTime”,现在要求我们统计一年中每个月份内的越限时长。
面对上述问题,如果所有越限的行为都发生在某一个月内,那么问题的处理相对简单很多,可以用引用Ctime头文件中包含的tm结构将所有越限的EndTime和StartTime转化成距1970年的毫秒数,相减得到一个月内所有越限的持续时间,然后相加求和得到当月的越限总时长,但是假如一个越限行为在某一月内的某个时间点发生,然后持续到下月或者数月甚至下年或者数年后才结束,由于在越限发生的月份的月末(即当月左后一天的23时59分59秒)没有返回EndTime,所以显然不能用之前我们所说的方法进行计算,所以我们在计算发生跨越月份的越限总时长时会丢失掉从越限开始到月末这一段时间。
为了解决这个问题,当一个跨月的越限在某个月内发生并持续到当月月末时,我们可以人为地将当月月末设定为EndTime,下个月的1日0时0分0秒设定为StartTime,这个过程持续到这个越限的真正的EndTime为止,并分段计算各个月份内的越限时长。
DateTime类构造
解决这个问题采用了C++工具来解决这个问题,为了方便我们去描述时间这个概念,针对题目设计了一个DateTime类,在设计前,笔者查阅了其他作者写的关于C++环境下实现DateTime类的博客,但是很多博客都只是将DateTime或者Date精确到了天数的精度下,不满足题目需求,所以构造了精确到秒的DateTime类,以简化计算。
Date类的构造代码如下:(重中之重是类中AddDate()成员函数)
class DateTime
{
private:
int _second;
int _minute;
int _hour;
int _day;
int _month;
int _year;
public:
DateTime(int year=0,int month=0,int day=0,int hour=0,int minute=0,int second=0)
:_year(year)
,_month(month)
,_day(day)
,_hour(hour)
,_minute(minute)
,_second(second)
{}
void SetDate()
{
int year,month,day,hour,minute,second;
cout<<"请输入年份:";
cin>>year;
cout<<"请输入月份:";
cin>>month;
cout<<"请输入天数:";
cin>>day;
cout<<"请输入小时:";
cin>>hour;
cout<<"请输入分钟:";
cin>>minute;
cout<<"请输入秒数:";
cin>>second;
start1:
if(month<1 || month>12)
{
MessageBeep(0);
cout<<"月份输出错误,请重新输入"<<endl;
cin>>month;
goto start1;
}
start2:
if(day<1 || day>31)
{
MessageBeep(0);
cout<<"日期输入错误,请重新输入"<<endl;
cin>>day;
goto start2;
}
start3:
if(hour<1 || hour>24)
{
MessageBeep(0);
cout<<"小时输入错误,请重新输入"<<endl;
cin>>hour;
goto start3;
}
start4:
if(minute<1 || minute>60)
{
MessageBeep(0);
cout<<"分钟输入错误,请重新输入"<<endl;
cin>>minute;
goto start4;
}
start5:
if(second<1 || minute>60)
{
MessageBeep(0);
cout<<"秒种输入错误,请重新输入"<<endl;
cin>>second;
goto start5;
}
if(month==2)
{
if((year%4==0&&year%100!=0) || year%400==0)
{
if(day>29)
{
MessageBeep(0);
cout<<"月份输出错误,请重新输入"<<endl;
cin>>month;
goto start1;
}
}
else if(day>28)
{
MessageBeep(0);
cout<<"月份输出错误,请重新输入"<<endl;
cin>>month;
goto start1;
}
}
_year=year;
_month=month;
_day=day;
_hour=hour;
_minute=minute;
_second=second;
}
void DisplayDate()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<"-"<<_hour<<"-"<<_minute<<"-"<<_second<<endl;
}
int Getlastday()
{
int num;
switch(_month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
num=31;
break;
case 4:
case 6:
case 9:
case 11:
num=30;
break;
case 2:
num=28+(((this->_year%4==0&&this->_year%100!=0)||this->_year%400==0)?1:0);
}
return num;
}
void AddDate()
{
if(_second<60)
_second++;
else
{
_second=0;
if(_minute<60)
_minute+=1;
else
{
_minute=0;
if(_hour<24)
_hour+=1;
else
{
_hour=0;
if(_day<Getlastday())
_day+=1;
else
{
if(_month<12)
_month+=1;
else
{
_month=1;
_year+=1;
}
}
}
}
}
}
int getyear(void);
void setyear(int year1);
int getmonth(void);
void setmonth(int month1);
int getday(void);
void setday(int day1);
int gethour(void);
void sethour(int hour1);
int getminute(void);
void setminute(int minute1);
int getsecond(void);
void setsecond(int second1);
DateTime& operator=(const DateTime& d);
DateTime& operator++();
DateTime operator++(int);
DateTime operator+(int days);
int operator-(DateTime& d);
friend int SubDatedays(DateTime &x,DateTime &y);
bool operator==(const DateTime& d);
bool operator!=(const DateTime& d);
bool operator>(const DateTime d);
bool operator<(const DateTime d);
};
DateTime& DateTime::operator=(const DateTime& d)
{
this->_year=d._year;
this->_month=d._month;
this->_day=d._day;
this->_hour=d._hour;
this->_minute=d._minute;
this->_second=d._second;
return *this;
}
DateTime& DateTime::operator++()
{
this->AddDate();
return *this;
}
DateTime DateTime::operator++(int)
{
DateTime temp(*this);
this->AddDate();
return temp;
}
DateTime DateTime::operator+(int days)
{
DateTime temp(*this);
for(int i=0;i<days;i++)
this->AddDate();
return temp;
}
int DateTime::operator-(DateTime& y)
{
DateTime x=*this;
DateTime d;
if(y._year>x._year)
{
d=x;
x=y;
y=d;
}
if(y._year==x._year)
{
if(y._month>x._month)
{
d=x;
x=y;
y=d;
}
}
if(y._year==x._year)
{
if(y._month==x._month)
{
if(x._day>y._day)
{
d=x;
x=y;
y=d;
}
}
}
int days=0;
for(int i=y._year;i<x._year;i++)
{
if((i%4==0&&i%100!=0) || i%400==0)
days+=366;
else
days+=365;
}
for(int j=1;j<y._month;j++)
{
if(j==1||j==3||j==5||j==7||j==8||j==10||j==12)
days-=31;
else if(j==2)
{
if((y._year%4==0&&y._year%100!=0) || y._year%400==0)
days=-29;
else
days-=28;
}
else
days-=30;
}
days-=y._day;
for(int k=1;k<x._month;k++)
{
if(k==1 || k==3 || k==5 || k==7 || k==8 || k==10 || k==12)
days+=31;
else if(k==2)
{
if((x._year%4==0&&x._year%100!=0) || x._year%400==0)
days+=29;
else
days+=28;
}
else
days+=30;
}
days+=x._day;
return days;
}
int SubDatedays(DateTime &x, DateTime &y)
{
DateTime d;
if(y._year>x._year)
{
d=x;
x=y;
y=d;
}
if(y._year==x._year)
{
if(y._month>x._month)
{
d=x;
x=y;
y=d;
}
}
if(y._year==x._year)
{
if(y._month==x._month)
{
if(y._day>x._day)
{
d=x;
x=y;
y=d;
}
}
}
int days=0;
for(int i=y._year;i<x._year;i++)
{
if((i%4==0&&i%100!=0) || i%400==0)
days+=366;
else
days+=365;
}
for(int j=1;j<y._month;j++)
{
if(j==1||j==3||j==5||j==7||j==8||j==10||j==12)
days-=31;
else if(j==2)
{
if((y._year%4==0&&y._year%100!=0) || y._year%400==0)
days=-29;
else
days-=28;
}
else
days-=30;
}
days-=y._day;
for(int k=1;k<x._month;k++)
{
if(k==1 || k==3 || k==5 || k==7 || k==8 || k==10 || k==12)
days+=31;
else if(k==2)
{
if((x._year%4==0&&x._year%100!=0) || x._year%400==0)
days+=29;
else
days+=28;
}
else
days+=30;
}
days+=x._day;
return days;
}
bool DateTime::operator==(const DateTime& d)
{
DateTime x(*this);
if(x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour==d._hour&&x._minute==d._minute&&x._second==d._second)
return 1;
else
return 0;
}
bool DateTime::operator!=(const DateTime& d)
{
DateTime x(*this);
if(x._year!=d._year || x._month!=d._month || x._day!=d._day || x._hour!=d._hour || x._minute!=d._minute || x._second!=d._second)
return 1;
else
return 0;
}
bool DateTime::operator>(const DateTime d)
{
DateTime x(*this);
if(x._year>d._year || x._year==d._year&&x._month>d._month || x._year==d._year&&x._month==d._month&&x._day>d._day || x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour>d._hour || x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour==d._hour&&x._minute>d._minute || x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour==d._hour&&x._minute==d._minute&&x._second>d._second )
return 1;
else
return 0;
}
bool DateTime::operator<(const DateTime d)
{
DateTime x(*this);
if(x._year<d._year || x._year==d._year&&x._month<d._month || x._year==d._year&&x._month==d._month&&x._day<d._day || x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour<d._hour || x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour==d._hour&&x._minute<d._minute || x._year==d._year&&x._month==d._month&&x._day==d._day&&x._hour==d._hour&&x._minute==d._minute&&x._second<d._second )
return 1;
else
return 0;
}
void DateTime::setyear(int year1)
{
_year=year1;
}
int DateTime::getyear(void)
{
return _year;
}
void DateTime::setmonth(int month1)
{
_month=month1;
}
int DateTime::getmonth(void)
{
return _month;
}
void DateTime::setday(int day1)
{
_day=day1;
}
int DateTime::getday(void)
{
return _day;
}
void DateTime::sethour(int hour1)
{
_hour=hour1;
}
int DateTime::gethour(void)
{
return _hour;
}
void DateTime::setminute(int minute1)
{
_minute=minute1;
}
int DateTime::getminute(void)
{
return _minute;
}
void DateTime::setsecond(int second1)
{
_second=second1;
}
int DateTime::getsecond(void)
{
return _second;
}
越限信号的主题函数部分
在主体函数中最重要的部分就是重载了“++”单目运算符,使其能够对DateTime类的对象进行操作,以便我们能够通过for循环语句进行时间段分区统计。主体函数头部语句:
int AccuTime=0;
DateTime StartTime,EndTime,iStartTime,iEndTime;
DateTime p;
cout<<"请输入越限起始时间:"<<endl;
StartTime.SetDate();
cout<<"请输入越限结束时间:"<<endl;
EndTime.SetDate();
主体函数中主要分为以下几种情况:
1.越限的起始和结束在一个月内完成
if(StartTime.getyear()==EndTime.getyear()&&StartTime.getmonth()==EndTime.getmonth())
{
cout<<"所选站点在第"<<StartTime.getmonth()<<"月的越限起始时间是:" <<endl;
StartTime.DisplayDate();
cout<<"所选站点在第"<<StartTime.getmonth()<<"月的越限结束时间是:" <<endl;
EndTime.DisplayDate();
for(p=StartTime;p<EndTime;p++)
{
AccuTime+=1;
Timeinterval+=1;
}
cout<<"所选站点在第"<<StartTime.getmonth()<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
2.越限的起始和结束不在一个月内但是在一年中
else if(StartTime.getyear()==EndTime.getyear()&&StartTime.getmonth()<EndTime.getmonth())
{
for(int k=StartTime.getmonth();k<=EndTime.getmonth();k++)
{
if(k==StartTime.getmonth())
{
/*先计算越限开始时间内的月份的越限情况*/
cout<<"所选站点在第"<<StartTime.getmonth()<<"月的越限起始时间是:" <<endl;
StartTime.DisplayDate();
iEndTime.setyear(StartTime.getyear());
iEndTime.setmonth(StartTime.getmonth());
iEndTime.sethour(23);
iEndTime.setminute(59);
iEndTime.setsecond(59);
if(StartTime.getmonth()==1 || StartTime.getmonth()==3 || StartTime.getmonth()==5 || StartTime.getmonth()==7 || StartTime.getmonth()==8 || StartTime.getmonth()==10 || StartTime.getmonth()==12)
{
iEndTime.setday(31);
}
else if(StartTime.getmonth()==4 || StartTime.getmonth()==6 || StartTime.getmonth()==9 || StartTime.getmonth()==11)
{
iEndTime.setday(30);
}
else if(((StartTime.getyear()%4==0&&StartTime.getyear()%100!=0) || StartTime.getyear()%400==0) && StartTime.getmonth()==2)
{
iEndTime.setday(29);
}
else if(StartTime.getyear()%4!=0 && StartTime.getmonth()==2)
{
iEndTime.setday(28);
}
cout<<"所选站点在第"<<StartTime.getmonth()<<"月的越限结束时间是:" <<endl;
iEndTime.DisplayDate();
AccuTime=0;
for(p=StartTime;p<iEndTime;p++)
{
AccuTime+=1;
Timeinterval+=1;
}
cout<<"所选站点在第"<<StartTime.getmonth()<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
else if(k==EndTime.getmonth())
{
iStartTime.setyear(EndTime.getyear());
iStartTime.setmonth(EndTime.getmonth());
iStartTime.setday(1);
iStartTime.sethour(0);
iStartTime.setminute(0);
iStartTime.setsecond(0);
cout<<"所选站点在第"<<EndTime.getmonth()<<"月的越限起始时间是:" <<endl;
iStartTime.DisplayDate();
cout<<"所选站点在第"<<EndTime.getmonth()<<"月的越限结束时间是:" <<endl;
EndTime.DisplayDate();
AccuTime=0;
for(p=iStartTime;p<EndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<EndTime.getmonth()<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
else
{
/*计算越限起始月份后面的月份*/
iStartTime.setyear(StartTime.getyear());
iStartTime.setmonth(k);
iStartTime.setday(1);
iStartTime.sethour(0);
iStartTime.setminute(0);
iStartTime.setsecond(0);
iEndTime.setyear(StartTime.getyear());
iEndTime.setmonth(k);
iEndTime.sethour(23);
iEndTime.setminute(59);
iEndTime.setsecond(59);
if(k==1 || k==3 || k==5 || k==7 || k==8 || k==10 || k==12)
{
iEndTime.setday(31);
}
else if(k==4 || k==6 || k==9 || k==11)
{
iEndTime.setday(30);
}
else if(((StartTime.getyear()%4==0&&StartTime.getyear()%100!=0) || StartTime.getyear()%400==0) && k==2)
{
iEndTime.setday(29);
}
else if(StartTime.getyear()%4!=0 && k==2)
{
iEndTime.setday(28);
}
cout<<"所选站点在第"<<iStartTime.getmonth()<<"月的越限起始时间是:" <<endl;
iStartTime.DisplayDate();
cout<<"所选站点在第"<<iEndTime.getmonth()<<"月的越限结束时间是:" <<endl;
iEndTime.DisplayDate();
AccuTime=0;
for(p=iStartTime;p<iEndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<iStartTime.getmonth()<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
}
}
3.越限的起始和终止不再同一年中
else if(StartTime.getyear()<EndTime.getyear())
{
for(int n=StartTime.getyear();n<=EndTime.getyear();n++)
{
/*越限起始当年*/
if(n==StartTime.getyear())
{
for(int k=StartTime.getmonth();k<=12;k++)
{
if(k==StartTime.getmonth())
{
/*越限起始所在的月份*/
iEndTime.setyear(StartTime.getyear());
iEndTime.setmonth(StartTime.getmonth());
iEndTime.sethour(23);
iEndTime.setminute(59);
iEndTime.setsecond(59);
if( StartTime.getmonth()==1 || StartTime.getmonth()== 3 || StartTime.getmonth()==5 || StartTime.getmonth()==7 || StartTime.getmonth()==8 || StartTime.getmonth()==10 || StartTime.getmonth()==12)
{
iEndTime.setday(31);
}
else if(StartTime.getmonth()==4 || StartTime.getmonth()==6 || StartTime.getmonth()==9 || StartTime.getmonth()==11)
{
iEndTime.setday(30);
}
else if(((StartTime.getyear()%4==0&&StartTime.getyear()%100!=0) || StartTime.getyear()%400==0)&&StartTime.getmonth()==2)
{
iEndTime.setday(29);
}
else if(StartTime.getyear()%4!=0&&StartTime.getmonth()==2)
{
iEndTime.setday(28);
}
cout<<"所选站点在第"<<k<<"月的越限起始时间是:"<<endl;
StartTime.DisplayDate();
cout<<"所选站点在第"<<k<<"月的越限结束时间是:"<<endl;
iEndTime.DisplayDate();
AccuTime=0;
for(p=StartTime;p<iEndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<k<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
else
{
iStartTime.setyear(n);
iStartTime.setmonth(k);
iStartTime.setday(1);
iStartTime.sethour(0);
iStartTime.setminute(0);
iStartTime.setsecond(0);
iEndTime.setyear(n);
iEndTime.setmonth(k);
iEndTime.sethour(23);
iEndTime.setminute(59);
iEndTime.setsecond(59);
if(k==1 || k==3 || k==5 || k==7 || k==8 || k==10 || k==12)
{
iEndTime.setday(31);
}
else if(k==4 || k==6 || k==9 || k==11)
{
iEndTime.setday(30);
}
else if(((n%4==0&&n%100!=0) || n%400==0)&&k==2)
{
iEndTime.setday(29);
}
else if((n%4!=0)&&k==2)
{
iEndTime.setday(28);
}
cout<<"所选站点在第"<<k<<"月的越限起始时间是:"<<endl;
iStartTime.DisplayDate();
cout<<"所选站点在第"<<k<<"月的越限结束时间是:"<<endl;
iEndTime.DisplayDate();
AccuTime=0;
for(p=iStartTime;p<iEndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<k<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
}
}
/*越限结束的当年*/
else if(n==EndTime.getyear())
{
for(int k=1;k<=EndTime.getmonth();k++)
{
if(k==EndTime.getmonth())
{
iStartTime.setyear(n);
iStartTime.setmonth(k);
iStartTime.setday(1);
iStartTime.sethour(0);
iStartTime.setminute(0);
iStartTime.setsecond(0);
cout<<"所选站点在第"<<k<<"月的越限起始时间是:"<<endl;
iStartTime.DisplayDate();
cout<<"所选站点在第"<<k<<"月的越限结束时间是:"<<endl;
EndTime.DisplayDate();
AccuTime=0;
for(p=iStartTime;p<EndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<k<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
else
{
iStartTime.setyear(n);
iStartTime.setmonth(k);
iStartTime.setday(1);
iStartTime.sethour(0);
iStartTime.setminute(0);
iStartTime.setsecond(0);
iEndTime.setyear(n);
iEndTime.setmonth(k);
iEndTime.sethour(23);
iEndTime.setminute(59);
iEndTime.setsecond(59);
if(k==1 || k==3 || k==5 || k==7 || k==8 || k==10 || k==12)
{
iEndTime.setday(31);
}
else if(k==4 || k==6 || k==9 || k==11)
{
iEndTime.setday(30);
}
else if(((n%4==0&&n%100!=0) || n%400==0)&&k==2)
{
iEndTime.setday(29);
}
else if(n%4!=0&&k==2)
{
iEndTime.setday(28);
}
cout<<"所选站点在第"<<k<<"月的越限起始时间是:"<<endl;
iStartTime.DisplayDate();
cout<<"所选站点在第"<<k<<"月的越限结束时间是:"<<endl;
iEndTime.DisplayDate();
AccuTime=0;
for(p=iStartTime;p<iEndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<k<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
}
}
/*越限起始和终止之间的年份*/
else
{
for(int k=1;k<=12;k++)
{
iStartTime.setyear(n);
iStartTime.setmonth(k);
iStartTime.setday(1);
iStartTime.sethour(0);
iStartTime.setminute(0);
iStartTime.setsecond(0);
iEndTime.setyear(n);
iEndTime.setmonth(k);
iEndTime.sethour(23);
iEndTime.setminute(59);
iEndTime.setsecond(59);
if(k==1 || k==3 || k==5 || k==7 || k==8 || k==10 || k==12)
{
iEndTime.setday(31);
}
else if(k==4 || k==6 || k==9 || k==11)
{
iEndTime.setday(30);
}
else if(((n%4==0&&n%100!=0) || n%400==0)&&k==2)
{
iEndTime.setday(29);
}
else if(n%4!=0&&k==2)
{
iEndTime.setday(28);
}
cout<<"所选站点在第"<<k<<"月的越限起始时间是:"<<endl;
iStartTime.DisplayDate();
cout<<"所选站点在第"<<k<<"月的越限结束时间是:"<<endl;
iEndTime.DisplayDate();
AccuTime=0;
for(p=iStartTime;p<iEndTime;p++)
{
AccuTime+=1;
}
cout<<"所选站点在第"<<k<<"月内的越限时长:"<<AccuTime/60<<"分"<<endl;
}
}
}
}
结论
计算结果如下:
假设发生一越限,起始时间StartTime设为2018年12月12日12时12分12秒,终止时间EndTime设为2019年3月3日3时3分3秒,可见其越限持续时间内发生了跨月和跨年两种行为,计算结果准确完成了我们的要求,可见编码执行成功。
希望本文对在学习路上的你有所帮助。