1. 功能说明
功能一:日历显示
说明:
1)在当前的终端窗口中显示当前的年、月、日。例如,当前日期为2017年7月20日;
2)表格的标题是年月;
3)表头是星期,并且是从星期日开始;
4)显示日期时,如果当前月份的1日不是星期日,则用上个月的末尾几日补齐表格。同样,如果当前月的结尾日期不是周六,则用下个月的开始几日补齐;
5)普通的日期用白色显示,当前日期用绿色显示,星期六和星期日用红色显示,用来补齐表格的日期用灰色显示。
功能二:键盘控制
说明:
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日后存放本月的日期;
③接着存放本月的日期直到本月的日期全部存完;
④最后存放下个月初的日期直到整个数组存满。
最后,把数组的值在指定位置打印出来,普通的日期用白色显示,当前日期用绿色 显示,星期六和星期日用红色显示,用来补齐表格的日期用灰色显示。
b.键盘控制部分:
通过getch() 获取从键盘输入的键值,运用switch语句,实现各个按键的功能。
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;
}