C语言小项目-万年历

1. 功能说明
功能一:日历显示
C语言小项目-万年历_第1张图片
说明:
1)在当前的终端窗口中显示当前的年、月、日。例如,当前日期为2017年7月20日;
2)表格的标题是年月;
3)表头是星期,并且是从星期日开始;
4)显示日期时,如果当前月份的1日不是星期日,则用上个月的末尾几日补齐表格。同样,如果当前月的结尾日期不是周六,则用下个月的开始几日补齐;
5)普通的日期用白色显示,当前日期用绿色显示,星期六和星期日用红色显示,用来补齐表格的日期用灰色显示。

功能二:键盘控制
C语言小项目-万年历_第2张图片
说明:
1)用户使用键盘的左、右键控制月份的增减,上、下键控制年份的增减;
2)用户想直接查看某日期的日历时,可以按下“F”键,提示用户输入年、月,在用户输入结束以后,显示用户输入的日期日历。如图,按下“F”键后,输入2015/12并按下Enter键,程序切换到2015年12月的日历显示;
3)按下“ESC”键,退出整个程序。

2. 知识点
1)一维数组、二维数组
2)逻辑结构
顺序结构、循环结构、分支结构
3)Zeller公式

蔡勒(Zeller)公式 计算历史上的某一天是星期几?未来的某一天是星期几?
w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 
公式中的符号含义如下,
w:星期;
c:世纪-1;
y:年(两位数);
m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年     的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算);
d:日;
[ ]代表取整,即只要整数部分。
(C是世纪数减一,y是年份后两位,M是月份,d是日数。1月和2月要按上一年的
13月和 14月来算,这时C和y均按上一年取值。)
算出来的W除以7,余数是几就是星期几。如果余数是0,则为星期日。 
以2049年10月1日(100周年国庆)为例,用蔡勒(Zeller)公式进行计算,过程如下: 
蔡勒(Zeller)公式:w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 
=49+[49/4]+[20/4]-2×20+[26×(10+1)/10]+1-1 
=49+[12.25]+5-40+[28.6] 
=49+12+5-40+28 
=54 (除以7余5) 
即2049年10月1日(100周年国庆)是星期5。
但是计算2000年3月1日:
蔡勒(Zeller)公式:w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 
=0+[0/4]+[20/4]-2×20+[26×(3+1)/10]+1-1 
=0+[0]+5-40+[10.4] +1-1
=0+0+5-40+10
=-25
那么-25 % 7  =  -4与实际不匹配,为了修正w值 ,故将w 的值加上42,使之为正值,(w+42)%7 = w%7 + 42%7,如果w为正值,(w+42)%7 = w%7,结果不变。

4)Curses库
HTTP://tldp.org/HOWTO/NCURSES-Programming-HOWTO/index.html
5)time.h库中相关函数,用于获取当前时间。

3. 设计思路
a.日历显示部分:
定义一个6行7列的二维数组存放要显示的日期。先判断要显示月份的1日在星期几。如果在星期日(如表2),则分三个部分:

①为了使本月的日期居中,第一行存放上个月末的日期;
②第二行开始存放本月的日期直到本月的日期全部存完;
③接着存放下个月初的日期直到整个数组存满。

如果在星期日(如表3),则分四个部分:

①第一行中本月1日前存放上个月末的日期;
②第一行的1日以及1日后存放本月的日期;
③接着存放本月的日期直到本月的日期全部存完;
④最后存放下个月初的日期直到整个数组存满。

最后,把数组的值在指定位置打印出来,普通的日期用白色显示,当前日期用绿色 显示,星期六和星期日用红色显示,用来补齐表格的日期用灰色显示。
C语言小项目-万年历_第3张图片
b.键盘控制部分:
通过getch() 获取从键盘输入的键值,运用switch语句,实现各个按键的功能。

c.系统流程图
C语言小项目-万年历_第4张图片 C语言小项目-万年历_第5张图片

4. 源码分析

/*********************************************************
* Author    : 谢保成
* File Name : calendar.c
* Date      : 2017/7/15
* H/W Platform  : ubuntu 16.04
* Description   : Define global variables and functions 
*********************************************************/
#include "calendar.h"
//主函数       
int main(int argc, const char *argv[])
{   

    int temp1, temp2;

    //初始化
    initscr();          /* Start curses mode        */  
    start_color();

/*      COLOR_BLACK   0
        COLOR_RED     1
        COLOR_GREEN   2
        COLOR_YELLOW  3
        COLOR_BLUE    4
        COLOR_MAGENTA 5
        COLOR_CYAN    6
        COLOR_WHITE   7 */

    init_pair(1, COLOR_RED, COLOR_BLACK);
    init_pair(2, COLOR_GREEN, COLOR_BLACK);
    init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
    init_pair(6, COLOR_CYAN, COLOR_BLACK);
    init_pair(7, COLOR_WHITE, COLOR_BLACK);
    get_time();
    while(quit)
    {
        clear();
        memset(num, 0 , sizeof(num));   //清空数组
        title(y, m);
        temp1 = check_leap_year(y);
        temp2 = check_week(y, m, 1);    //判断每月的1号是星期几   
        disp_calendar(temp1, temp2, m);
        get_key();

    }
    endwin();           /* End curses mode        */
    return 0;   
}
/*******************************************************************************
* Author    : 谢保成
* File Name : calendar.h
* Date      : 2017/7/15
* H/W Platform  : VMware Workstation
* Description   : Define calendar macros and function prototypes. 
*******************************************************************************/

#ifndef __CALENDAR_H
#define __CALENDAR_H

#include 
#include 
#include 
#include 
#include 

#define TRUE    1
#define FALSE   0

#define KEY_ESC 033
#define M_KEY_F 0106
#define M_KEY_f 0146

int check_leap_year(int);
int check_week(int, int, int);
void disp_calendar(int , int , int);
void title(int, int);
void get_key(void);
void date_search(void);
void month_up(void);
void month_down(void);
void get_time(void);

extern int y, m, d;
extern char quit;
extern int num[6][7];

#endif

#include "calendar.h"

//上一年的12月和闰年的1-12月
int leapday[13] = {31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};     
//上一年的12月和平年的1-12月
int nonleapday[13] = {31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};  
int num[6][7];
int y, m, d;//年,月,日
char quit = 1;  //当 quit 为0时,结束程序

void get_time(void)
{
    struct tm *t;
    time_t timer = time(NULL);
    t = localtime(&timer);
    y = 1900 + t->tm_year;
    m = 1 + t->tm_mon;
    d = t->tm_mday;
}

void title(int year, int month)
{                
    attron(COLOR_PAIR(5));
    attron(A_BOLD);

    mvprintw(4,35, "%d", y);
    mvprintw(4,39, "%s", "/");
    mvprintw(4,40, "%d", m/10); //十位
    mvprintw(4,41, "%d", m%10); //个位

    mvprintw(6, 23, "%s", "SUN");
    mvprintw(6, 28, "%s", "MON");
    mvprintw(6, 33, "%s", "TUE");
    mvprintw(6, 38, "%s", "WED");
    mvprintw(6, 43, "%s", "THU");
    mvprintw(6, 48, "%s", "FRI");
    mvprintw(6, 53, "%s", "SAT"); 

    attroff(COLOR_PAIR(5));
    attroff(A_BOLD);
}

//日历算法以及打印日历
void disp_calendar(int leap, int first_day_week, int month)
{
    int i,j,k=1;
    //日历是一个6行7列的表格
    for(i=0; i<6; i++)
    {
        //上个月月尾剩余的日期    

        //  1.如果当前月份的1日不是星期日,则用上个有的末尾几日补齐表格。
        //  2.如果当前月份的1日是星期日,则用上个有的末尾7日补齐表格。
        if(first_day_week == 0)     //判断1日是不是在星期日
        {
            first_day_week = 7;
            for(j=0; (j0) ; j++)
            {
                if(leap == 0)num[i][j] = nonleapday[month-1]-first_day_week+1+j;
                else num[i][j] = leapday[month-1]-first_day_week+1+j;

                attron(COLOR_PAIR(6));              
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);
                attroff(COLOR_PAIR(6));
            }
        }
        else
        {
            for(j=0; (j0) ; j++)
            {
                if(leap == 0)num[i][j] = nonleapday[month-1]-first_day_week+1+j;
                else num[i][j] = leapday[month-1]-first_day_week+1+j;

                attron(COLOR_PAIR(6));
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);  //打印十位
                attroff(COLOR_PAIR(6));             
            }
        }

        //本月开头的第一行日期
        for(j=first_day_week; (j<7) && (i==0) && (first_day_week != 0); j++)
        {       
            num[i][j] = k;
            k++;

            attron(COLOR_PAIR(7));
            attron(A_BOLD);
            mvprintw(8+2*i,24+5*j,"%d",num[i][j]);  //打印十位
            attroff(COLOR_PAIR(7));
            attroff(A_BOLD);
        }
        //剩余的日期
        for(j=0; (j<7) && (i>0); j++)
        {           
            //如果当前月的结尾日期不是周六,则用下个月的开始几日补齐。
            if((leap == 0 && k <= nonleapday[month]) || (leap == 1 && k <= leapday[month]))
            {
                num[i][j] = k;

                attron(A_BOLD);
                attron(COLOR_PAIR(7));
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);  //打印十位
                attroff(COLOR_PAIR(7));

                //周末为红色显示
                attron(COLOR_PAIR(1));

                if(first_day_week != 7){                    
                    mvprintw(8+2*0,24+5*6,"%d",num[0][6]);  
                }
                mvprintw(8+2*1,24+5*0,"%d",num[1][0]);  
                mvprintw(8+2*1,24+5*6,"%d",num[1][6]);  
                mvprintw(8+2*2,24+5*0,"%d",num[2][0]);  
                mvprintw(8+2*2,24+5*6,"%d",num[2][6]);  
                mvprintw(8+2*3,24+5*0,"%d",num[3][0]);  
                mvprintw(8+2*3,24+5*6,"%d",num[3][6]);  
                mvprintw(8+2*4,24+5*0,"%d",num[4][0]);  
                if (num[4][6] > 25)  //num[4][6] 的值最小为26
                {
                    mvprintw(8+2*4,24+5*6,"%d",num[4][6]);  
                }
                if (num[5][0] > 25)
                {
                    mvprintw(8+2*5,24+5*0,"%d",num[5][0]);
                }
                attroff(COLOR_PAIR(1));
                attroff(A_BOLD);
            }       
            else if(leap == 0 && k > nonleapday[month])
            {
                num[i][j] = k - nonleapday[month];
                attron(COLOR_PAIR(6));
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);
                attroff(COLOR_PAIR(6));
            }
            else if(leap == 1 && leapday[month])
            {
                num[i][j] = k - leapday[month];
                attron(COLOR_PAIR(6));
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);
                attroff(COLOR_PAIR(6));
            }
            k++;
        }
    }

    //显示当前日期为绿色
    for (i = 0; i < 6; i++)
    {
        for (j = 0; j < 7; j++)
        {
            attron(COLOR_PAIR(2));
            attron(A_BOLD);
            attron(A_STANDOUT); //高亮显示
            //同一个表中可能会出现相同的两个日期,故要进行判断
            if (num[i][j] == d && d>14 && d<22)
            {           
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);
            }
            else if(num[i][j] == d && d <=14 && (7*i+j)<21)
            {
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);
            }
            else if(num[i][j] == d && d >=22 && (7*i+j)>7)
            {
                mvprintw(8+2*i,24+5*j,"%d",num[i][j]);
            }
            attroff(COLOR_PAIR(2));
            attroff(A_STANDOUT);
        }
    }
    move(0,0);
    attroff(A_BOLD);
    refresh();
}

//判断闰年
int check_leap_year(int year)
{
    if((year%4 == 0) && (year%100 != 0) || (year%400 == 0))
        return TRUE;
    else
        return FALSE;
}

//用Zeller公式计算历史某天是星期几
int check_week(int year, int month, int day)
{
    int w, y, c, m, d;//week, year, centry, month, day
    y = year%1000%100;
    c = year/100;
    d = day;
    if(month > 2)
    {
        m = month;  
    }else{
        y -= 1;
        m = month + 12;
    }   
    w = y + (y/4) + (c/4) - (2*c) + (26*(m+1)/10) + d - 1;
    return ((w+49)%7);
}

//从键盘中获取键值,并执行相应的代码
void get_key(void)
{
    int ch;

    raw();                      /* Line buffering disabled  */
    keypad(stdscr, TRUE);   
    noecho();               //关闭回显

    ch = getch();   
    switch(ch)
    {
        case KEY_LEFT:  month_down();break; 
        case KEY_RIGHT: month_up();break;
        case KEY_UP:    y++;break;
        case KEY_DOWN:  y--;break;
        case KEY_ESC:   quit = 0;break; //结束程序
        case M_KEY_f:
        case M_KEY_F:   date_search();break;
        default:    break;  
    }

}

void month_down(void)
{
    m--;
    if(m < 1) 
    {
        m=12;
        y--;
    }
}

void month_up(void)
{
    m++;
    if(m > 12) 
    {
        m=1;
        y++;
    }
}

void date_search(void)
{
    int new_y,new_m;
    echo();     //打开回显
    mvprintw(20, 24, "%s", "Tips: year between 1900 and 2100,");
    mvprintw(21, 30, "%s", "month between 1 and 12.");
    mvprintw(22, 30, "%s", "For example:2015/7");       
    mvprintw(23, 24, "%s", "Search:");      
    scanw("%d/%d",&new_y,&new_m);
    while(new_y < 1900 || new_y > 2100 || new_m>12 || new_m <1) //按键处理
    {
        mvprintw(23, 24, "%s", "Search: sorry try again!");
        getch();
        mvprintw(23, 24, "%s", "Search:                 "); 
        move(23, 33);   
        scanw("%d/%d",&new_y,&new_m);
    }
    y = new_y;
    m = new_m;
}

你可能感兴趣的:(Projiect)