【纯干货】超全! 计算天数、计算星期几等日期问题 一文炖!

本文全面解析日期计算的基础知识点,涵盖闰年判断、日期转换、日期间隔计算等实用技巧,通过清晰的代码示例帮助读者掌握日期相关的算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面:

大家好,我是贝贝。昵称 "贝贝今天AC了吗" 是为了督促自己码题,相信圈子里的大家伙都懂喇。我热爱技术、热爱开源、热爱编程。

我始终相信:技术是开源的,知识是共享的

 

博客内均是我平常学习的总结,在便于自己往后回顾之余,也希望向大家分享知识。

每篇文章我都是负责任地总结着,相信您看几篇文章就会发现我的乖巧

 

目前在总结比较基础入门的知识。如果喜欢,不妨关注,我们一起成长。

 

做最乖巧的博客er,做最扎实的程序员

 

关于日期的问题一直是比较恼火的,有点琐碎,这篇文章就总和了一下所有的基础点。都是清晰讲解可读性佳的代码,满满干货!看完希望你能有所收获哟~


目录

一、判断是否为闰年

 

二、给出日期,求此日是本年第几天

方法一:

方法二:

 

三、计算两年份之间所隔天数

 

四、给出一个日期,求是星期几

方法一:直接计算

方法二:基姆拉尔森公式法

方法三:蔡勒公式法

五、给出两个日期,求天数之差

方法1:直接累加推出

方法2:直接计算

方法三:做差

方法四:利用 "" 库计算

end 


 


建议从前面开始顺序阅读哟,因为函数、数组等都是后面会直接调用前面写好的。

一、判断是否为闰年

先来看看闰年(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型变量,用来描述年月日时分秒

       下面只说说这里需要用到的三个:

MemberTypeMeaningRange
tm_mdayintday of the month1 ~ 31
tm_monintmonths since January0 ~ 11
tm_yearintyears 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,做最扎实的程序员 ----

旨在用心写好每一篇文章,平常会把笔记汇总成推送更新~

在这里插入图片描述

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值