[Java] 3개월 달력 출력하기 (LocalDate)

jina·2024년 4월 7일
0

Java

목록 보기
5/10
post-thumbnail
post-custom-banner

❔ 문제

사용자가 입력한 연도와 월을 기준으로 이전 월과 다음 월을 포함하는 3개월 달력을 출력하도록 코드를 작성합니다. LocalDate클래스를 활용해 아래와 같은 포맷으로 만듭니다.

💭 풀이

접근 방법

먼저 사용자에게 연도와 월을 입력받습니다. Main 메소드에서 Scanner를 이용해 사용자에게 연도와 월을 입력받고, createCalendar라는 메소드를 별도로 생성해 달력 정보를 출력하도록 셋팅했습니다.

public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("[달력 출력 프로그램]");
        System.out.print("달력의 년도를 입력해 주세요.(yyyy): ");
        int yyyy = sc.nextInt();
        sc.nextLine();
        System.out.print("달력의 월을 입력해 주세요.(mm): ");
        int mm = sc.nextInt();

        createCalendar(yyyy, mm);
    }

달력 정보를 가져올 메소드를 만듭니다. 먼저, 입력받은 연도와 월을 사용해 이전과 다음의 월과 연도를 계산합니다. 그리고 3개의 LocalDate 객체를 만들어서 앞뒤로 출력해야 하는 달의 정보를 가져옵니다. 반복 출력에 사용하기 위해 해당 정보를 배열에 담아주었습니다.

    public static void createCalendar(int yyyy, int mm) {
		// 매개변수 기준으로 앞뒤 연도와 월 선언하기
        int previousMonth = (mm == 1) ? 12 : mm - 1;
        int previousYear = (mm == 1) ? yyyy - 1 : yyyy;
        int nextMonth = (mm == 12) ? 1 : mm + 1;
        int nextYear = (mm == 12) ? yyyy + 1 : yyyy;

        // 세 달의 LocalDate 객체 생성하기
        LocalDate cal1 = LocalDate.of(previousYear, previousMonth, 1);
        LocalDate cal2 = LocalDate.of(yyyy, mm, 1);
        LocalDate cal3 = LocalDate.of(nextYear, nextMonth, 1);

        // 세 달의 시작 요일과 마지막 날짜 가져오기
        int firstDay1 = cal1.getDayOfWeek().getValue();
        int lastDate1 = cal1.lengthOfMonth();
        int firstDay2 = cal2.getDayOfWeek().getValue();
        int lastDate2 = cal2.lengthOfMonth();
        int firstDay3 = cal3.getDayOfWeek().getValue();
        int lastDate3 = cal3.lengthOfMonth();

        // 배열에 담기
        int[] months = {previousMonth, mm, nextMonth};
        int[] dates = {1, 1, 1};
        int[] lastDates = {lastDate1, lastDate2, lastDate3};
        int[] days = {firstDay1, firstDay2, firstDay3};

세 달치의 정보가 모두 준비되면, 달력의 첫 줄부터 출력합니다. 세 달을 가로로 연결해서 출력하기 위해 한 달을 한 개의 열로 설정했습니다. 또한, 한 달은 최대 6번의 주를 출력할 수 있기 때문에, 한 주를 한 개의 행으로 설정해 반복문에 넣어 주었습니다.

달력 모양의 특징을 생각했을 때, 매월 첫 날짜가 시작하는 요일이 모두 다르기 때문에 시작 요일이 되기 전까지는 날짜대신 공백을 빈 요일 개수만큼 반복해서 출력합니다.


        // 달력 첫 줄 만들기
        System.out.print("[" + previousYear + "년 " + String.format("%02d", (previousMonth)) + "월]" + "\t\t\t\t\t");
        System.out.print("[" + yyyy + "년 " + String.format("%02d", mm) + "월]" + "\t\t\t\t\t");
        System.out.println("[" + nextYear + "년 " + String.format("%02d", (nextMonth)) + "월]");

        System.out.print("일\t월\t화\t수\t목\t금\t토\t\t");
        System.out.print("일\t월\t화\t수\t목\t금\t토\t\t");
        System.out.println("일\t월\t화\t수\t목\t금\t토");

        for (int j = 0; j < 3; j++) { // 세 달 모두 할 때까지
            int i = 0;
            if (days[j] > 6) { // 해당 달의 시작 요일 값이 7 이상인 경우
                days[j] = days[j] - 7; // 줄바꿈 없이 바로 첫 번째 줄에서 시작하도록 값 조정
            }
            while (i < days[j]) { // 1. 시작 요일까지 공백 넣기
                System.out.print("\t");
                i++;
            }
            for (i = 0; i < lastDates[j]; dates[j]++, i++) {
                System.out.printf("%02d\t", dates[j]); // 2. 날짜 넣기
                days[j]++;
                if (days[j] % 7 == 0) {
                    System.out.print("\t"); // 3. 다음 달과 간격두기
                    dates[j]++;
                    break;
                }
            }
        }
        System.out.println(); // 첫 줄 완성

둘째 줄부터는 첫 요일부터 날짜가 이어지고, 해당 월의 마지막 날짜가 될 때까지 출력을 반복합니다. 이때 마지막 날짜를 먼저 출력한 달은 다음 달 출력으로 넘어가기 위해 공백을 계산해서 넣어 줍니다.

		// 달력 둘째 줄 ~ 나머지 줄 만들기
        for (int k = 0; k < 5; k++) { // 아래로 몇 번 (행)
            for (int j = 0; j < 3; j++) { // 옆으로 몇 번 (열)
                for (int i = 0; i < lastDates[j]; i++) { // 해당 월 마지막 날짜까지
                    if (dates[j] == 0) { // 이미 날짜 표기가 끝났는지 먼저 확인
                        System.out.print("\t\t\t\t\t\t\t\t");
                        break;
                    } else if (dates[j] <= lastDates[j]) {
                        System.out.printf("%02d\t", dates[j]); // 날짜 넣기
                        dates[j]++; // 날짜 증가
                        days[j]++; // 요일 증가
                    }
                    if (dates[j] > lastDates[j]) { // 날짜를 넣다가 -> 마지막 날짜를 표기할 차례라면
                        for (int n = 0; n <= (35 - days[j]); n++) { // 5주차 일요일에서 해당 요일까지 차이 계산
                            System.out.print("\t"); // 빈 요일에 공백 추가
                        }
                        dates[j] = 0;
                        break; // 다음 열로
                    }
                    if (days[j] % 7 == 0) { // 날짜를 넣다가 -> 토요일이면
                        System.out.print("\t"); // 공백 하나만 추가
                        break; // 다음 열로
                    }
                }
            }
            System.out.println(); // 나머지 줄 완성
        }

❕ 정리

LocalDate 클래스 정리

  1. LocalDate란?

    • LocalDate 클래스는 날짜 정보를 표현하는 데 사용됩니다. ISO-8601 달력 시스템에 기반하여 날짜 정보를 저장하고 다룰 수 있도록 Java 8부터 추가된 java.time 패키지 소속 클래스입니다.
  2. 사용법

    • LocalDate를 사용하면 불변(immutable) 객체를 생성하여 날짜 정보를 다룰 수 있습니다. 몇 가지 일반적인 메소드는 다음과 같습니다.
      • LocalDate.now(): 현재 날짜를 가져오기
      • LocalDate.of(year, month, dayOfMonth): 주어진 연도, 월, 일을 사용하여 LocalDate 객체를 생성
      • LocalDate.parse("YYYY-MM-DD"): 문자열로부터 LocalDate 객체를 파싱(분석해서 변환)
      • LocalDate.plusDays(daysToAdd), LocalDate.minusDays(daysToSubtract): 날짜에 지정한 일 수를 더하거나 빼기
  3. 주의점

    • 시간 정보를 포함하지 않으므로, 시간대(Time Zone)나 시간을 다루어야 하는 경우에는 LocalDateTime 또는 ZonedDateTime을 사용합니다.

✅ 문제에서 LocalDate를 활용했던 코드를 정리해 보았습니다.

  • LocalDate cal = LocalDate.of(연도, 월, 일): LocalDate 객체를 생성한 후 of() 메소드를 사용해 특정 일자 정보를 불러오기
  • int day = cal.getDayOfWeek().getValue(): getDayOfWeek() 메소드로 cal 날짜의 요일을 나타내는 열거형 DayOfWeek 데이터를 받은 후, getValue() 메소드를 이용해 열거형의 숫자 표현인 int형 값으로 반환 (일요일을 시작으로 정수 0부터 카운트)
  • int length = cal.lengthOfMonth(): cal 날짜가 속하는 달의 총 일 수를 int형 값으로 반환

참고
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html (LocalDate)
https://velog.io/@kkatalchae/Java8-Time-API-LocalDate-%EC%A0%95%EB%A6%AC (LocalDate)

profile
오늘의 기록은 내일의 보물
post-custom-banner

0개의 댓글