一、简介

      Linux系统自带万年历程序,可以使用cal 11 2015命令查看11月份的日历。

二、详解

1、代码一

(1)calendar.c:

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

int main(int argc, char **argv)
{
	if (argc != 2) {
		printf("usage: [%s 2000/10/1] or [%s 2000/10]\n", argv[0], argv[0]);
		return -1;
	}
	int year = 0, month = 0, day = 0, week = 0, days = 0;
	int i = 0;
	int flag = -1;   //分隔年月日
	int dm = 0;      //月不同,与标准天的星期1-7天数的偏差
	int dy = 0;      //年不同,引起的星期1-7天数的偏差
	int m2 = 0;      //2月,引起的星期1-7天数的偏差
	char WEEK[9] = {0};
	
	while(argv[1][i]) { /*遍历传入的参数日期,计算出year,month,day*/
	  if ((argv[1][i] == '/' || argv[1][i] == '.' || argv[1][i] == '-') && flag == -1 ) {
	    flag = 0;
	    i++;
	    continue;
	  }
	  if ((argv[1][i] == '/' || argv[1][i] == '.' || argv[1][i] == '-') && flag == 0 ) {
	    flag = 1;
	    i++;
	    continue;
	  }
	  if (flag == -1)  year = year * 10 + (argv[1][i] - '0');
	  if (flag == 0)  month = month * 10 + (argv[1][i] - '0');
	  if (flag == 1)  day = day * 10 + (argv[1][i] - '0');
	  i++;
	}
	if (month < 1 || month > 12 || year < 0) {    /*若月份传入错误数字*/
	  printf ("ERROR! the entered MONTH or YEAR is invalid\n"); 
	  return -1; 
  }
  if (year == 2000) {
    dy = 0;    //以2000/1/1为基准点
    m2 = 1;    //29天
  }
  else {
  	/*** 该年 1月1号 到2000年1月1号的 " 天数差 " ***/
  	if(year > 2000)  dy =(year -2000 ) + (year-1-2000)/4 - (year-1-2000)/100 + (year-1-2000)/400 + 1;
  	else  dy =(year -2000 ) + (year-2000)/4 - (year-2000)/100 + (year-2000)/400;
    if((year%4 == 0 && year%100 != 0) || (year%100 == 0 && year%400 == 0))  m2 = 1;
    else  m2 = 0;  /***该年不是润年***/
  }
  switch (month) {
    case 1: dm = 0;  days = 31;  break;   //31%7 == 3得偏移天数
    case 2: dm = 3;  days = (m2 == 1 ? 29 : 28);  break;
    case 3: dm = 3 + m2;  days = 31;  break;
    case 4: dm = 6 + m2;  days = 30;  break;
    case 5: dm = 1 + m2;  days = 31;  break;
    case 6: dm = 4 + m2;  days = 30;  break;
    case 7: dm = 6 + m2;  days = 31;  break;
    case 8: dm = 2 + m2;  days = 31;  break;
    case 9: dm = 5 + m2;  days = 30;  break;
    case 10: dm = m2;  days = 31;  break;
    case 11: dm = 3 + m2;  days= 30;  break;
    case 12: dm = 5 + m2;  days = 31;  break;
  }
  if (day < 0 || day > days) { 
	  printf ("ERROR! the entered DAY is invalid\n"); 
	  exit (0);
  }
  week = (dy + dm + day - 1 + 6) % 7;   
  if (week < 0)  week += 7;
  if (day > 0) {  /*** 判定查看类型 ***/
    switch (week) {
      case 0: strcpy (WEEK,"SUNDAY");  break;
      case 1: strcpy (WEEK,"MONDAY");  break;
      case 2: strcpy (WEEK,"TUESDAY");  break;
      case 3: strcpy (WEEK,"WEDNESDAY");  break;
      case 4: strcpy (WEEK,"THURSDAY");  break;
      case 5: strcpy (WEEK,"FRIDAY");  break;
      case 6: strcpy (WEEK,"SATURDAY");  break;
    }
    printf ("this day is %s[%d]\nOK!\n", WEEK, week);
  }
  else {
    week = (++week ) % 7;
    printf ("the calender of this month as following\n");
    printf ("\n*********************************\n");
    printf (" SUN  MON  TUE  WEN  THU  FRI  STA\n");
    for (i = 0; i < week; i++)  printf ("     ");
    for (i = 1; i <= days; i++) {
      printf (" %2d  ",i);
      week++;
      if (week % 7 == 0 && i != days)  printf ("\n");
    }
    printf ("\n*********************************\n");
    printf ("OK!\n");
  }
	return 0;
}
(2)编译运行
gcc -o calendar calendar.c
./calendar 2015/11

./calendar 2015/11/4

2、代码二

(1)calendar.c:
/*
 * cal.c
 *
 *  Created on: Apr 10, 2011
 *  Author: Administrator
 *
 *      现行的格里历是从儒略历演化而来的。儒略历每4年一个润年,润年366天,平年365天。
 *      如果从公元1年算的话,那么凡是能够被4整除的都是润年。从天文角度看,儒略历这种
 *      历法是有误差的,到16世纪误差已经达到了10天。1582年,罗马教皇对儒略历进行了
 *      一次校定,该年的10-5到10-14这10天被抹掉,并规定凡不能被400整除的世纪年不再
 *      算为润年,校定之后的儒略历即为现行的格里历。
 *
 *      但是英国直到1752年才开始使用格里历,此时时间误差已经达到了11天,原因是1700
 *      年在儒略历中是润年,而在格里历中是平年。1752年英国议会决定将本年的9-3到9-13
 *      这11天抹掉,以同步格里历。当时美国不是英国的殖民地,所以在美国的日历中也是没有
 *      1752-9-3到1752-9-13这11天的。 我们知道UNIX系统源于美国,Linux系统源于UNIX,
 *      这就是为什么当我们在Linux系统中敲"cal 9 1752"这条命令时,会看到一个只有19天
 *      的9月。
 *
 * 以上内容参考自维基百科
 *
 *		本程序模似Linux中cal命令的部分功能。允许输入的年分为1...9999。
 *		编译器:gcc V4.5.0
 *
 *		分析:
 *			1752年是特殊年,1752-9月是特殊年中的特殊月。
 *
 *			1752-9-2之前,采用的是儒略历,凡能被4整除都为润年;1752-9-14之后,采用的是
 *			格里历,不能被400整除的世纪年不再作为润年。
 *
 *			格里历闰年计算方法
 *			世纪年 	***4000的倍数 	**3200/1900的倍数 	400的倍数 	100的倍数 	4的倍数
 *			 结果 		不闰 				不闰 			闰 			不闰 	闰
 *			注:
 *				3200/1900的倍数是天文科学家研究出来
 *				增加4000的倍数不闰更准确,但目前只是世纪年还不足4000,因此尚未用途
 *
 *			格里历在400年的周期中有146,097天,恰好是20,871 星期。所以,例如,格里历七年、
 *			407年、807年、1207年、1607年、2007年的日期与星期是完全相同的。也就是说,格里
 *			历每400年一个周期。
 *
 *			公元1-1-1,按儒略历是周六,按格里历是周一;因1752-9-2之前采用的都是儒略历,所以
 *			公元1-1-1应该是周六。
 *			1752-9-14是周四。
 *			
 *			公元1-1-1到1752-9-2,日期是连续的,儒略历;公元1752-9-14至今,日期是连续的,格里历。
 *
 *			对于给定的一个 年(Y)-月(M)-日(D) ,则从公元1-1-1到	给定的日期的天数为:
 *				1752-9-2前:365*(Y-1)+(Y-1)/4+e,其中e表示从Y-1-1到Y-M-D的天数,
 *				且这天是该周的第((Y-1)+(Y-1)/4+5+e)%7天。
 *			
 *				1752-9-14后:365*(Y-1)+(Y-1)/4-(Y-1)/100+(Y-1)/400+e,
 *				且这天是该周的第((Y-1)+(Y-1)/4-(Y-1)/100+(Y-1)/400+e)%7天。
 *			
 *			需要注意的是,从1-1-1到1752-9-14之后的某个日期的天数并非实际的天数,它只是根据格里历的
 *			的规则推算出来的,目的也只是为了得到日期与星期的对应。
 *
 *		算法思想:
 *			对于年历,若能知道本年的1月1日是周几,也就知道了本年的年历分布。
 *			对于月历,若能知道本月的1日是周几,也就知道了本月的月历分布。
 *			所以关键是正确的推算出给定的一个日期是星期几。以下为推算方法:
 *				1752-9-2之前:((Y-1)+(Y-1)/4+5+e)%7
 *				1752-9-14之后:((Y-1)+(Y-1)/4-(Y-1)/100+(Y-1)/400+e)%7
 *			把1800-2199年当成一个周期,对于其后的日期,只需在1800-2199之间找到
 *			对应的日期,就可以知道是星期几,这样就不用再考虑4000年之后4000的倍数
 *			不是润年这种情况。 (Y-1800)%400+1800
 *			
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

static void PrintHelp();
static int* CurrentCal();
static int CheckYear(int year);
static int CheckMonth(int month);
static int CheckDay(int year, int month, int day);
static int IsNum(char *argv);
static int IsLegalParameter(int argc, char **argv);
static long StrToLong(char *argv);
static long Power(int baseNumber, int exponent);
static void PrintCalendar(int year);
static void PrintCommonCalendar(int year, int row);
static void PrintOneQuarter(int year, int mfOfWeek, int mfDays, int msOfWeek, int msDays, int mtOfWeek, int mtDays, int row);
static void Print1752_3Quarter();
static void PrintMonthlyCalendar(int year, int month);
static void PrintMonthName(char *, int year);
static void PrintCommonMonthlyCalendar(int dayOfWeek, int days);
static void PrintDayOfWeek(int year, int month, int day);
static int DayOfWeek(int year, int month, int day);
int CurrentDays(int year, int month, int day);

int len_year = 0;	//输入的年这个参数的字符串长度,主根用于后面的PrintMonthName(char *, int)函数
int cal[3] = {0};

int main(int argc, char **argv)
{
	int year = 0, month = 0, day = 0;
	int flag = IsLegalParameter(argc, argv);
	switch(flag) {
	  case -1:
	  	printf("Syntax Error!\n\n");
		  PrintHelp();
	  	break;
	  case 0:
	  	PrintMonthlyCalendar(*(CurrentCal()), *(CurrentCal() + 1));
		  break;
    case 1: {
    	len_year = strlen(*(argv + 1));
    	switch(argc) {
    	  case 2:
    	  	year = StrToLong(*(argv + 1));
    	  	if (!CheckYear(year))  break;
          printf("                               %4d\n\n", year);
          PrintCalendar(year);
    	  	break;
    	  case 3:
    	  	year = StrToLong(*(argv + 1));
    	  	if (!CheckYear(year))  break;
			    month = StrToLong(*(argv + 2));
			    if (!CheckMonth(month))  break;
          PrintMonthlyCalendar(year, month);
    	    break;
    	  case 4:
    	  	year = StrToLong(*(argv + 1));
    	  	if (!CheckYear(year))  break;
    	  	month = StrToLong(*(argv + 2));
    	  	if (!CheckMonth(month))  break;
    	  	day = StrToLong(*(argv + 3));
    	  	if (!CheckDay(year, month, day))  break;
    	  	PrintDayOfWeek(year, month, day);
    	  	break;
    	  default:
    	  	year = StrToLong(*(argv + 1));
    	  	if (!CheckYear(year))  break;
    	  	month = StrToLong(*(argv + 2));
    	  	if (!CheckMonth(month))  break;
    	  	day = StrToLong(*(argv + 3));
    	  	if (!CheckDay(year, month, day))  break;
    	  	PrintDayOfWeek(year, month, day);
    	  	break;
    	}
    	break;
    }
	  default:  break;
	}
	return 0;
}
void PrintHelp()
{
	printf("usage:cal [<YEAR> [<MONTH> [<DAY>]]");
}
/*
 *以下函数判断输入的参数是否合法
 */
int IsLegalParameter(int argc, char **argv) 
{
	if (argc == 1) {
		return 0;    //0表示没有输入参数
	}
	while (argc > 1) {
		if (!IsNum(*(argv + --argc))) {
			return -1; //-1表示输入的参数不合法
		}
	}
	return 1; //1表示输入了参数,并且参数是合法的
}
/*
 * 以下函数判断一个字符串是否能被转换成数字
 */
int IsNum(char *argv) {
	while (*argv != '\0') {
		if (*argv >= 48 && *argv <= 57) {
			argv++;
			continue;
		}
		return 0;
	}
	return 1;
}
/**
 * 以下函数返回一个包含当前年 月 日的整型数组
 */
int* CurrentCal()
{
	time_t nowtime;
	struct tm *timeinfo;
	time(&nowtime);
	timeinfo = localtime(&nowtime);
	memset(cal, 0, sizeof(cal));
	cal[0] = timeinfo->tm_year + 1900;
	cal[1] = timeinfo->tm_mon + 1;
	cal[2] = timeinfo->tm_mday;
	//printf("data=%d, %d, %d\n", cal[0], cal[1], cal[2]);
	return cal;
}
/**
 * 打印指定月的月历
 */
void PrintMonthlyCalendar(int year, int month)
{
	int dayOfWeek = DayOfWeek(year, month, 1);
  switch (month) {
  	case 1:
  		PrintMonthName("January", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
		case 2:
		  PrintMonthName("February", year);
      if (year < 1753 && year % 4 == 0) {
			  PrintCommonMonthlyCalendar(dayOfWeek, 29);
			  break;
		  }
		  else if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
			  PrintCommonMonthlyCalendar(dayOfWeek, 29);
			  break;
			}
			PrintCommonMonthlyCalendar(dayOfWeek, 28);
			break;
		case 3:
			PrintMonthName("March", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
		case 4:
			PrintMonthName("April", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 30);
		  break;
		case 5:
			PrintMonthName("May", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
		case 6:
			PrintMonthName("June", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 30);
		  break;
		case 7:
			PrintMonthName("July", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
		case 8:
			PrintMonthName("August", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
		case 9:
			PrintMonthName("September", year);
			if (year == 1752) {   //特殊的1752-9
				printf("Su Mo Tu We Th Fr Sa\n       1  2 14 15 16\n17 18 19 20 21 22 23\n24 25 26 27 28 29 30\n");
        break;
		  }
		  PrintCommonMonthlyCalendar(dayOfWeek, 30);
		  break;
		case 10:
		  PrintMonthName("October", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
	  case 11:
		  PrintMonthName("November", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 30);
		  break;
	  case 12:
		  PrintMonthName("December", year);
		  PrintCommonMonthlyCalendar(dayOfWeek, 31);
		  break;
	  default:
		  break;
  }
}
/**
 * 以下函数返回给定的年、月、日是周几
 */
int DayOfWeek(int year, int month, int day)
{
	if ((year < 1752) || (year == 1752 && month < 9) || (year == 1752 && month == 9 && day < 3)) {
	  return ((year - 1) + (year - 1) / 4 + CurrentDays(year, month, day) + 5) % 7;
	}
	if (year == 1752 && month == 9 && (day > 2 && day < 14)) {
    printf("本万年历中没有%d-%d-%d这一天\n", year, month, day);
		return -1;
	}
  if (year > 2199) {
    year = (year - 1800) % 400 + 1800;
  }
  return ((year - 1) + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + CurrentDays(year, month, day)) % 7;
}
/**
 * 以下函数计算从给定年的1-1到month-day的天数
 */
int CurrentDays(int year, int month, int day)
{
	int days = 0;
	switch(month) {
	  case 1: days = day;  break;
	  case 2: days = 31 + day;  break;
	  case 3: days = 59 + day;  break;
    case 4: days = 90 + day;  break;
	  case 5: days = 120 + day;  break;
	  case 6: days = 151 + day;  break;
	  case 7: days = 181 + day;  break;
	  case 8: days = 212 + day;  break;
	  case 9: days = 243 + day;  break;
	  case 10: days = 273 + day;  break;
	  case 11: days = 304 + day;  break;
	  case 12: days = 334 + day;  break;
	  default: break;
	}
	if (year < 1753 && year % 4 == 0) {
	  if (month > 2)  ++days;
	}
  else if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
	  if (month > 2)  ++days;
	}
	return days;
}
/**
 * 以下函数的作用仅仅是将月名在水平方向上打印到月历的中间
 */
void PrintMonthName(char *monthName, int year)
{
	char space = 32;
	int preSpaces = 0;
	preSpaces = (21 - strlen(monthName) - len_year - 1) / 2;
  printf("%*c%s %d\n", preSpaces, space, monthName, year);
  printf("--------------------\n");
}
/**
 * 根据一个月的天数及该月有几天这两个参数,打印出该月的月历
 */
void PrintCommonMonthlyCalendar(int dayOfWeek, int days)
{
	int i, j, day = 1;
	char space = 32;
	int weeks = (days + dayOfWeek) / 7 + 1; //计算出该月有几周
	printf("Su Mo Tu We Th Fr Sa\n");
	printf("%*c", 3 * dayOfWeek, space);
	for (i = 0; i < weeks; ++i) {
		if (i == 0) {
			//打印第1周
			for (j = dayOfWeek; j < 7; ++j) {
				printf("%2d ", day);
				day++;
			}
			continue;
		}
		//打印第2周到倒数第2周
		if (i < weeks - 1) {
			printf("\n%2d %2d %2d %2d %2d %2d %2d", day, day + 1, day + 2, day + 3, day + 4, day + 5, day + 6);
			day += 7;
			continue;
		}
		//打印最后一周
		printf("\n");
		for (j = 0; j < 7; ++j) {
			if (day <= days) {
				printf("%2d ", day++);
			}
		}
		printf("\n");
	}
}
/**
 * 以下函数将一个字符串转换成数字。
 */
long StrToLong(char *argv)
{
	if (!IsNum(argv)) {
		return 0;
	}
	long result = 0;
	int argvLength = strlen(argv);
	int *nums = (int *)malloc((argvLength + 1) * sizeof(int));
	memset(nums, 0, (argvLength + 1) * sizeof(int));
	//int nums[1000] = {0};
	int *pInt = nums;
	int i = 0;
	do {
	  *(pInt++) = *argv - 48;
	  int num = *argv - 48;
		argv++;
	} while(*argv != '\0');
	pInt = nums;
	do {
		result += *pInt * Power(10, argvLength - 1);
		pInt++;
		argvLength--;
	} while (argvLength != 0);
	free(nums);
	return result;
}
/*
 * According to the base number and the exponent calculate power.
 * 根据基数和指数,计算出乘方数。
 */
long Power(int baseNumber, int exponent)
{
	long result = 1;
	if (exponent == 0) {
		return 1;
	}
  do {
		result *= baseNumber;
		exponent--;
	} while (exponent != 0);
	return result;
}
int CheckYear(int year)
{
	if (year == 0 || year > 9999) {
		printf("cal: year %d not in range 1..9999\n", year);
		return 0;
	}
	return 1;
}
int CheckMonth(int month) 
{
	if (month < 1 || month > 12) {
		printf("cal: month %d not in range (1..12)\n", month);
		return 0;
	}
	return 1;
}
int CheckDay(int year, int month, int day) 
{
	switch (month) {
	case 1:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	case 2:
		if ((year < 1753 && year % 4 == 0) || ((year % 4 == 0 && year % 100
				!= 0) || year % 400 == 0)) {
			if (day < 1 || day > 29) {
				printf("cal: day %d not in range (1..29)\n", day);
				return 0;
			}
		}
		if (day < 1 || day > 28) {
			printf("cal: day %d not in range (1..28)\n", day);
			return 0;
		}
		return 1;
		break;
	case 3:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	case 4:
		if (day < 1 || day > 30) {
			printf("cal: day %d not in range (1..30)\n", day);
			return 0;
		}
		return 1;
		break;
	case 5:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	case 6:
		if (day < 1 || day > 30) {
			printf("cal: day %d not in range (1..30)\n", day);
			return 0;
		}
		return 1;
		break;
	case 7:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	case 8:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	case 9:
		if (day < 1 || day > 30) {
			printf("cal: day %d not in range (1..30)\n", day);
			return 0;
		}
		return 1;
		break;
	case 10:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	case 11:
		if (day < 1 || day > 30) {
			printf("cal: day %d not in range (1..30)\n", day);
			return 0;
		}
		return 1;
		break;
	case 12:
		if (day < 1 || day > 31) {
			printf("cal: day %d not in range (1..31)\n", day);
			return 0;
		}
		return 1;
		break;
	default:
		break;
		return 0;
	}
}
/**
 * 打印给定的年、月、日是周几
 */
void PrintDayOfWeek(int year, int month, int day)
{
	char *names[9] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
  int dayOfWeek = DayOfWeek(year, month, day);
  if (dayOfWeek == -1)  return;
  printf("%d-%d-%d: %s\n", year, month, day, *(names + dayOfWeek));
}
/**
 * 以下函数打印年历,以月为单位,分成四行打印,每行打印三个月
 */
void PrintCalendar(int year)
{
	int i;
	for (i = 0; i < 4; ++i) {
		switch (i) {
			case 0:
			  //打印第1行,1-3月的月历
			  printf("       January              February               March\n");
			  printf("--------------------  --------------------  --------------------\n");
			  printf("Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa\n");
			  PrintCommonCalendar(year, 1);
			  break;
		  case 1:
		    //打印第2行,4-6月的月历
		    printf("        April                  May                  June\n");
			  printf("--------------------  --------------------  --------------------\n");
			  printf("Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa\n");
			  PrintCommonCalendar(year, 2);
			  break;
		  case 2:
		 	  //打印第3行,7-9月的月历
		 	  printf("        July                 August              September\n");
			  printf("--------------------  --------------------  --------------------\n");
			  printf("Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa\n");
			  PrintCommonCalendar(year, 3);
			  break;
			case 3:
				//打印第4行,11-12月的月历
			  printf("       October              November              December\n");
			  printf("--------------------  --------------------  --------------------\n");
			  printf("Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa\n");
			  PrintCommonCalendar(year, 4);
			  break;
		  default:
			  break;
		}
	}
}
/**
 * 打印出给定年,给定行的3个月的月历。
 */
void PrintCommonCalendar(int year, int row)
{
	int mfOfWeek, mfDays; //mfOfWeek表示该行第1个月的1号是周几,mfDays表示第1个月的天数
	int msOfWeek, msDays; //mfOfWeek表示该行第2个月的1号是周几,mfDays表示第2个月的天数
	int mtOfWeek, mtDays; //mfOfWeek表示该行第3个月的1号是周几,mfDays表示第3个月的天数
	switch (row) {
		case 1:
			mfOfWeek = DayOfWeek(year, 1, 1);
			mfDays = 31;
		  msOfWeek = DayOfWeek(year, 2, 1);
		  msDays = 28;
		  mtOfWeek = DayOfWeek(year, 3, 1);
		  mtDays = 31;
		  PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek, mtDays, row);
			break;
		case 2:
			mfOfWeek = DayOfWeek(year, 4, 1);
		  mfDays = 30;
		  msOfWeek = DayOfWeek(year, 5, 1);
		  msDays = 31;
		  mtOfWeek = DayOfWeek(year, 6, 1);
		  mtDays = 30;
		  PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek, mtDays, row);
			break;
		case 3:
			if (year == 1752) {
			  Print1752_3Quarter();
			  break;
		  }
		  mfOfWeek = DayOfWeek(year, 7, 1);
		  mfDays = 31;
		  msOfWeek = DayOfWeek(year, 8, 1);
		  msDays = 31;
		  mtOfWeek = DayOfWeek(year, 9, 1);
		  mtDays = 30;
		  PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek, mtDays, row);
			break;
		case 4:
			mfOfWeek = DayOfWeek(year, 10, 1);
		  mfDays = 31;
		  msOfWeek = DayOfWeek(year, 11, 1);
		  msDays = 30;
		  mtOfWeek = DayOfWeek(year, 12, 1);
		  mtDays = 31;
		  PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek, mtDays, row);
			break;
		default:
		  break;
	}
}
/**
 * 以下函数打印一个季度,也即是在同一行上的3个月的月历。
 *
 * 打印方式肯定是从上到下,从左到右依次打印。
 *
 * 每月至多有6周,至少有5周;第1周、第5周和第6周(如果有第6周)是需要特殊处理
 * 的三个周,因为这3周并不一定总是有7天。
 */
void PrintOneQuarter(int year, int mfOfWeek, int mfDays, int msOfWeek, int msDays, int mtOfWeek, int mtDays, int row)
{
	int mfDay = 1, msDay = 1, mtDay = 1; //对已经打印的天数记数
	int i, j;
	char space = 32;
	//如果要打印的是1-3月的月历,则应该判断一下该年的2月是29天不是29天
	if (row == 1) {
		if ((year < 1753 && year % 4 == 0) || (year >= 1753 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))) {
			msDays = 29;
		}
	}
	for (i = 0; i < 6; ++i) {
		if (i == 0) {
			//输出第一个月的第一周
			if (mfOfWeek > 0) {
				printf("%*c", 3 * mfOfWeek, space);
			}
			for (j = mfOfWeek; j < 7; ++j) {
				printf("%2d ", mfDay);
				mfDay++;
			}
			//输出第二个月的第一周
			printf("%*c", 3 * msOfWeek + 1, space);
			for (j = msOfWeek; j < 7; ++j) {
				printf("%2d ", msDay);
				msDay++;
			}
			//输出第三个月的第一周
			printf("%*c", 3 * mtOfWeek + 1, space);
			for (j = mtOfWeek; j < 7; ++j) {
				printf("%2d ", mtDay);
				mtDay++;
			}
			continue;
		}

		//打印2-4周
		if (i < 4) {
			printf("\n%2d %2d %2d %2d %2d %2d %2d  %2d %2d %2d %2d %2d %2d %2d  %2d %2d %2d %2d %2d %2d %2d", \
					mfDay, mfDay + 1, mfDay + 2, mfDay + 3, mfDay + 4, mfDay + 5, mfDay + 6, msDay, msDay + 1, msDay + 2, \
					msDay + 3, msDay + 4, msDay + 5, msDay + 6, mtDay, mtDay + 1, mtDay + 2, mtDay + 3, mtDay + 4, \
					mtDay + 5, mtDay + 6);
			mfDay += 7;
			msDay += 7;
			mtDay += 7;
			continue;
		}

		//打印第1个月的第5周或第6周
		if (mfDays - mfDay > 6) { //未打印天数不少于7天时,能够占满1周,直接打印
			printf("\n%2d %2d %2d %2d %2d %2d %2d ", mfDay, mfDay + 1, mfDay + 2, mfDay + 3, mfDay + 4, mfDay + 5, mfDay + 6);
			mfDay += 7;
		} 
		else {   //未打印天数不足一周时,按以下方式打印
			printf("\n");
			for (j = 0; j < 7; ++j) {
				//有数据打印数据,没有数据用空格填充
				if (mfDay <= mfDays) {
					printf("%2d ", mfDay++);
				} 
				else {
					printf("   ");
				}
			}
		}
		printf(" ");

		//打印第2个月的第5周或第6周
		if (msDays - msDay > 6) {
			printf("%2d %2d %2d %2d %2d %2d %2d ", msDay, msDay + 1, msDay + 2, msDay + 3, msDay + 4, msDay + 5, msDay + 6);
			msDay += 7;
		} 
		else {
			for (j = 0; j < 7; ++j) {
				if (msDay <= msDays) {
					printf("%2d ", msDay++);
				} 
				else {
					printf("   ");
				}
			}
		}
		printf(" ");

		//打印第3个月的第5周或第6周
		if (mtDays - mtDay > 6) {
			printf("%2d %2d %2d %2d %2d %2d %2d ", mtDay, mtDay + 1, mtDay + 2, mtDay + 3, mtDay + 4, mtDay + 5, mtDay + 6);
			mtDay += 7;
		}
		else {
			for (j = 0; j < 7; ++j) {
				if (mtDay <= mtDays) {
					printf("%2d ", mtDay++);
				}
			}
		}
	}
	printf("\n");
}
/**
 *特殊的1752年第3季度:7月8月9月
 */
void Print1752_3Quarter() 
{
	char space = 32;
	printf("%10c1  2  3  4%21c1%9c1  2 14 15 16\n", space, space, space);
	printf(" 5  6  7  8  9 10 11   2  3  4  5  6  7  8  17 18 19 20 21 22 23\n");
	printf("12 13 14 15 16 17 18   9 10 11 12 13 14 15  24 25 26 27 28 29 30\n");
	printf("19 20 21 22 23 24 25  16 17 18 19 20 21 22\n");
	printf("26 27 28 29 30 31     23 24 25 26 27 28 29\n");
	printf("%22c30 31\n", space);
}
(2)编译运行:
gcc -o calendar calendar.c
./calendar(等同于./calendar 2015 11)

./calendar 2015


./calendar 2015 11 4

三、总结

(1)万年历的算法还是比较复杂的,需要考虑的因素比较多,也可看看linux系统的源码包util-linux-ng参考万年历的实现。
(2)若有建议,请留言,在此先感谢!

Logo

更多推荐