写在前面:
大家好,我是贝贝。昵称 "贝贝今天AC了吗" 是为了督促自己码题,相信圈子里的大家伙都懂喇。我热爱技术、热爱开源、热爱编程。
我始终相信:技术是开源的,知识是共享的。
博客内均是我平常学习的总结,在便于自己往后回顾之余,也希望向大家分享知识。
每篇文章我都是负责任地总结着,相信您看几篇文章就会发现我的乖巧。
目前在总结比较基础入门的知识。如果喜欢,不妨关注,我们一起成长。
做最乖巧的博客er,做最扎实的程序员。
关于日期的问题一直是比较恼火的,有点琐碎,这篇文章就总和了一下所有的基础点。都是清晰讲解和可读性佳的代码,满满干货!看完希望你能有所收获哟~
目录
建议从前面开始顺序阅读哟,因为函数、数组等都是后面会直接调用前面写好的。
一、判断是否为闰年
先来看看闰年(leap year)的定义:
闰年分为两种:
- 普通闰年 : 公历年份是4的倍数的,且不是100的倍数(如2004年就是普通闰年)
- 世纪闰年 : 公历年份是整百数的,必须是400的倍数才是世纪闰年(如1900年不是世纪闰年,2000年是世纪闰年);
闰年和平年的区别在于,二月有 29 天,一年有 366 天。
最基础的,给出一个年份 y 判断是否为闰年:
/* 判断y年是否为闰年
* 是 则返回1 ; 否 则返回0 */
int isLeapYear(int y) {
if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)
return 1;
return 0;
}
注:这里不返回 bool 型而返回 int 型的 0 或 1 是为了减少代码量,在之后用起来很方便。
二、给出日期,求此日是本年第几天
给出年(y)、月(m)、日(d),计算该日是该年第多少天。
🔺简单的思路:
本月之前已经经过 m - 1 个月,本月已经经过 d 天。
那么,天数 = d + (1月到 m-1 月天数)
方法一:
可以通过switch语句实现,只是2月的分支要根据是否为闰年而有所不同。
/* 计算该日是该年第多少天 */
int DayOfYear(int y, int m, int d) {
int num = d;
switch (m - 1) {
case 11:
num += 30;
case 10:
num += 31;
case 9:
num += 30;
case 8:
num += 31;
case 7:
num += 31;
case 6:
num += 30;
case 5:
num += 31;
case 4:
num += 30;
case 3:
num += 31;
case 2:
num += 28 + isLeapYear(y);
case 1:
num += 31;
default:
break;
}
return num;
}
方法二:
本质上和方法一差不多,但是可以简化代码。
将每个月份的天数预先存入数组,由于有闰年和平年之分,我们存储在二位数组中,第一列为平年的数据,第二列为闰年的数据。
/* 平年和闰年每个月的天数 */
int month[13][2] = {
{0, 0}, {31, 31}, {28, 29}, {31, 31}, {30, 30}, {31, 31}, {30, 30},
{31, 31}, {31, 31}, {30, 30}, {31, 31}, {30, 30}, {31, 31}
};
利用已经写好的month数组,再直接计算即可:
/* 计算该日是该年第多少天 */
int DayOfYear(int y, int m, int d) {
int num = d;
for(int i = 1; i < m; i++)
d += month[i][isLeapYear(y)];
return num;
}
三、计算两年份之间所隔天数
直接循环累加 365 或者 366 即可,此处又显示了isLeapYear返回 0 或 1 的简便之处。
/* 计算 y1 年到 y2年 所隔天数*/
int DayBetweenYear(int y1, int y2) {
int num = 0;
for (int i = y1; i < y2; i++)
num += 365 + isLeapYear(i);
return num;
}
四、给出一个日期,求是星期几
方法一:直接计算
这种问题一般会给出一个参照日期,比如说1900年1月1日是星期1。
那么弄懂以上几个问题后,这就比较简单了。
只需要计算该日距离1900年1月1日多少天,再将天数除以7 取余即可。
距离1900年1月1日的天数 = 1900年至该年的天数 + 该日在该年的天数
/* 计算 y 年 m 月 d 日是周几
* 返回值:
* 周一 - 1
* 周二 - 2
* 周三 - 3
* 周四 - 4
* 周五 - 5
* 周六 - 6
* 周日 - 0 */
int Date(int y, int m, int d) {
int num = DayBetweenYear(1900, y) + DayOfYear(y, m, d);
return num % 7;
}
注意:此法只限于求1900年以后的日期。
方法二:基姆拉尔森公式法
其实给定年月日,是有直接的公式计算星期几的。
话不多说,直接上公式:
基姆拉尔森公式(Kim larsen calculation formula)
W = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7
在公式中d表示日期中的日数,m表示月份数,y表示年数。
🔺注意,在公式中有个与其他公式不同的地方:
要把一月和二月看成是上一年的十三月和十四月。
例:如果是2004-1-10则换算成:2003-13-10来代入公式计算。
有了公式后代码就很简单啦!
/* 计算 y 年 m 月 d 日是周几
* 返回值:
* 周一 - 1
* 周二 - 2
* 周三 - 3
* 周四 - 4
* 周五 - 5
* 周六 - 6
* 周日 - 0 */
int Date(int y, int m, int d) {
if(m == 1 || m == 2) {
y --;
m += 12;
}
return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;
}
方法三:蔡勒公式法
和上面大体差不多,就是公式稍微麻烦一点,而且有时间限制。
就不具体展开了,感兴趣可以百度,也是一个可以直接上手码题省去一堆代码的公式。
五、给出两个日期,求天数之差
给出两个日期:y1年m1月d1日 和 y2年m2月d2日。
(默认两个相邻日期是相隔两天)
方法1:直接累加推出
这种方法十分好理解,直接从 y1年m1月d1日 一直往后推,直到日期到了 y2年m2月d2日,这个过程中推后了多少天即为所求。
/* 计算 y1年m1月d1日 到 y2年m2月d2日 所隔天数 */
int Days(int y1, int m1, int d1, int y2, int m2, int d2) {
int ans = 0; // 最终总天数
while (y1 < y2 || m1 < m2 || d1 < d2) {
d1 ++; //往后推一天
/* 满当月天数 */
if(d1 == month[m1][isLeapYear(y1)] + 1){
m1++; //月进1
d1 = 1;
}
/* 满月数 */
if(m1 == 13){
y1++; //年进1
m1 = 1;
}
ans++; //记入一天
}
return ans;
}
方法2:直接计算
y1年m1月d1日 和 y2年m2月d2日
所隔天数 = y1 与 y2年所隔天数 + (m2月d2日在y2年的天数)- (m1月d1日在y1年的天数)+ 1
直接用之前写好的函数就很简单了。
/* 计算 y1年m1月d1日 到 y2年m2月d2日 所隔天数 */
int Days(int y1, int m1, int d1, int y2, int m2, int d2) {
return DayBetweenYear(y1, y2) +
DayOfYear(y1, m1, d1) - DayOfYear(y2, m2, d2) + 1;
}
方法三:做差
分别计算从y1年m1月d1日 和 y2年m2月d2日 距离公元1年1月1日的天数。
/* 以公元 1 月 1 日为基准, 计算经过的日期 */
int getDays(int y, int m, int d) {
int num = 0;
for(int i = 1; i < y; i++)
num += 365 + isLeapYear(i);
for(int i = 1; i < m; i++)
num += month[i][isLeapYear(y)];
num += d;
return num;
}
再将两天数做差即可。
/* 计算 y1年m1月d1日 到 y2年m2月d2日 所隔天数 */
int Days(int y1, int m1, int d1, int y2, int m2, int d2) {
return getDays(y2, m2, d2) - getDays(y1, m1, d1);
}
方法四:利用 "<ctime>" 库计算
这个头文件是关于时间的,c++中有一个 "<ctime>" 库, c中也有 "<time.h>" 库。
下面来讲讲如何利用:
1、有两种记时⏲方式,对应两种储存时间的变量:
🔺 一个是结构体变量:struct tm,这个结构体中有9个int型变量,用来描述年月日时分秒
下面只说说这里需要用到的三个:
Member | Type | Meaning | Range |
tm_mday | int | day of the month | 1 ~ 31 |
tm_mon | int | months since January | 0 ~ 11 |
tm_year | int | years since 1900 |
需要注意的地方表格内我已经标红了,和我们认知的时间有点偏差,月份需要 -1,年份需要 -1900。
🔺 另一个是变量:time_t 类型,描述从1900年至存入的时间经过了多少秒
2、这个头文件里还有两个函数对我们来说有用
🔺 time_t mktime(struct tm* s)
将 struct tm 变量对应的记时方式转化为 time_t 变量对应的计时方式
传入的参数类型必须为struct tm* 类型,返回值类型为time_t型
🔺 double difftime(time_t time1, time_t time2)
返回两个时间time1、time2相差的秒数.
传入的参数类型必须为time_t 类型,返回值类型为double型
那么结合以上两个函数就可以计算两个日期隔了多少秒了!代码如下:
#include <ctime>
/* 计算 y1年m1月d1日 到 y2年m2月d2日 所隔天数 */
int Days(int y1, int m1, int d1, int y2, int m2, int d2) {
struct tm t1 = {0};
struct tm t2 = {0};
t1.tm_year = y1 - 1900, t1.tm_mon = m1 - 1, t1.tm_mday = d1;
t1.tm_year = y2 - 1900, t1.tm_mon = m2 - 1, t1.tm_mday = d2;
double seconds = difftime(mktime(&t2), mktime(&t1)); //获取相隔秒数
return seconds / (60 * 60 * 24); //将秒数转化为天数
}
end
欢迎关注个人公众号“ 鸡翅编程 ”,这里是认真且乖巧的码农一枚。
---- 做最乖巧的博客er,做最扎实的程序员 ----
旨在用心写好每一篇文章,平常会把笔记汇总成推送更新~