前言
虽然这几天很忙,写文档也很花时间,但是还是控制不住想写的心情(Markdown语法好用)。我想介绍另一种更快速的推演日期的方法,同时改进一下日期间隔计算,再把我上一期留下的坑填了……
#往期传送门#
C++之初识“类”:写一个关于日期的类(附两种日期间隔计算)
https://blog.csdn.net/henry_23/article/details/104832525
C++之初识“类” (2):增添“日期”MyDate类的功能
https://blog.csdn.net/henry_23/article/details/104906562
小目录
一些改进
日期间隔计算:代码 / 算法上的一些改进
还是先来说说日期间隔计算,在和同学交流过后,面向过程的算法有了许多可以改进的地方(这里要感谢我的一个室友,每次看他写的代码都会有新收获!)
其实,也不能算是改进吧,大概就是可以少写一些代码的样子;没有仔细研究,所以效率有没有变高不好说。想看最初版本的读者请移步传送门第一篇文章。
首先,同年的时候免去是否同月的讨论,只需要分别计算两个日期到1月1日的天数,然后两者相减即可,如下:
if (ey == cy)
{
int sum1 = 0, sum2 = 0;
for (int i=1; i<cm; i++) sum1 += days(cy,i);
sum1 += cd;
for (int i=1; i<em; i++) sum2 += days(cy,i);
sum2 += ed;
sum = sum2 - sum1;
}
还是在室友的启发下,我发现,也不必判断日期谁先谁后了,只需要判断年份是否相等即可,因为,如果同年,则通过绝对值法可以解决问题;如果跨年,则根据情况用365或者366减去已经求得的日期,即可实现从“一年的剩余天数”到“一年的已过天数”的转换。代码如下:
int MyDate::gap1(const MyDate& date)
{
int sum = 0;
if (year != date.year)
{
//分别计算当前年到年末的天数,和目标年已经走过的天数
sum += (days(year,month)-day);
for(int i = month+1; i <= 12; i++) sum += days(year,i);
sum += date.day;
for(int i = 1; i < date.month; i++) sum += days(date.year,i);
int y1 = year, y2 = date.year;
if (y1 > y2) //如果年份y1>y2,说明目标日期早于当前日期
{
//因此求得的日期需要“转换”
int t = 365 * 2 + isLeap(y1) + isLeap(y2);
sum = t - sum;
swap(y1,y2);
}
for(int i = y1+1; i<y2; i++)
{
if(isLeap(i)==1) sum += 366;
else sum += 365;
}
}
if(year == date.year)
{
int sum1=0,sum2=0;
for(int i=1; i<month; i++) sum1+=days(year,i);
sum1 += day;
for(int i=1; i<date.month; i++) sum2+=days(date.year,i);
sum2 += date.day;
sum = abs(sum2-sum1);
}
return sum;
}
紧接着又想到,利用“减法取反”的思想,代码量还可以减少一些,即分别计算两个日期到当年1月1日的天数,然后对于同年,直接相减取绝对值;对于跨年,根据情况,用365或366减去其中那个较早年份计算得到的结果,即可转化成该日期到年末的天数,再加上中间间隔年份的天数即可完成计算。
int MyDate::gap1(const MyDate& date)
{
int sum = 0, sum1 = 0, sum2 = 0;
for(int i=1; i<month; i++) sum1 += days(year,i);
sum1 += day;
for(int i=1; i<date.month; i++) sum2 += days(date.year,i);
sum2 += date.day;
if (year == date.year) sum = abs(sum2-sum1);
else
{
int y1 = year, y2 = date.year;
if (y1 > y2)
{
swap(y1,y2);
swap(sum1,sum2);
}
sum = sum2 + (365 + isLeap(y1) - sum1);
for (int i = y1+1; i<y2; i++)
{
if (isLeap(i)==1) sum+=366;
else sum+=365;
}
}
return sum;
}
日期推演算法的改进
因为本人比较懒,也不聪明,所以上期该功能的实现是使用while
循环多次执行tomorrow()
或者yesterday()
实现的,效率上很低下。也是在看了室友的代码过后才发现,其实同样使用while
循环,同样是“进位”的思想,就能够实现高效的日期推演。(我在上期说写起来会很复杂,这里打脸了…)
下面放上同学的代码(我对其做了一些变量名的修改)。说明一下,days()
函数,可以返回当前月份的天数,详细情况请查看前篇博客。
forward:日期向后推演
跟着代码走一遍,很容易就能明白,我就不解释了。
void MyDate::forward(int k)
{
int sum_days = k + day;
day += k;
while(day > days())
{
day -= days();
month++;
if(month > 12)
{
year++;
month = 1;
}
}
}
rollback:日期向前推演
这个比forward()
稍微难想那么一点点,毕竟“退位”涉及到“退位”操作,按理说比“进位”稍微难一点。
void MyDate::rollback(int k)
{
while(k >= day)
{
k = k - day;
month--;
if(month<1)
{
year--;
month = 12;
}
day = days();
}
day -= k;
}
rolling 函数,调用上面两者
单独执行上面两个函数,就可以完成日期的向前、向后推演。我套用上期自己写的rolling()
函数的模板,将两个功能整合到了一起,方便使用,同时可以避免直接更改源数据、并且直接输出推演的结果。
至于函数参数的解释,也请参见上一篇博……嗯,我还是简单说一下吧:
rolling()
有两个参数,一个是整型的k
,用来传入需要前进 / 回滚的天数;另一个是默认值为0
的整型参数mode
,这是由于老师的要求“显示指定天数之前/之后的日期”,没有说明是否需要改变原函数,所以设置了它,用来限定是否修改元数据(默认不修改);
void MyDate::rolling1(int k, int mode = 0)
{
if (mode == 0) {
MyDate tmp(year,month,day);
print();
cout<<" 之";
if (k>0) {
tmp.forward(k); cout<<"后 "; }
else {
tmp.rollback(-k); cout<<"前 "; }
cout<<abs(k)<<" 天是 ";
tmp.print();
cout<<endl;
}
else {
if (k>0) forward(k);
else rollback(-k);
}
}
新功能(填上期的坑)
多风格输出
定义了一个静态成员变量style
,用来指定风格,同时拓展了print()
函数的功能。
似乎这里可以在set_style()
里面用枚举型?奈何本人学艺不精,不会使用,有兴趣者可以试试。
void MyDate::set_style(int t) {
style = t;};
void MyDate::print()
{
if (style == 0) cout<<year<<"年"<<month<<"月"<<day<<"日";
else if (style == 1) cout<<year<<"/"<<month<<"/"<<day;
else if (style >1 && style < 4) //2 Amreican 3 English
{
char months_eng[13][15] = {
"0","January","February","March","April",
"May","June","July","August","September","October","November","December"};
if (style == 2) cout<<months_eng[month]<<" "<<day<<", "<<year;
else if (style == 3) cout<<day<<" "<<months_eng[month]<<", "<<year;
}
}
样例展示:
style
=1
:2020/3/12
style
=2
:March 12, 2020
style
=3
:12 March, 2020
当然,也可以加上输出序数词日期的功能:先判断日期是否落在11~13日,如果是,输出th
;如果不是,则用day % 10
求得日期个位,相应地输出输出st
、nd
、rd
或者th
:
char s[4][4]={
"th","st","nd","rd"};
if (style == 4)
{
cout<<months_eng[month]<<" ";
cout<<day;
if(day>10&&day<14) cout<<s[0];
else
{
if (day%10 > 0 && day % 10 < 4) cout<<s[day%10];
else cout<<s[0];
}
cout<<", "<<year;
}
也可以把英文月份换作缩写:
char months_eng_short[13][8] = {
"0","Jan.","Feb.","Mar.",
"Apr.","May","June","July","Aug.","Sept.","Oct.","Nov.","Dec."};
还可以加入对其他语言的支持,或者把输出该日是星期几的功能也整合进去(相应功能见下边)。
今天星期几?
“Which day is it today?” 今天是星期几呢?让我们来写一个函数实现它吧!
先说明一下,这些代码都没有参考网上的代码实现,均是我或者我同学想出来的(当然我同学有没有上网上看就不知道了),有些思想是之前在网上看到过而已,比如求今天是星期几。
如前文所说,根据现有的函数,可以设定一个基准日期,计算与之的日期间隔,然后结果对7求余,同时判断日期是在基准日之前还是之后,继而得出该日期是星期几。
我选了最近的一个星期日,2020年3月15日;当然你也可以选择任何一个日期作为基准日,选星期日可能会方便一点。
get_day()
函数会返回一个1-7的整数,用来表示今天是星期几;比如返回值是1,就表示星期1,返回值是5就表示星期五。
int MyDate::get_day()
{
MyDate date(2020,3,15); //Sunday;
int dif = gap(date);
dif %= 7;
if (dif == 0) return 7;
//current date is later than the standard date:
else if (compare(date)==1) return dif;
else return 7-dif;
}
还要写一个函数,用来输出“用户友好”的内容。这个函数会去访问style
这个静态成员变量,输出中文或者英文的“星期几”。
void MyDate::print_day()
{
if (style == 0 || style == 1)
{
cout<<"星期";
char str[8][4] = {
"一","二","三","四","五","六","日"};
cout<<str[get_day()-1];
}
else
{
char str[7][12]={
"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
cout<<str[get_day()-1];
}
}
结语
至此应该MyDate
类的功能应该是很完善了,一时想不到什么其他功能了,如果有想法欢迎留言交流。我个人感觉能继续做的方面,可以是更加完善风格输出,或者定义一些运算符重载(八成我是不会做了,除非老师布置作业哈)。
放一下现在的MyDate类声明,其中像gap1()
、rolling1()
这种是集成进来的别的同学的功能实现(forward()
、rollback()
也是),最终的运行结果都是一样的。
class MyDate
{
private:
int year, month, day;
static int style;
public:
static void set_style(int t);
void set(int y_, int m_, int d_);
void set(const MyDate& );
int days();
int days(int, int);
void print();
int equals(const MyDate& );
void tomorrow();
int isLeap();
int isLeap(int );
int isLegal();
int compare(const MyDate& );
int get_gap(const MyDate& );
int gap(const MyDate& );
int gap1(const MyDate& );
void forward(int);
void rollback(int);
void rolling(int, int );
void rolling1(int, int );
int get_day();
void print_day();
MyDate yesterday();
MyDate();
MyDate(int y_, int m_, int d_);
};
int MyDate::style = 0;
最后再次感谢和我交流的同学,真的收获良多!