-
[Java]STEP0 - 달력 알고리즘을 이용하여, 진짜 캘린더에서 나오는 달력과 똑같이 만들기개발 공부/Java 2020. 8. 21. 21:04
본 게시글은 인프런의 "만들어 가면서 배우는 JAVA 플레이그라운드" 에 나오는 과제물을 직접 구현한 것입니다.
모든 저작권은 해당 강의의 강사님이신 코드스쿼드 정호영님에게 있음을 알립니다.
<요구 사항>
- 사용자에게 년도와 월을 입력받아, 해당 년도와 월에 해당하는 달력을 출력한다.
- 달력 알고리즘을 이용하여, 실제와 똑같이 만든다.
- 윤년을 고려한다.
- 4로 나누어 떨어지지만, 100으로도 나누어 떨어지는 해는 평년이다.
- 400으로 나누어 떨어지는 해는 윤년이다. (ex. 2000년, 2400년)
<동작 구조>
기본적인 아이디어는 다음과 같다. 매우 중요!!!
- 특정 년도의 1월 1일의 요일에서 그 다음 년도의 1월 1일의 요일은 평년일 때 1일, 윤년일 때 2일 오른쪽으로 이동한다.
ex) 2019년 1월 1일이 화요일이라면, 1년 뒤인 2020년 1월 1일은 수요일이다. (2019년은 평년)
2020년 1월 1일이 수요일이라면, 1년 뒤인 2021년 1월 1일은 금요일이다. (2020년은 윤년) - 특정 월의 1일의 요일에서 그 다음 월의 1일의 요일은 해당 월의 총 일수를 7로 나눈 나머지만큼 오른쪽으로 이동한다.
ex) 1월 1일이 수요일이라면, 2월 1일은 토요일이다. (1월은 31일까지 있기 때문에, 31 % 7 == 3이기 때문이다.)
2월 1일이 토요일이고 윤년이 아니라면(즉 28일까지만 있다면), 3월 1일도 토요일이다. (28 % 7 == 0이기 때문이다.) - 위의 두 아이디어를 합치면 어느 년도의 어느 월이든 1일의 요일을 알아낼 수 있다.
ex) 기준점을 2020년 1월 1일 수요일로 놓는다고 가정했을 때, 내가 원하는 달력이 2021년 2월의 달력이라면
첫 번째 아이디어를 통해서 2021년 1월 1일의 요일이 금요일이라는 것을 알 수 있고
두 번째 아이디어를 통해서 2021년 2월 1일의 요일이 월요일이라는 것을 알 수 있다.
따라서 2021년 2월의 시작점을 알기 때문에 1일 금요일을 기준으로 28일까지 순서대로 출력하면 완성된다.
1. 몇 월인지에 따라 최대 일 수를 지정해주는 monthDay(int year, int month) 메소드
main 함수 외부에 있는 monthDay 메소드 먼저 살펴본다.
monthDay 메소드의 타입은 정수형(integer)이고, 파라미터는 int year와 int month를 갖는다.
if 문에 따라 1, 3, 5, 7, 8, 10, 12월은 31일을 갖기 때문에 31을 반환한다.
4, 6, 9, 11월은 30일을 갖기 때문에 30을 반환한다.
2월은 윤년일 경우 29를, 평년일 경우 28을 반환한다.
2. 첫 번째 아이디어를 기반으로 하여, 원하는 년도의 1월 1일의 요일 알아내기
기준일을 1583년 1월 1일 토요일로 설정하였다.
그 이유는 1582년도에 그레고리력이 공식적으로 인정받았고 1583년도 부터 완전한 그레고리력이 사용되었기 때문이다.그리고 1583년 이전은 그레고리력이 아니므로, 달력을 출력할 의미가 없기 때문이다.
따라서 for문 내의 i를 1583으로 설정하고 사용자가 입력한 년도 year 까지 윤년이 몇 번 있는지 알아낸 다음
윤년이라면 2일을, 평년이라면 1일을 요일을 오른쪽으로 이동시켰다.
요일 일월화수목금토는 각각 숫자 0123456에 대입된다.
따라서 기준일인 1583년 1월 1일 토요일은 index가 6이고
1583년도 부터 year 까지 윤년의 개수를 따져서 요일을 이동시킨 총 숫자 sum을 더한 다음
마지막으로 index가 6을 넘어가지 않도록 7로 나눈 나머지가
사용자가 입력한 year의 1월 1일의 요일이 되고 이를 first라는 변수에 넣었다.
3. 두 번째 아이디어를 기반으로 하여, 원하는 월의 1일의 요일 알아내기
현재 우리는 사용자가 입력한 year의 1월 1일의 요일을 알아내는데 까지 성공하였다.
그럼 이제 사용자가 입력한 month의 1일의 요일을 알아낸다면, 달력을 출력할 수 있을 것이다.
먼저 등장하는 for문에서 j는 1월부터 사용자가 입력한 month 까지의 범위를 가진다.
1월부터 month 전 달까지 총 일수를 monthDay 메소드를 통해서 구한 다음 계속 더해나간다.
그리고 day 변수에 first % 7을 넣게되면, day는 입력한 month의 1일의 요일을 가지게 된다.
아까부터 계속 7로 나눈 나머지를 사용하는 이유는, 요일은 일~토(0~6)의 값만을 가지기 때문이다.
이어서 나오는 num, max, start는 다음에 나올 달력 출력 코드에 꼭 필요한 변수 들이다.
4. 두 번째 아이디어를 기반으로 하여, 원하는 월의 1일의 요일 알아내기
row와 column은 각각 달력에서의 행과 열이다.
start는 달력의 첫 행(row가 0일 때)은 빈칸이 존재할 수 있기 때문에 만들어놓은 일종의 장치이다.
그래서 만약 달력의 첫 행이고(row가 0이고) start가 아직 활성화 되어있다면,
첫 행의 열을 하나씩 거치면서, 시작 열에 도달하면 start를 비활성화하고
시작 열에 도달할 때까지는 빈칸을 출력하게 만들었다.
그리고 달력의 첫 행이 완성되면 그 다음부터는 순서에 맞게 쭉 출력하였고
num과 max를 비교하여 달력이 해당 월의 최대 일 수에 도달하면 loop를 break하여 프로그램을 끝내도록 설정하였다.
<결과 화면>
사용자가 2020과 8을 각각 입력하면 실제 달력과 똑같이 출력하는 것을 볼 수 있다.
<전체 코드>
import java.util.Scanner; public class calender { public static int monthDay(int year, int month) { if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { return 31; } else if (month == 4 || month == 6 || month == 9 || month == 11) { return 30; } else { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { return 29; } else { return 28; } } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("년도를 입력하세요: "); int year = scanner.nextInt(); System.out.print("월을 입력하세요: "); int month = scanner.nextInt(); System.out.println(""); System.out.println(year + "년 " + month + "월의 달력"); System.out.println(""); int sum = 0; for (int i = 1583; i < year; i++) { if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) { // 윤년이라면 sum += 2; } else { // 평년이라면 sum += 1; } } int first = (sum + 6) % 7; // 입력한 year의 1월 1일의 요일 for (int j = 1; j < month; j++) { first += monthDay(year, j) % 7; } int day = first % 7; // 입력한 month의 1일의 요일 int num = 1; // month의 일 표시 int max = monthDay(year, month); // 해당 month가 가지는 최대 일 수 boolean start = false; System.out.println(" Sun Mon Tue Wed Thu Fri Sat "); loop: for (int row = 0; row <= 5; row++) { for (int column = 0; column <= 6; column++) { if (row == 0 && !start) { // 달력의 첫 행 if (column == day) { // 시작 일에 도달하면 start = true; } else { // 시작 일에 도달 전에는 공백 System.out.print(" "); continue; } } System.out.printf(" %02d ", num); num++; if (num > max) { // 최대 일 수에 도달하면 break loop break loop; } } System.out.println(""); } } }
본 게시물은 여기서 마치겠습니다. 읽어주셔서 감사합니다.
수정사항이나 건의사항은 댓글로 남겨주시면 감사하겠습니다.
'개발 공부 > Java' 카테고리의 다른 글
[Java]STEP1 - 3) 연산자, 우선순위, 종류(단항/이항/삼항), NaN과 Infinity (0) 2020.09.02 [Java]STEP1 - 2) 변수, 리터럴, 데이터 타입, 타입 변환 (0) 2020.08.28 [Java]STEP1 - 1) 자바의 개념, 특징, 구조, 주석과 실행문, 이클립스에 대하여 (0) 2020.08.27 [Java]STEP0 - 달력을 바탕으로 일정의 등록, 검색, 변경 기능이 있는 일정 관리 프로그램 만들기 (0) 2020.08.23 [Java]STEP0 - 사용자가 입력한 값에 따라, 크기가 다른 구구단 출력하기 (0) 2020.08.21