Chinaunix首页 | 论坛 | 博客
  • 博客访问: 76073
  • 博文数量: 7
  • 博客积分: 1415
  • 博客等级: 上尉
  • 技术积分: 100
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-15 22:11
文章存档

2009年(2)

2008年(4)

2007年(1)

我的朋友

分类: C/C++

2008-05-10 17:46:24

要计算某年某月某日(未知量)是星期几,我们应根据已知量(如:)进行推算。

以下是本人推导的思路:
假设: oyears表示两个年份之间相差的年数; oleaps表示两个年份之间拥有的闰年数;odays表示某月某日相对于1月1日的相差天数;  
      baseyear表示已知年份(如:2000年); basewk表示对应的已知星期(如:2000年1月1日是星期六)
      alldays两个日期间相差的总天数; wkn表示计算结果.

则有:
1.  wkn = (alldays + basewk) % 7
2.  当不考虑闰年时:
        alldays = 365*oyears + odays
展开常数365 -->
        alldays = (52*7 + 1)*oyears +odays
由于一个星期的周期为7天 -->
        alldays = oyears + odays
3.  考虑闰年:
因为闰年比平年多1天,所以在2所得的结果上加上闰年数  -->
        alldays = oyears + oleaps + odays
4.  把3的结果代入1  -->
       公式: wkn = (oyears + oleaps + odays + basewk) % 7

    所以只需根据已知量baseyear 和 basewk计算出oyears ,oleaps ,odays这三个偏移量,然后代入公式,便求得了计算结果。

   
    下面这个程序是根据这种思路实现的,理论上可以计算 -2^31 -> 2^31-1 年间的任何一个日期是星期几,消耗的计算机时间很少,与日期大小无关。
    注:由于历史原因,当时古人制定阳历规则的时候,取地球的公转时间的精度不高,导致产生10天偏差,所以把1752年9月3号后的10天忽略了。因此本程序对1752年9月14日以后及3800年以前的推算才是准确的(即使我们现在用的闰年规则,也会导致3800年产生1天的偏差,这与地球的公转时间精度相关)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define BASEYEAR     2000    // base year
#define BASEWN        6        // base week# (2000.1.1 is Saturday)

#define IS_LEAP(y) (!(y % 4) && (y % 100)) || !(y % 400)    // is leap year?

long leaps(long sml, long big);
int getweekn(long year, int mon, int day);

// #days from the first day of year(January.1) to month#.1
int mondays[2][13] = {
    // is not leap year
    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
    // is leap year
    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
};


// get week# of year#.month#.day#
// return week# on success, or not return -1
int getweekn(long year, int mon, int day)
{
    long        oyears;    // year offset
    int            odays;    // days offset
    long        oleaps = 0;    // leap years
    int            wkn;        // the result week#
    int            is;            // is leap year? yes: is=1 or no: is=0

    
    is = IS_LEAP(year);

    // incorrect month#
    if (mon < 1 || mon > 12){    
        return -1;
    }
    // incorrect day#
    if ((day < 1) || (day > (mondays[is][mon] - mondays[is][mon - 1]))){
        return -1;
    }
    
    // get wkn
    if (year >= BASEYEAR){
        // here: off_year, offdays, lps are positive value
        oyears = (year - BASEYEAR) % 7;                
        odays = mondays[is][mon - 1] + (day - 1);
        oleaps = leaps(BASEYEAR, year - 1);                
        wkn = (oyears + odays + oleaps + BASEWN) % 7;
    } else{
        // here: off_year, offdays, lps are negative value
        oyears = ((year + 1) - BASEYEAR) % 7;
        odays = mondays[is][mon - 1] - mondays[is][12] + (day - 1);
        oleaps = -1 * leaps(year + 1, BASEYEAR - 1);
        
        // convert negative value into positive
        wkn = ((oyears + odays + oleaps + BASEWN) % 7 + 7) % 7;
    }

    return wkn;
}


// get how many leap years from small year to big year
// if big_year > small_year return lps, else return 0
long leaps(long sml, long big)
{
    long        lps = 0;    // leap years
    long        step;        // small year increased by it
    long        oset;

    if (sml >= big){
        return 0;
    }

    for (step = 1; sml <= big; sml += step){
        if (sml % 400 == 0){
            lps++;
            oset = big - sml;
            lps += (oset / 4) - (oset / 100) + (oset / 400);
            break;    
        }
        if (step == 1){
            if (IS_LEAP(sml)){
                step = 4;
                lps++;
            }
            continue;
        }
        if (sml % 100 != 0){
            lps++;
        }
    }    

    return lps;
}


int main(int argc, char *argv[])
{
    int         wkday;
    long        year;
    int            mon;
    int            cmondays;
    int            i;

    if (argc != 3){
        printf("usage: %s year month\n", argv[0]);
        return 0;
    }
    year = atoi(argv[1]);
    mon = atoi(argv[2]);
    if (mon < 1 || mon > 12){
        printf("Bad agument: month should between 1 and 12\n");
        return 0;
    }

    wkday = getweekn(year, mon, 1);
    cmondays = mondays[IS_LEAP(year)][mon] - mondays[IS_LEAP(year)][mon - 1];

    printf("\n\t\t===================== Calendar =====================\n");
    printf("\t\t date: %ld.%d\n\n", year, mon);
    printf("\t\tSUN\tMON\tTUE\tWEN\tTHU\tFRI\tSAT\n\t\t");
    for (i = 0; i < wkday; i++){
        printf(" \t");
    }
    for (i = 0; i < cmondays; i++){
        if ((i != 0) && ((i + wkday) % 7 == 0)){
            printf("\n\t\t");
        }
        printf("%2d \t", i + 1);
    }
    printf("\n\n");

    return 0;
}

阅读(4025) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~