刷一些算法题时总能遇到计算日期间天数的问题,每每遇到这种情况,不是打开excel就是用系统自带的计算器。私以为这种问题及其简单以至于不需要自己动脑子,只要会调用工具就好。直到近些天在写一个日历程序的时候遇到了这个问题,不调用别人的API,那就只能自己动手了。
一、概述
天数计算问题的解法大致分为两类。一类是直接计算日期间的差值,另一类先是分别求得该日期到某一特殊时间点的差值,再两个差值相减得到两个日期间的差值。目前网络上大多都是第一类解法,直接从公元元年开始循环,简单粗暴。而另一种就较为少见了,得定义结构体等一系列繁琐的操作,结构较为臃肿。所以,如何在尽可能偷懒的情况下又使得代码变得优雅呢?在我试验过了多种不同的结构体后,我还是(无奈地)选择了<time.h>。
二、约束
此程序假定日期2018-1-1与日期2018-1-3的差值为一天。当然,为两天也没问题,在代码上做少量的改动就行[滑稽]。
三、关于<time.h>
作为C语言标准库里的时间和日期头文件,<time.h>也集成了一些简单、常用的类型及函数。虽然和JAVA等语言还是显得有些简陋,不过一个支点都能撬动地球,这点东西也够进行复杂的操作了。
类型:time_t 表示时间的算术类型(日历时间)
而time_t的定义:
-
-
-
-
typedef __time32_t time_t; -
-
typedef __time64_t time_t; -
-
__time32_t和__time64_t的定义:
-
#ifndef _TIME32_T_DEFINED -
#define _TIME32_T_DEFINED -
-
-
-
#ifndef _TIME64_T_DEFINED -
#define _TIME64_T_DEFINED -
__MINGW_EXTENSION typedef __int64 __time64_t; -
-
#define __int64 long long 所以绕来绕去,time_t就是一个long类型的变量,记录的是从1970年1月1日00:00:00(UTC)开始,到目前为止经过的秒数,即日历时间。
类型:struct tm 用于保存组成日历时间各个部分的结构类型
日历的各个组成部分被称为分解时间(broken-down time)。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
函数:time_t mktime(struct tm *tmptr) 将tmptr指向的分解时间转换为日历时间
四、思路
在获得两组正确的时间后,分别求该日期所对应的日历时间,再相减,除以一天的秒数,即得到天数差值。
主要模块:
-
bool review(const char str[][11]); -
int error(int i,int result); -
int timeparse(const char str[][11]); -
time_t toTime_t(int year,int month,int day); -
-
-
-
-
-
-
-
-
-
-
printf("Please input a date,like xxxx xx xx:(%d/2)\n",i+1); -
-
-
}while(review((const char(*)[11])str)==false); -
-
second = timeparse((const char(*)[11])str); -
second = second>0?second:-second; -
-
printf("day = %d\n",day-1); -
-
-
bool review(const char str[][11]); //对输入日期进行验证
int error(int i,int result); //日期错误信息提示
int timeparse(const char str[][11]); //对输入日期进行解析,为运算的主要函数
time_t toTime_t(int year,int month,int day); //被int timeparse()调用,得到具体日历时间的子函数
五、实现
1.头文件的导入及宏
-
-
-
-
2.日期的输入及验证
用个一do…while()结构接受日期,直到日期正确跳出循环。review函数对输入的日期进行验证,年份不得小于1900,天数得在该月所有天数内。对闰月的验证在天数验证的模块内。因为只需要看日期是否合法,所以并不用纠结日期内有几个闰月。
-
int month_day[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; -
-
-
-
-
-
-
printf("Please input a date,like xxxx xx xx:(%d/2)\n",i+1); -
-
-
}while(review((const char(*)[11])str)==false); -
bool review(const char str[][11]) -
-
-
-
-
-
-
sscanf(str[i],"%d %d %d",&arr[i][0],&arr[i][1],&arr[i][2]); -
-
-
-
-
-
-
if(arr[i][1]>12 || arr[i][1]<1) -
-
if(arr[i][2]>month_day[arr[i][1]]) -
-
if((arr[i][0]%4==0 && arr[i][0]%100!=0) || arr[i][0]%400==0) -
if(arr[i][1]==2 && arr[i][2]==month_day[arr[i][1]]+1) -
-
-
-
-
-
-
-
-
-
-
-
int error(int i,int result) -
-
-
-
-
-
-
printf("第%d个日期%s\n",i+1,arr[result]); -
-
-
3.日期的计算
timeparse()得到两个日期间相差的秒数,再对秒数做处理(转为天数)。
-
-
-
-
second = timeparse((const char(*)[11])str); -
second = second>0?second:-second; -
-
printf("day = %d\n",day-1); toTime_t()求得一个日期的日历时间(s)。
-
int timeparse(const char str[][11]) -
-
-
-
-
-
sscanf(str[i],"%d %d %d",&year,&month,&day); -
s[i] = (int)toTime_t(year,month,day); -
-
-
-
-
-
time_t toTime_t(int year,int month,int day) -
-
-
ti.tm_year = year - 1900; -
-
-
-
-
六、总结
总共100行的代码,日期检验的子函数就占了40行,还有很大的优化空间。并且受制于time_t的特性,该程序并不能计算1900年前的日期,不具有通用性。另外关于相邻日期间隔几天的不同定义,在主函数的输出里做了处理,如果认为1-1与1-3相隔两天的话,把-1去掉就行。