본문 바로가기

강의자료/C/C++

038. 클래스로 달력을 만들자.

◈ 이 글은 강의를 위하여 제가 직접 작성한 내용입니다. 따라서 퍼가실 경우, 출처를 명확히 해주시기 바랍니다!!
◈ This ariticle was written by me for teaching. So if you want to copy this article, please clarify the source!!


커피자판기는 매우 단순한 클래스였다.
이제는 좀 더 그럴듯한 달력 클래스를 만들어보자.
처음에는 클래스를 사용하지 않고 달력이 만들어지는 원리에 대해 먼저 알아볼 것이다.
달력의 원리를 모두 이해하였다면, 직접 클래스로 만들어 보고 클래스의 특성을 이용하여 좀 더 활용할 수 있는 방안을 연구해본다.

1. 윤년을 구하자.
// 조건이 참이면 참을 리턴하고 거짓이면 거짓을 리턴하는 경우,
// 굳이 if문을 사용할 필요없이 조건자체를 리턴하면 된다.

/*
(1) 년도를 입력받아 윤년이면 true, 아니면 false를 리턴하는 isLeapYear 함수를 만드시오. (조건문을 한줄로 작성하세요.)
	※ 윤년을 구별하는 법
	- 4의 배수이면서 100의 배수가 아니면 윤년이다.
	- 하지만, 400의 배수는 윤년이다.
(2) isLeapYear함수를 이용하여 1970년부터 2030년중에 윤년만을 화면에 출력하시오.
*/

#include  
using namespace std;

bool isLeapYear(int year);

void main( )
{
	for(int i=1970; i<2030; i++)
	{
		if(isLeapYear(i))
		{
			cout << i << "년은 윤년"  << endl;
		}
	}
}

bool isLeapYear(int year)
{
	return ((year%4==0)&&(year%100!=0)) || (year%400==0);

// 	bool isL=false;
// 	if( ( (year%4==0) && (year%100!=0) ) || (year%400==0) )
// 	{
// 		isL=true;            
// 	}
// 	return isL;
}

2. 월별날짜를 구하자.
/*
(1) 년도와 월을 입력하면 해당년도/월에 몇일이 존재하는지를 리턴하는 CountsOfDay함수를 작성하시오.
	※ CountsOfDay 함수 작성 규칙
	- 1, 3, 5, 7, 8, 10, 12월은 31일을 리턴한다.
	- 4, 6, 9, 11월은 30일을 리턴한다.
	- 2월은 윤년일경우 29일을, 윤년이 아니라면 28일을 리턴한다.
(2) CountsOfDay함수를 이용하여 2008년도의 1월달부터 12월달까지의 일수를 화면에 출력하시오.

>> 출력결과
 [ 2008년 ]
 1월 : 31일
 2월 : 29일
 3월 : 31일
 4월 : 30일
 5월 : 31일
 6월 : 30일
 7월 : 31일
 8월 : 31일
 9월 : 30일
10월 : 31일
11월 : 30일
12월 : 31일

*/

#include 
using namespace std;

bool isLeapYear(int year);				//윤년인가? 
int CountsOfDay(int year,int month);	//몇일이 있는가? 

void main()
{
	int year=2008;
	cout << " [ " << year << "년 ]" << endl;

	for(int i=1; i<=12; i++)
	{
		cout.width(2);
		cout << i << "월 : " << CountsOfDay(year, i) << "일" << endl;
	}
}

bool isLeapYear(int year)
{
	return ((year%4==0)&&(year%100!=0)) || (year%400==0);
}

int CountsOfDay(int year,int month)
{
	int temp=0;
	switch(month)
	{
		case 1:  
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12: temp=31;break;
		case 4:
		case 6:
		case 9:
		case 11: temp=30;break;
		case 2: isLeapYear(year) ? temp=29 : temp=28; break;
	}
	return temp;

	int months[]={31,28,31,30,31,30,31,31,30,31,30,31};
	int leapMonths[]={31,29,31,30,31,30,31,31,30,31,30,31};

	return isLeapYear(year)?leapMonths[month-1]:months[month-1];
	//return isLeapYear(year)&&(month == 2)?29:months[month-1];
}

3. 요일을 구하자.
// 월별날짜를 구하는 방식을 switch/case문에서 배열로 변경하여 표현하는 방법을 설명.

/*
(1) 년도를 입력받아 (년도-1)년까지의 총 일수를 리턴하는 TotalDays함수를 만드시오.
	(윤년은 366일, 윤년이 아닐경우 365일을 더해나가는 식으로 구한다.)
(2) 함수 오버로딩을 이용하여 년도와 월을 입력받아 해당년도/(월-1)월까지의 총 일수를 리턴하는 TotalDays함수를 만드시오.
	(1번에서 작성한 TotalDays함수로 구한 일수에 CountOfDays함수로 월의 합을 구한다.)
(3) 함수 오버로딩을 이용하여 년도와 월과 일을 입력받아 해당년도/월/일까지의 총 일수를 리턴하는 TotalDays함수를 만드시오.
	(2번에서 작성한 TotalDays함수로 구한 일수에 날짜를 더한다.)
(4) 년/월/일을 입력받아 해당일의 요일을 리턴하는 DayOfTheWeek 함수를 만드시오.
	(요일은 1년1월1일이 월요일임을 이용하여 구하시오. 출력은 "SUNDAY", "MONDAY", "TUESDAY", "WENDSDAY", "THURSDAY", "FRIDAY", "SATURDAY"와 같은 문자열 형태로 출력하시오.)
(5) 지금까지 작성한 함수를 이용하여 2000년 1월 1일이 1년 1월 1일로부터 몇일이 지났는지 화면에 출력하고 몇요일인지를 출력하시오.
*/

#include 
using namespace std;

enum Dates{SUN, MON, TUE, WED, THU, FRI, SAT};

bool isLeapYear(int year);
int CountsOfDay(int year,int month);
int TotalDays(int year);
int TotalDays(int year, int month);
int TotalDays(int year, int month, int day);
int DayEnumOfTheWeek(int year, int month, int day);
char* DayStringOfTheWeek(int year, int month, int day);

void main()
{
	cout << "1년 1월 1일은 월(1)요일 이다." << endl;
	int totaldays = TotalDays(2000,1,1);
	cout << "0001.01.01부터 " << "2000.01.01일까지 " << totaldays-1 << "일 경과." << endl;
	cout << "2000.01.01일은 " << DayStringOfTheWeek(2000,1,1) << "이다." << endl;
}

//윤년
bool isLeapYear(int year)
{
	return ((year%4==0)&&(year%100!=0)) || (year%400==0);
}
//월별날짜.
int CountsOfDay(int year,int month)
{
	int months[]={31,28,31,30,31,30,31,31,30,31,30,31};
	int leapMonths[]={31,29,31,30,31,30,31,31,30,31,30,31};

	return isLeapYear(year)?leapMonths[month-1]:months[month-1];
	//return isLeapYear(year)&&(month == 2)?29:months[month-1];
}
//1년부터 year-1년 까지 총 일수
int TotalDays(int year)
{     
	int temp=0;
	for(int i=1;i < year;i++) //1년 기준  
	{
		(isLeapYear(i)?temp+=366:temp+=365);
	}
	return temp;
}
//year년 1월부터  month-1월까지 총 일수 
int TotalDays(int year, int month)
{  
	int temp=TotalDays(year);
	for(int i=1;i < month;i++)
	{
		temp+=CountsOfDay(year,i);
	}
	return temp;
}
//year년 month월 day일까지 총 일수  
int TotalDays(int year, int month, int day)
{
	int temp=TotalDays(year,month);
	temp+=day;
	return temp;	
}
//요일 구하기.
int DayEnumOfTheWeek(int year, int month, int day)
{
	int totaldays = TotalDays(year, month, day);
	return totaldays%7;
}
char* DayStringOfTheWeek(int year, int month, int day)
{
	switch(DayEnumOfTheWeek(year, month, day))
	{
	case SUN  : return "SUNDAY";
	case MON  : return "MONDAY";
	case TUE  : return "TUESDAY";
	case WED  : return "WENDSDAY"; 
	case THU  : return "THURSDAY";   
	case FRI  : return "FRIDAY";   
	case SAT  : return "SATURDAY";       
	}
	return "UNKNOWN";
}
/*
1 -> 월, 1%7 = 1
2 -> 화, 2&7 = 2
...
6 -> 토, 6%7 = 6
7 -> 일, 7%7 = 0
8 -> 월, 8%7 = 1
9 -> 화, 9%7 = 2
...
13 -> 토, 13%7 = 6
14 -> 일, 14%7 = 7
*/

4. 달력을 출력하자.
/*
(1) 년,월,일을 입력받아 달력울 출력하는 PrintCalendar()함수를 작성하세요.
	(출력하기 원하는 월의 1일을 요일에 맞게 출력한다. 그 후, 순서대로 출력하며 일요일에 맞춰 줄바꿈을 하면 달력이 된다.)
*/

#include 
using namespace std;

enum Dates{SUN, MON, TUE, WED, THU, FRI, SAT};

bool isLeapYear(int year);
int CountsOfDay(int year,int month);
int TotalDays(int year);
int TotalDays(int year, int month);
int TotalDays(int year, int month, int day);
int DayEnumOfTheWeek(int year, int month, int day);
char* DayStringOfTheWeek(int year, int month, int day);
void PrintCalendar(int year, int month);

void main()
{
	for(int i=1;i<=3;i++)
	{
		PrintCalendar(2010,i);
	}
}

//윤년
bool isLeapYear(int year)
{
	return ((year%4==0)&&(year%100!=0)) || (year%400==0);
}
//월별날짜.
int CountsOfDay(int year,int month)
{
	int months[] = {31,28,31,30,31,30,31,31,30,31,30,31};
	int leapMonths[] = {31,29,31,30,31,30,31,31,30,31,30,31};

	return isLeapYear(year)?leapMonths[month-1]:months[month-1];
	//return isLeapYear(year)&&(month == 2)?29:months[month-1];
}
//1년부터 year-1년 까지 총 일수
int TotalDays(int year)
{     
	int temp = 0;
	for(int i=1;i < year;i++) //1년 기준  
	{
		(isLeapYear(i)?temp+=366:temp+=365);
	}
	return temp;
}
//year년 1월부터  month-1월까지 총 일수 
int TotalDays(int year, int month)
{  
	int temp = TotalDays(year);
	for(int i=1;i < month;i++)
	{
		temp += CountsOfDay(year,i);
	}
	return temp;
}
//year년 month월 day일까지 총 일수  
int TotalDays(int year, int month, int day)
{
	int temp = TotalDays(year,month);
	temp += day;
	return temp;	
}
//요일 구하기.
int DayEnumOfTheWeek(int year, int month, int day)
{
	int totaldays = TotalDays(year, month, day);
	return totaldays%7;
}
char* DayStringOfTheWeek(int year, int month, int day)
{
	switch(DayEnumOfTheWeek(year, month, day))
	{
	case SUN  : return "SUNDAY";
	case MON  : return "MONDAY";
	case TUE  : return "TUESDAY";
	case WED  : return "WENDSDAY"; 
	case THU  : return "THURSDAY";   
	case FRI  : return "FRIDAY";   
	case SAT  : return "SATURDAY";       
	}
	return "UNKNOWN";
}
//달력 출력.
void PrintCalendar(int year, int month)
{
	cout << "              " << " -- " << year << "년 " << month << "월 --" << endl;

	cout << "일\t월\t화\t수\t목\t금\t토" << endl;

	int starts = DayEnumOfTheWeek(year,month,1);
	
	if(starts != SUN)
	{
		// 해당 요일만큼 공백을 띄운다.
		for(int i=0;i < starts;i++)
		{
			cout << "\t";
		}
	}

	int days = CountsOfDay(year,month);
	for(int i=1;i <= days;i++)
	{
		if(i!=1 && DayEnumOfTheWeek(year,month,i) == SUN) cout << endl;

		cout.width(2);
		cout << i << "\t";
	}

	cout << "\n===================================================" << endl;
} 

5. 클래스로 만들어 활용하자.
// 달력을 클래스로 변경.
// 소멸자가 호출되는 시기는?
// 오늘날짜를 멤버변수로 저장.
// thismonth, nextmonth, prevmonth 함수 작성.
// 달력에서 오늘날짜는 기호로 표시.

#pragma once
#include 
#include 
using namespace std;

enum Dates{SUN, MON, TUE, WED, THU, FRI, SAT};

class Calendar
{
public:
	int _year;
	int _month;
	int _day;

public:
	Calendar(int year, int month, int day);
	~Calendar();

	bool isLeapYear(int year);
	int CountsOfDay(int year,int month);
	int TotalDays(int year);
	int TotalDays(int year, int month);
	int TotalDays(int year, int month, int day);
	int DayEnumOfTheWeek(int year, int month, int day);
	char* DayStringOfTheWeek(int year, int month, int day);
	void PrintCalendar(int year, int month);
	
public:
	void ThisMonth();
	void NextMonth(int count = 1);
	void PrevMonth(int count = 1);
	void SetToday(int year, int month, int day);
	bool isToday(int year, int month, int day);
};

Calendar::Calendar(int year, int month, int day)
: _year(year), _month(month), _day(day)
{
	cout << "Calendar 객체 생성" << endl;
}
Calendar::~Calendar()
{
	cout << "Calendar 객체 소멸" << endl;
}

//윤년  
bool Calendar::isLeapYear(int year)
{
	return ((year%4==0)&&(year%100!=0)) || (year%400==0);
}

//switch case  --> 배열
int Calendar::CountsOfDay(int year,int month)
{
	int months[]={31,28,31,30,31,30,31,31,30,31,30,31};
	int leapMonths[]={31,29,31,30,31,30,31,31,30,31,30,31};

	return isLeapYear(year)?leapMonths[month-1]:months[month-1];
}

//1년부터 year-1년 까지 총 일수
int Calendar::TotalDays(int year)
{     
	int temp=0;
	for(int i=1;i < year;i++) //1년 기준  
	{
		(isLeapYear(i)?temp+=366:temp+=365);
	}
	return temp;
}
//year년 1월부터  month-1월까지 총 일수 
int Calendar::TotalDays(int year, int month)
{  
	int temp=TotalDays(year);
	for(int i=1;i < month;i++)
	{
		temp+=CountsOfDay(year,i);
	}
	return temp;
}
//year년 month월 day일 까지  총 일 수  
int Calendar::TotalDays(int year, int month, int day)
{
	return TotalDays(year,month)+day;	
}

//요일 구하기.
int Calendar::DayEnumOfTheWeek(int year, int month, int day)
{
	int totaldays = TotalDays(year, month, day);
	return totaldays%7;
}
char* Calendar::DayStringOfTheWeek(int year, int month, int day)
{
	switch(DayEnumOfTheWeek(year, month, day))
	{
	case SUN  : return "SUNDAY";
	case MON  : return "MONDAY";
	case TUE  : return "TUESDAY";
	case WED  : return "WENDSDAY"; 
	case THU  : return "THURSDAY";   
	case FRI  : return "FRIDAY";   
	case SAT  : return "SATURDAY";       
	}
	return "UNKNOWN";
}

void Calendar::PrintCalendar(int year, int month)
{
	cout << "〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓" << endl;
	cout << "              " << " ◈ " << year << "년 " << month << "월 ◈" << endl;
	cout << "〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓" << endl;

	cout << "일\t월\t화\t수\t목\t금\t토" << endl;
	cout << "──────────────────────────" << endl;

	int starts=DayEnumOfTheWeek(year,month,1);
	
	if(starts != SUN)
	{
		// 해당 요일만큼 공백을 띄운다.
		for(int i=0;i < starts;i++)
		{
			cout << "\t";
		}
	}

	int days = CountsOfDay(year,month);
	for(int i=1;i <= days;i++)
	{
		if(i!=1 && DayEnumOfTheWeek(year,month,i) == SUN) cout << endl;

		cout.width(2);

		if(isToday(year, month, i)) 
			cout << "▣" << "\t";
		else 
			cout << i << "\t";	
	}

	cout << "\n〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓" << endl;
} 

void Calendar::ThisMonth()
{
	PrintCalendar(_year, _month);
}

void Calendar::NextMonth(int count)
{
	int year = _year;
	int month = _month+count;

	while(month > 12)
	{
		year += 1;
		month -= 12;
	}

	PrintCalendar(year, month);
}

void Calendar::PrevMonth(int count)
{
	int year = _year;
	int month = _month-count;

	while(month < 1)
	{
		year -= 1;
		month += 12;
	}

	PrintCalendar(year, month);
}

void Calendar::SetToday(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

bool Calendar::isToday(int year, int month, int day)
{
	return (_year == year && _month == month && _day == day);
}

void GetLocalTime(tm* local_time)
{
	// 현재시간을 초단위로 구하기.
	// 1970/01/01 00:00:00부터 시작하여 현재까지의 초.
	time_t now = time(NULL);
	// 초단위의 시간을 분리하여 구조체에 저장.
	localtime_s(local_time, &now);
	local_time->tm_year += 1900;
	local_time->tm_mon += 1;
}

int main()
{
// 	time_t now = time(NULL);
// 	tm local;
// 	localtime_s(&local, &now);

//	Calendar calendar(local.tm_year+1900, local.tm_mon+1, local.tm_mday);

	tm local;
	GetLocalTime(&local);

	//Calendar calendar(2010, 6, 17);
	Calendar calendar(local.tm_year, local.tm_mon, local.tm_mday);
	calendar.PrevMonth(6);	// 2009/12
	calendar.ThisMonth();
	calendar.NextMonth(7);	// 2011/01
	
	cout << endl << endl;
	cout << "###############<2010년 달력의 시작>################" << endl;
	for(int i=1;i <= 12;i++)
	{
		calendar.PrintCalendar(2010, i);
		cout << endl;
	}
	cout << "################<2010년 달력의 끝>#################" << endl;
	return 0;
}