编写万年历程序时的一些意外收获

前些天在CSDN每日一练上做到了万年历程序的一道题,觉得很有意思,于是便尝试自己写写看,结果遇到了“公元1年1月1日是星期几”这个问题。拿着手机翻华为日历翻了半天,找到了这一天是星期六:

然而我发现,根据这一结果写出的程序在公元1年的运行结果正确,但在2000多年的运行结果出现了错误:

我检查了代码一段时间,自认为判断闰年的部分没有出错,判断星期几的部分也没有出错,于是怀疑1月1日究竟是否是星期六。我从当前日期向前推算,发现若按当前日期,公元1年1月1日应该是星期一,这与我在手机上查到的结果相矛盾。

之后,我突然想起之前看过的一篇文章,介绍说历史上有一段日期是没有记载的,查了一下,果然发现手机日历上1582年10月4日之后紧跟的是15日而不是5日,这一年10月5日到14日没有在日历上出现:

缺这10天的原因是变更了历法,由儒略历改为格里高里历来消除之前历法产生的误差。然而,算进去这10天之后结果还是不对,减去这10天之后公元1年应该是星期四(星期一往后推10天)。

在翻日历的过程中,我偶然发现日历上100年、200年、300年等的2月都是29天,这和现在的闰年判断规则(四年一闰;百年不闰,四百年再闰)相悖。我推测这是因为制定儒略历的时期人们对于地球公转的测量尚有误差,只是规定4年一闰,没有规定百年不闰,四百年再闰。但实际上地球公转的周期要比365天6时短,若4年一闰,会发现几百年后闰下来多算了天数,所以后来采用了格里高里历,规定百年不闰,四百年再闰。

后来查了以下日历,发现果然从100年、200年、300年...直到1500年这15个整百年都是闰年,但更改历法之后的1700年、1800年、1900年则不是闰年。

根据以上两个发现,我调整了程序,现输出结果如下:

现在的结果应该和日历上保持一致了。 

代码如下(写的很烂,大家见谅):

/**
 * @file demo.c
 * @author ZchenG202 (zhuchenguang202@mails.ucas.ac.cn)
 * @brief This program print calendar for a given month after January of AC 1.
 *        The program considered the change of calendar in October of 1582.
 * @version 2.0
 * @date 2022-07-22
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include <stdio.h>

/**
 * @brief return the number of days of a year
 * 
 * @param y 
 * @return int 
 */
int year(int y)
{
    if (y == 1582)   //10 days were missing in 1582
        return 355;
    else if ((y % 4 == 0 && y < 1582) || (y % 4 == 0 && y % 100 != 0 && y > 1582) || (y % 400 == 0 && y > 1582))
        return 366;
    else
        return 365;
}

int main()
{
    char c;
    do
    {
        int y, month;
        printf("enter month and year (month-year): ");
        scanf("%d-%d", &month, &y);
        getchar();

        int sum = 0;
        int i;
        for (i = 1; i < y; i++) {
            sum += year(i);
        }
    
        int dayofmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        if (year(y) == 366)
            dayofmonth[1] = 29;

        for (i = 1; i < month; i++)
            sum += dayofmonth[i - 1];

        int weekofday1 = (sum + 6) % 7;   // January 1st of AC 1 is Saturday.

        printf("month %d of year %d:\n", month, y);
        printf("SUN MON TUE WED THU FRI SAT\n");
        printf("===========================\n");

        int week = weekofday1;   // week implements what day is ith day
        
        for (i = 0; i < weekofday1; i++)
            printf("    ");
        
        if (month == 10 && y == 1582) {   //days from 5th to 14th were missing in this month
            for (i = 1; i < 5; i++) {
                week++;
                printf("%3d ", i);
            }
            for (i = 15; i <= 31; i++) {
                if (week == 6) {
                    week = 0;
                    printf("%3d\n", i);
                }
                else {
                    week++;
                    printf("%3d ", i);
                }
            }
        }
        else {
            for (i = 1; i <= dayofmonth[month - 1]; i++) {
                if (week == 6) {
                    week = 0;
                    printf("%3d\n", i);
                }
                else {
                    week++;
                    printf("%3d ", i);
                }
            }
        }

        if (week == 0)
            printf("\n");
        else
            printf("\n\n");

        printf("type 0 to exit, or type 1 to continue:");
        c = getchar();
    } while (c == '1');

    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值