이 문제에서는 하나의 연도가 입력으로 주어지며, 해당 연도의 전체 달력(1월~12월)을 콘솔에 출력하는 것이 목표입니다.
연간 달력은 매달의 날짜와 요일을 모두 보여줍니다. 이 글에서는 주어진 연도의 달력을 화면에 출력하는 C++ 프로그램을 단계별로 만들어 보겠습니다.
달력을 만들기 위해서는 크게 두 가지 정보를 계산해야 합니다.
1. 특정 월의 일수 구하기
각 월마다 날짜 수가 다르므로 이를 정확히 반영해야 합니다.
- 1월, 3월, 5월, 7월, 8월, 10월, 12월은 31일입니다.
- 2월은 평년에는 28일, 윤년에는 29일입니다.
- 4월, 6월, 9월, 11월은 30일입니다.
윤년 판별 조건은 다음과 같습니다. 연도가 400으로 나누어 떨어지거나, 4로 나누어 떨어지면서 100으로는 나누어 떨어지지 않으면 윤년입니다.
2. 각 월의 시작 요일 구하기
연도와 월 정보를 바탕으로 그 달 1일이 무슨 요일(일요일~토요일)인지 계산합니다. 이 값에 따라 달력 첫 줄에서 숫자 1이 출력될 위치가 결정됩니다.
요일 계산에는 널리 알려진 사카모토(Sakamoto) 알고리즘을 활용합니다. 이 방법은 미리 정의된 오프셋 배열과 연도 보정값을 사용해 복잡한 조건문 없이 한 번의 수식으로 요일을 구할 수 있습니다.
C++ 구현 예제
다음은 2019년 달력을 출력하는 완전한 프로그램입니다.
#include<iostream>
using namespace std;
int dayNumber(int day, int month, int year){
static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
year -= month < 3;
return ( year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;
}
string getMonthName(int monthNumber){
string months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
return (months[monthNumber]);
}
int numberOfDays (int monthNumber, int year){
switch(monthNumber){
case 0 :
case 2 :
case 4 :
case 6 :
case 7 :
case 9 :
case 11: return(31);
break;
case 1 :
if (year % 400 == 0 || (year % 4 == 0 && year %100 != 0))
return (29);
else
return (28);
break;
case 3 :
case 5 :
case 8 :
case 10 : return(30);
break;
}
}
void printCalendar(int year){
cout<<"\t\t\t Calendar - Year "<<year;
int days;
int current = dayNumber (1, 1, year);
for (int i = 0; i < 12; i++){
days = numberOfDays (i, year);
cout<<endl<<"\t\t ----X----"<<getMonthName (i).c_str()<<"----X---- \t\t"<<endl;
cout<<" Sun Mon Tue Wed Thu Fri Sat \n";
int k;
for (k = 0; k < current; k++)
cout<<"\t";
for (int j = 1; j <= days; j++){
printf("%5d", j);
if (++k > 6){
k = 0;
cout<<endl;
}
}
if (k)
cout<<endl;
current = k;
}
return;
}
int main(){
int year = 2019;
printCalendar(year);
return (0);
}
코드 설명
- dayNumber(): 특정 날짜의 요일을 0(일요일)~6(토요일) 범위의 값으로 반환합니다.
- getMonthName(): 월 번호(0~11)에 해당하는 영문 월 이름을 반환합니다.
- numberOfDays(): 해당 월의 일수를 반환하며, 2월은 윤년 여부를 검사해 28일 또는 29일을 결정합니다.
- printCalendar(): 1월부터 12월까지 순회하며 각 달의 제목과 요일 헤더를 출력한 뒤, 1일이 시작되는 요일만큼 공백을 두고 날짜를 정렬해 출력합니다. 또한 지난 달의 마지막 요일 위치를 다음 달의 시작 위치로 이어받아 자연스럽게 연결합니다.
출력 결과
Calendar - Year 2019
----X----January----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
----X----February----X----
Sun Mon Tue Wed Thu Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28
----X----March----X----
Sun Mon Tue Wed Thu Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
----X----April----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
----X----May----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
----X----June----X----
Sun Mon Tue Wed Thu Fri Sat
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
----X----July----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
----X----August----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
----X----September----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
----X----October----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
----X----November----X----
Sun Mon Tue Wed Thu Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
----X----December----X----
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31