问题描述:
已知一个日期以及任意整数n,求n天后的日期。
问题分析:
可通过求每一日的下一日实现,即每向后推移一日就求一次日期,总共需要循环n次求下一日,即可得到已知n天后的日期。故设计函数void NextDay(Date *pd)。此时还需要考虑3种情况:(1)、如果当前日期不是月末(即每月的最后一天),则第二天的日期只需要将当前日期的日子加1即可;(2)、当前日期是月末但月份不是12月,则第二天的日期则是下一个月的1月;(3)、当前日期是月末而且月份是12月,则下一天的日期就是下一年的1月1日。如何判断当前日期是否是所在年所在月的月末呢?还需要知道每个月的天数,每个月的天数还与平年与闰年有关。定义了一个二维数组monthdays[2][13],行标为0的存放平年的个月的天数,行标为1的存放闰年的每个月的天数(每个月的天数有一个记忆口诀:一,三,五,七,八,十,腊(也就是12月)是31天,除二月外剩下都是30天,二月只能28或29天)。且下标从a[1]开始存储。
实现代码:
/*
已知一个日期以及任意整数n,求n天后的日期
*/
#include
#include
typedef struct Date
{
int year;
int month;
int day;
} Date;
int IsLeap(int y);//判断y年份是否为闰年,是则返回1,否则返回0
void NextDay(Date *pd);//将pd所指向的日期变成下一天的日期
int monthdays[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},//1,3,5,7,8,10,腊(也就是12月)是31天
{0,31,29,31,30,31,30,31,31,30,31,30,31}//下标从1开始,第一行为平年月份,第二行为闰年月份
};
int main(void)
{
Date today;//存放当前日期
int n;//存放当前日期向后推移的天数
time_t now;
struct tm *localtp;
time(&now);
localtp=localtime(&now);
printf("本地日期是:%d年%d月%d日\n",localtp->tm_year+1900,localtp->tm_mon+1,localtp->tm_mday);
today.year=localtp->tm_year+1900;
today.month=localtp->tm_mon+1;
today.day=localtp->tm_mday;
printf("请输入向后推移的天数:");
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
NextDay(&today);//通过指针参数返回日期d的下一天日期
}
printf("显示推移后的日期:%d-%02d-%02d\n",today.year,today.month,today.day) ;
return 0;
}
int IsLeap(int y)
{
return y % 400 == 0 || y % 4 == 0 && y % 100 != 0 ;//逻辑表达式返回值只有true or false转换为1或0
}
void NextDay(Date *pd)
{
int leap=0;//初始假设pd->year不是闰年
if(IsLeap(pd->year))
{
leap=1;
}
if(pd->day<monthdays[leap][pd->month])//如果pd所指日期不是月末
{
pd->day++;//下一天日期仅修改日
}
else//pd所指日期是月末
{
if(pd->month<12)//pd所指日期是月末 ,但不是年末,下一天日期为下月1日
{
pd->month++;
pd->day=1;
}
else//所指日期是月末 ,也是年末,则下一天日期变为下一年的1月1日
{
pd->year++;
pd->month=1;
pd->day=1;
}
}
}
运行结果:
此时n=90,故要调用函数NextDay()函数就要90次,当n(向后推移的天数越大时),运行效率越低。
问题分析:
方法二的大的思路就是先算得 当前日期所在年的天数 加上 推移后的天数所获得的总天数,设计了函数int Days(Date dd)来获得总天数,再将总天数一次性转换为对应的年月份,设计了函数void NextNDate(int days,Date *pd),该函数函数体相较方法一有较大改动,该方法运行效率比方法一高,因为不是日期每向后推移一次就做一次日期转换。
实现代码:
#include
#include
typedef struct Date
{
int year;
int month;
int day;
}Date;
int IsLeap(int y);
int Days(Date dd);//计算日期dd是dd.year年的第几天
void NextNDate(int days,Date *pd);//计算pd->year年后days天的日期
int monthdays[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},//1,3,5,7,8,10,腊(也就是12月)是31天
{0,31,29,31,30,31,30,31,31,30,31,30,31}//下标从1开始,第一行为平年月份,第二行为闰年月份
};
int main(void)
{
Date today;//存放当前日期
int n;//存放当前日期向后推移的天数
time_t now;
struct tm *localtp;
time(&now);
localtp=localtime(&now);
printf("本地日期是:%d年%d月%d日\n",localtp->tm_year+1900,localtp->tm_mon+1,localtp->tm_mday);
today.year=localtp->tm_year+1900;
today.month=localtp->tm_mon+1;
today.day=localtp->tm_mday;
printf("请输入向后推移的天数:");
scanf("%d",&n);
int sumdays=Days(today);//求today是today.year年的第几天
NextNDate(sumdays+n,&today);
printf("显示推移后的日期:%d-%02d-%02d\n",today.year,today.month,today.day) ;
return 0;
}
int IsLeap(int y)
{
return y % 400 == 0 || y % 4 == 0 && y % 100 != 0 ;
}
int Days(Date dd)
{
int sum=0,leap=0;
if(IsLeap(dd.year))
{
leap=1;
}
for(int i=1;i<dd.month;i++)//求1月至m-1月的总天数
{
sum+=monthdays[leap][i];
}
sum+=dd.day;
return sum;
}
void NextNDate(int days,Date *pd)
{
int y,yeardays;//y存放当前日期所在年份,yeardays用于判断总天数是否大于当前日期所在年份的总天数
y=pd->year;
yeardays=365;
if(IsLeap(y))
{
yeardays=366;
}
while(days>yeardays)//通过循环执行days减去y年的天数,计算所求日期的年份,若总天数不足一年则跳过该循环
{
days-=yeardays;//不断减少
y++;//y用于最后的赋值
yeardays=365;//重新判断一次自增后的y是平年还是闰年
if(IsLeap(y))
{
yeardays=366;
}
}
int leap=0;
if(IsLeap(y))
{
leap=1;
}
int m=1;//月份从1开始计算,m用于最后的赋值
while(days>monthdays[leap][m])//通过循环执行y(若有执行过上面的while循环,则y是更新后的值)年m月的天数,计算所求日期的月份
{
days-=monthdays[leap][m];//不断减少
m++;
}
pd->year=y;
pd->month=m;
pd->day=days;
}
问题描述:
已知某一日期以及n,求n天前的日期。
问题分析:
算法采用问题一的方法一思路,还是3种情况:(1)、如果当前日期不是每月的1日,则前一天日期只需要将当前日减去1;(2)、如果当前日期是该月的1日,但当前日期所在月份不是1月份,则前一天日期只需要将当前月减1,当前日赋值为前一月的月末值;(3)、如果当前日期是该月的1日,且当前日期所在月份是1月份(某年1月1日),则前一天日期为前一年的12月31日。
实现代码:
/*
已知一个日期以及任意整数n,求n天后的日期
*/
#include
#include
typedef struct Date
{
int year;
int month;
int day;
} Date;
int IsLeap(int y);//判断y年份是否为闰年,是则返回1,否则返回0
void YesterDay(Date *pd);//将pd所指向的日期变成前一天的日期
int monthdays[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},//1,3,5,7,8,10,腊(也就是12月)是31天
{0,31,29,31,30,31,30,31,31,30,31,30,31}//下标从1开始,第一行为平年月份,第二行为闰年月份
};
int main(void)
{
Date today;//存放当前日期
int n;//存放当前日期向后推移的天数
time_t now;
struct tm *localtp;
time(&now);
localtp=localtime(&now);
printf("本地日期是:%d年%d月%d日\n",localtp->tm_year+1900,localtp->tm_mon+1,localtp->tm_mday);
today.year=localtp->tm_year+1900;
today.month=localtp->tm_mon+1;
today.day=localtp->tm_mday;
printf("请输入向前推移的天数:");
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
YesterDay(&today);//通过指针参数返回日期d的下一天日期
}
printf("显示向前推移后的日期:%d-%02d-%02d\n",today.year,today.month,today.day) ;
return 0;
}
int IsLeap(int y)
{
return y % 400 == 0 || y % 4 == 0 && y % 100 != 0 ;//逻辑表达式返回值只有true or false转换为1或0
}
void YesterDay(Date *pd)
{
int leap=0;//初始假设pd->year不是闰年
if(IsLeap(pd->year))
{
leap=1;
}
if(pd->day!=1)//如果pd所指日期不是每月1号
{
pd->day--;//日子减1则是前一天的日期
}
else//pd所指日期是每月1号
{
if(pd->month>1)//但不是1月1号
{
pd->month--;//则某月1日的前一天日期为前一月的月末
pd->day=monthdays[leap][pd->month];
}
else//pd所指日期是1月1号
{
pd->year--;//则1月1日的前一天日期为前一年12月31日
pd->month=12;
pd->day=31;
}
}
}
问题描述:
对于给定的年月值,打印当月的月历表。比如输入两个整数,第一个整数是year(1900<=year<=2099),第二个整数是月份month(1<=month<=12),中间用单个空格隔开,打印输出月历表。
问题分析:
月历(阴历)表由月历头和月历体构成。月历头第一行由输入的月month和年year构成,可定义一个字符型二维数组存放12个月的英文单词,即char months[13][15]。月历头的第二行是一个固定的字符串,直接用printf函数输出。月历主体分为两部分,先输出month月1日前的空白,再输出1号至月末,输出空白和日期时要记录个数,用对7求余思想,控制每行打印7个日期。
实现代码:
#include
int IsLeap(int y);//判断y是否为闰年,是返回1,否则返回0
void WeekValue(int y);//求y年各个月1日的星期值,存到weekofmonth数组里面
void MonCalendar(int y,int m);//打印y年m月的日历表
int monthdays[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; //非闰年每个月的天数
int weekofmonth[13];//存放某年各个月1日是星期几
int main(void)
{
int year,month;//存放要输入的年月值
printf("请输入格式为\"年 月\":");
scanf("%d %d",&year,&month);
WeekValue(year);//用打表法求year年每月1日是星期几
MonCalendar(year,month);
return 0;
}
int IsLeap(int y)
{
return y % 400 == 0 || y % 4 == 0 && y % 100 != 0;
}
void WeekValue(int y)
{
int days=0;//存放y年各个月1日的星期数是1900年到y年的总的天数对7求余所得
for(int i=1900;i<y;i++)
{
if(IsLeap(i))
{
days+=366%7;//days+=2
}
else
{
days+=365%7;//days+=1
}
}
if(IsLeap(y))
{
monthdays[2]=29;
}
for(int i=1;i<=12;i++)
{
weekofmonth[i]=(days+1)%7;//days为前面1900年到y年1月1日的总的天数对7求余的总数,再加1对7求余,即为y年1月1日所在星期数
days+=monthdays[i]; //i=2月1日所在星期数,需要在原来days的基础上再加上1月的天数再加1对7求余 ,得2月1日所在星期数,后面月份以此类推
}
}
void MonCalendar(int y,int m)
{
char months[13][15]={
{" "},{"January"},{"February"},{"March"},{"April"},{"May"},{"June"},{"July"},{"August"},{"September"},{"October"},{"November"},{"December"}
};//二维数组months存放各个月英文单词串
printf("\t%s(%d)\n",months[m],y);
printf(" Sun Mon Tue Wed Thu Fri Sat\n");
for(int i=0;i<weekofmonth[m];i++)//输出1日前的w各空白
{
printf(" ");
}
if(IsLeap(y))
{
monthdays[2]=29;
}
else
{
monthdays[2]=28;
}
int k=weekofmonth[m];//k用于每输出7个日期输出一个换行 ,此处的
初始化为1日前的空格数
for(int i=1;i<=monthdays[m];i++)
{
printf("%4d",i);
k++;//此处的k是空格数加上输出日期数
if(k%7==0)//若k达到7个输出字符(包括空白字符),则输出一个换行符
{
printf("\n");
}
}
printf("\n");
}
输出结果:
代码释疑:
此段程序的核心处理模块,要属void WeekValue(int y)函数,该函数的主要作用就是处理用户输入的year年(局部变量y)的1~12月1日这个日期所对应的是星期几。便于输出函数MonCalendar()打印每月1号前的空白符数量。
for(int i=1900;i
以上代码块的作用即运算1900后n(假设的)年的1月1日取决于前一年的总天数(要么365或者366)%7,即剩余天数将占用后n年的星期数,假设一个例子(我们输入y=1901),1900年为平年,365%7余1,1将占用1901年1月1日的星期一,故1901的1月1日是星期二。
for(int i=1;i<=12;i++)
{
weekofmonth[i]=(days+1)%7;//days为前面1900年到y年1月1日的总的天数对7求余的总数,再加1对7求余,即为y年1月1日所在星期数
days+=monthdays[i]; //i=2月1日所在星期数,需要在原来days的基础上再加上1月的天数再加1对7求余 ,得2月1日所在星期数,后面月份以此类推
}
以上代码块的作用就是操作整型数组weekofmonth[13],从下标为1开始存储,存储的是从1月到12月的1日所在星期数,用整数表示,0->Sunday,1->Monday。。。。
问题描述:
母亲节是每年五月的第二个星期日,针对输入的年份,确定该年的哪一天是母亲节,已知1900年1月1日是星期一。
问题分析:
因为母亲节是每年五月的第二个星期日,所以我们所要求得就是给定年份的五月第二个星期日是几号。经过思考可以找到如下规律:
(如果某年的5月1日有如下情况)
某年5月1日是: | 5月第一周的有前w天在4月 | 4月30号的星期 | 第二个星期日的日期为(两周14天) |
---|---|---|---|
星期一 | 5月第一周有w=0天在4月 | 星期日 | 14-w=14日 |
星期二 | 5月第一周有w=1天在4月 | 星期一 | 14-w=13日 |
星期三 | 5月第一周有w=2天在4月 | 星期二 | 14-w=12日 |
星期四 | 5月第一周有w=3天在4月 | 星期三 | 14-w=11日 |
星期五 | 5月第一周有w=4天在4月 | 星期四 | 14-w=10日 |
星期六 | 5月第一周有w=5天在4月 | 星期五 | 14-w=9日 |
星期日 | 5月第一周有w=6天在4月 | 星期六 | 14-w=8日 |
实现代码:
#include
int IsLeap(int y);
int monthdays[2][13]={//关注1~4月份
{0,31,28,31,30},
{0,31,29,31,30}
};
int main(void)
{
int year;
printf("请输入要求哪一年的母亲节日期:");
scanf("%d",&year);
int days=0;//存放year年4月30日是1900年后总共多少天
for(int i=1900;i<year;i++)
{
if(IsLeap(i))
{
days+=366;
}
else
{
days+=365;
}
}
int leap=0;
if(IsLeap(year))
{
leap=1;
}
for(int i=1;i<5;i++)
{
days+=monthdays[leap][i];
}
int dayofweek=days%7;//计算4月30号是星期几(w=dayofweek=0->Sunday,1->Monday...)
int motherday=(14-dayofweek);//计算母亲节是5月多少日
printf("%d年5月%d日是母亲节\n",year,motherday);
return 0;
}
int IsLeap(int y)
{
if(y % 400 == 0 || y % 4 == 0 && y % 100 != 0)
{
return 1;
}
else
{
return 0;
}
}
问题分析:
假设已知year年的第n周的第d天,先要求出year年第一周有多少天在(year-1)年,即求出(year-1)年12月31日是星期几(w),然后用公式7*(n-1)+da-w,求得year年第n周的第d天是year年的第几天,然后通过while循环执行year年m月天数,计算所求日期的月份。
实现代码:
/*
如果已知某一年第几周的第几天,求这一天的具体日期
*/
#include
int monthdays[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},//1,3,5,7,8,10,腊(也就是12月)是31天
{0,31,29,31,30,31,30,31,31,30,31,30,31}//下标从1开始,第一行为平年月份,第二行为闰年月份
};
int isLeap(int y);
int main(void)
{
int year,n,d;//存放year年第n周第d天
printf("请输入年份以及概念的第几周第几天(3个参数之间用空格隔开):");
scanf("%d %d %d",&year,&n,&d);
int w=0;
for(int i=1;i<year;i++)
{
if(isLeap(i))
{
w+=366%7;
}
else
{
w+=365%7;
}
}
w%=7;//先求出前一年(year-1)12月31日是星期几(w),若是w>0则不是星期日,那么会占用本年的星期数,
int days=(n-1)*7+d-w;//year年第n周第d天的总天数。减去w是减去前一年占用的星期数
int leap=0;
if(isLeap(year))
{
leap=1;
}
int m=1;//m记录月份
while(days>monthdays[leap][m])//days总天数减去每月的天数, 月份m自增1, 当days天数不足月份m自增1所在的天数,days就是几号了
{
days-=monthdays[leap][m];
m++;
}
printf("日期是:%d年%d月%d日\n",year,m,days);
return 0;
}
int isLeap(int y)
{
return y % 400 == 0 || y % 4 == 0 && y % 100 != 0 ;//逻辑表达式返回值只有true or false转换为1或0
}
问题描述:
在西方,星期五和数字13都代表着坏运气,两个不幸的个体最后结合成超级不幸的一天,所以不管哪个月的13日恰逢星期五就叫做“黑色星期五”。对于输入的年份,如果有黑色星期五,则输出黑色星期五具体日期以及个数;如果没有,则输出“本年无黑色星期五”。
问题分析:根据前面的内容,如果知道某日期,那么就能计算出这个日期是星期几。所以以下代码中定义了一个二维数组days[2][13],分别计算year(平年or闰年)中各个月13日是1900年至year年的第几天,最后再判断是否是星期五
实现代码:
#include
int monthdays[2][13]=
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},//1,3,5,7,8,10,腊(也就是12月)是31天
{0,31,29,31,30,31,30,31,31,30,31,30,31}//下标从1开始,第一行为平年月份,第二行为闰年月份
};
int IsLeap(int y);//判断y年份是否为闰年,是则返回1,否则返回0
int DaysF(int year);//判断输出year年中的黑色星期五日期
int main(void)
{
int year,ans=0;//存放年份以及该年黑色星期五的个数
int yeardays[2]={365,366};//存放非闰年,闰年一年的天数
printf("请输入年份:");
scanf("%d",&year);
DaysF(year);
return 0;
}
int IsLeap(int y)
{
return y % 400 == 0 || y % 4 == 0 && y % 100 != 0 ;//逻辑表达式返回值只有true or false转换为1或0
}
int DaysF(int year)
{
int days=0;
if(year<1900)
{
printf("必须大于1900年\n");
return 1;
}
for(int i=1900;i<year;i++) //计算1900年到year年的总天数
{
if(IsLeap(i))//闰年
{
days+=366;
}
else
{
days+=365;
}
}
days+=13;//计算1900年到year年1月13日的总天数
int leap=0,i;
if(IsLeap(year))
{
leap=1;
}
for(int month=1;month<=12;month++)
{
if(days%7==5)
{
printf("%d/%02d/13 是黑色星期五.\n",year,month);
}
//到下月13号的总天数
days+=monthdays[leap][month];
}
return 0;
}