[Coding] 달력 출력하기 (Java)
📌 1. Problem
- 사용자에게 날짜를 입력받아 달력 혹은 해당 날짜를 출력
- 연, 월, 일 전부다 입력 받을 시 "YYYY년 MM월 DD일 입니다." 라는 문구 출력
- 연, 월 까지만 입력 시 해당 연도 월 달력을 출력
- 연도만 입력 시 해당연도 전체 달력을 출력
🔖 1-1. Problem Solving
여기서 중요한 것은 월 달력을 출력할 때 해당 월 1일이 무슨 요일인지와 거기에 따라 토요일을 기준으로 어떻게 개행("\n")처리를 해야할 것인가이다. 여기에 초점을 맞춰 코드를 짜봤다.
1-1-1. 월 1일은 무슨 요일?
1년 1월 1일이 월요일이라는 것을 이용한다. 입력한 연도 전년도까지 모든 일수를 구하고 입력한 연도의 월, 일까지의 일수를 더해주면 1년 1월 1일부터 입력한 날짜까지의 총 일수가 나올 것이다. 거기에 7을 나눈 나머지를 기준으로 0(일요일) ~6(토요일)이라는 것을 이용해 입력한 날짜가 무슨 요일인지 구한다.
여기에는 윤년이 포함되어 있기 때문에 이를 꼭 염두하고 코드를 작성해야 한다.
→ 윤년 = 4의 배수 but 100의 배수X, but 400의 배수는 포함이라는 것을 이용해 method화 했다.
1-1-2. 토요일을 기준으로 어떻게 개행처리해야 하는가?
본인은 일요일을 일주일의 시작으로 해서 토요일을 기점으로 달력 숫자가 아래로 내려가는 형식으로 작성했다. 해당 월의 1일이 무슨요일 시작인지를 아는 것이 중요하다. 위에서 (총일수 % 7)을 통한 나머지를 가지고 시작점이 어딘지를 알고 나서 토요일은 나머지가 6이기 때문에 "6 - (1일의 나머지값)"을 해주고 for loop을 돌릴 때 이 값에 도달하면 "\n"처리해서 넘겼다.
public final class CalendarDev {
public static final int[] monthArray = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public static boolean isLeaf(int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ? true : false;
}
// 해당 연도, 월 직전까지 총 일수를 계산하는 method
public static int calTotalDay(int year, int month) {
int totalDay = 0;
int priorYear = year - 1;
totalDay = priorYear * 365;
totalDay += (priorYear / 4 - priorYear / 100 + priorYear / 400);
for (int i = 0; i < (month - 1); i++) {
if (i == 1) {
// 해당연도가 윤년인 경우
// 2월달에는 29일을 총 일수에 더해주기 위해 continue 지정
if (isLeaf(year)) {
totalDay += 29;
continue;
}
}
totalDay += monthArray[i];
}
return (totalDay + 1);
}
// calendar를 그리는 method
public static void calendarForm(int year, int month) {
int dayOfWeek = calTotalDay(year, month) % 7;
System.out.println("일\t월\t화\t수\t목\t금\t토");
for (int i = 0; i < dayOfWeek; i++) {
System.out.print(" \t");
}
int monthDay = monthArray[month - 1];
// 윤년 중 2월달에 해당하는 일수 29일로 고정
if (isLeaf(year)) {
if (month == 2) {
monthDay = 29;
}
}
// 월달력 출력하는 부분
for (int i = 1; i <= monthDay; i++) {
System.out.print(i);
if ((i - 1) % 7 == (6 - dayOfWeek)) {
System.out.print("\n");
} else {
System.out.print("\t");
}
}
}
// 해당 연 달력 출력
public static void printCal(int year) {
for (int i = 1; i <= 12; i++) {
System.out.println(i + "월 달력");
calendarForm(year, i);
System.out.println();
System.out.println();
}
}
// 해당연도 월 달력 출력
public static void printCal(int year, int month) {
calendarForm(year, month);
}
// 해당연도 월 일 출력
public static void printCal(int year, int month, int day) {
System.out.printf("%d년 %d월 %d일 입니다.\n", year, month, day);
// 요일 구할 때 숫자로 요일을 표현하지말고
// MONDAY 상수화해서 명확하게 표현하는 것이 좋다.
}
}
import java.util.Scanner;
public class CalendarDevDemo {
public static void main(String[] args) {
// 1. 4개지 방법으로 표현 그 외의 표현은 예외처리
// => "2019-11-30", "2019/11/30", "2019.11.30", "2019 11 30"
// 2. 존재하지 않는 날짜도 예외처리
System.out.print("날짜를 입력하세요>");
Scanner sc = new Scanner(System.in);
String date = sc.nextLine();
sc.close();
try {
int[] dateNum = CalendarFormCheck.splitDate(date);
if (dateNum.length == 3) {
CalendarDev.printCal(dateNum[0], dateNum[1], dateNum[2]);
} else if (dateNum.length == 2) {
CalendarDev.printCal(dateNum[0], dateNum[1]);
} else {
CalendarDev.printCal(dateNum[0]);
}
} catch (IncorrectCalendarFormException e) {
System.out.println(e.getMessage());
} catch (DateOutOfBoundsException e) {
System.out.println(e.getMessage());
}
System.exit(0);
}
}
여기서 추가로 입력받을 때 형식을 지키기 위해 형식에 어긋난 입력값은 전부 예외 처리를 했다.
→ 2019-12-04 or 2019/12/04 or 2019 12 04 or 2019.12.04 이 4개의 형식으로만 받게 처리
→ 월, 일은 1의 자리 숫자들에 대해서 04 or 4와 같이 자리에 상관없이 자유롭게 입력할 수 있다.
→ 연은 1~9999년 범위를 한정 / 월, 일은 1~2자리 숫자로 제한
올바른 형식을 받았으면 다음으로 입력한 날짜가 존재하는 날짜인지도 조사했다. 이 두 개의 예외 상황에 대해 따로 예외클래스를 만들어서 throw를 이용해 임의로 던졌다.
import java.util.ArrayList;
public final class CalendarFormCheck {
public static void isRightForm(String date) throws IncorrectCalendarFormException {
// 정규표현식을 이용해서 올바른 날짜 양식을 받게끔 제한함
// year => 1 ~ 9999
// month => 2자리 정수
// day => 2자리 정수
// 중간 특수기호 : '.' '/' '-' ' ' 4개 문자로 제한
// YYYY-MM-DD or YYYY/MM/DD or YYYY.MM.DD or YYYY MM DD
if (!date.matches("[1-9][0-9]{0,3}[ -./][0-9]{1,2}[ -./][0-9]{1,2}")) {
// YYYY-MM or YYYY/MM or YYYY.MM or YYYY MM
if (!date.matches("[1-9][0-9]{0,3}[ -./][0-9]{1,2}")) {
// YYYY
if (!date.matches("[1-9][0-9]{0,3}")) {
// 모두다 만족 못할 시 false
throw new IncorrectCalendarFormException("올바른 날짜 형식이 아닙니다.");
}
}
}
}
public static int[] splitDate(String date) throws IncorrectCalendarFormException, DateOutOfBoundsException {
isRightForm(date);
// ArrayList Generic을 이용해서 입력받은 문자열을 연, 월, 일로 나눔
ArrayList<String> temp = new ArrayList<String>();
int begin = 0;
for (int i = 0; i < date.length(); i++) {
// 문자 기준으로 나눔
if (Character.isDigit(date.charAt(i)) == false) {
temp.add(date.substring(begin, i));
begin = i + 1;
}
}
temp.add(date.substring(begin));
// 문자열을 담은 ArrayList를 integer로 변환 후 배열에 저장
int[] dateNum = new int[temp.size()];
for (int i = 0; i < dateNum.length; i++) {
dateNum[i] = Integer.parseInt(temp.get(i));
}
// "연도"만 입력한 경우 이미 isRightForm method에서
// 검증을 완료했기에 따로 체크안해도 된다.
// "연도 월"까지 입력한 경우
if (dateNum.length == 2) {
if (dateNum[1] < 1 || dateNum[1] > 12) {
throw new DateOutOfBoundsException();
}
}
// "연도 월 일"까지 입력한 경우
if (dateNum.length == 3) {
if (dateNum[1] < 1 || dateNum[1] > 12) {
throw new DateOutOfBoundsException();
} else {
// 월 별로 입력한 일이 올바른지 점검
// 1,3,5,7,8,10,12월은 => 1~31일
// 4,6,9,11월은 => 1~30일
// 2월은 해당연도가 윤년인지 판단 후 => 윤년) 1~29일 / 윤년X) 1~28일
switch (dateNum[1]) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
if (dateNum[2] < 1 || dateNum[2] > 31) {
throw new DateOutOfBoundsException();
}
break;
case 2:
if (CalendarDev.isLeaf(dateNum[0]) {
if (dateNum[2] < 1 || dateNum[2] > 29) {
throw new DateOutOfBoundsException();
}
} else {
if (dateNum[2] < 1 || dateNum[2] > 28) {
throw new DateOutOfBoundsException();
}
}
break;
case 4:
case 6:
case 9:
case 11:
if (dateNum[2] < 1 || dateNum[2] > 30) {
throw new DateOutOfBoundsException();
}
break;
default:
}
}
}
return dateNum;
}
}
public class DateOutOfBoundsException extends Exception {
public DateOutOfBoundsException() {
super("존재하지 않는 날짜입니다.");
}
public DateOutOfBoundsException(String message) {
super(message);
}
}
public class IncorrectCalendarFormException extends Exception {
public IncorrectCalendarFormException() {
super("올바른 날짜 형식이 아닙니다.");
}
public IncorrectCalendarFormException(String message) {
super(message);
}
}
'Java' 카테고리의 다른 글
[Java] IO 입출력(Stream)에 대한 공부 (0) | 2019.12.05 |
---|---|
[Java] Thread에 대한 공부 (0) | 2019.12.05 |
[Java] equals & hashCode - 두 method에 대한 공부 (0) | 2019.12.02 |
[Java] Access Modifier - 접근한정자에 대한 공부 (0) | 2019.12.02 |
[Java] Class - Abstract Class를 사용하는 이유(두 번째) (0) | 2019.11.28 |
댓글
댓글(Github)
다른 글
-
[Java] IO 입출력(Stream)에 대한 공부
[Java] IO 입출력(Stream)에 대한 공부
2019.12.05 -
[Java] Thread에 대한 공부
[Java] Thread에 대한 공부
2019.12.05 -
[Java] equals & hashCode - 두 method에 대한 공부
[Java] equals & hashCode - 두 method에 대한 공부
2019.12.02 -
[Java] Access Modifier - 접근한정자에 대한 공부
[Java] Access Modifier - 접근한정자에 대한 공부
2019.12.02