java 날짜객체!

Date

Date - jdk 1.0 제공한 클래스, java.util 패키지 안에 있다.

Date 출력 함수

Date now = new Date();
System.out.println(now); // Wed Jan 30 10:24:46 KST 2019
System.out.println(now.getTime()); // 1548812137967

System.out.println(now.getDate());      // 30
System.out.println(now.getDay());       // 3, 일요일=0 수요일=3
System.out.println(now.getMonth()+1);   // 1, 0~11 값으로 표현
System.out.println(now.getYear()+1900); // 2019, 1900 더해줘야함
System.out.println(now.getHours());     // 4
System.out.println(now.getMinutes());   // 47

System.out.println(now.toString());       // Mon May 31 16:50:40 KST 2010
System.out.println(now.toGMTString());    // 31 May 2010 07:50:40 GMT
System.out.println(now.toLocaleString()); // 1.    5. 31 오후 4:50:40

String d = String.format("%d년 %d월 %d일 %d:%d:%d (%c)"
    , 1900 + now.getYear()
    , now.getMonth() + 1
    , now.getDate()
    , now.getHours()
    , now.getMinutes()
    , now.getSeconds()
    , "일월화수목금토".charAt( now.getDay() )  // 3
);
System.out.println(d); // 2010년 5월 31일 22:2:27 (월)

Date 수정 및 비교 함수

Date when = new Date(2019-1900, 1-1, 29); // 현재 30일
Date past = new Date(2010-1900, 5-1, 1);  // 2010 5 1 설정
System.out.println(now.before(past));     // false
System.out.println(now.after(past));      // true

now.setYear(2010-1900);
now.setMonth(5-1);
now.setDate(1);

설정되지 않은 시, 분, 초는 현재시, 분, 초로 들어감…
이렇게 자투리로 남는 시간값 때문에 오차가 생길 수 도 있다.
날짜끼리 차이 계산할때에는 시간은 모두 0으로 세팅하거나 clone메서드를 사용해서 서로 같은 시간을 가리키는 식으로 오차를 없애야함.

SimpleDateFormat

날짜를 다양하게 다루기 위해서 사용되는 클래스 Format클래스중 가장 많이 사용된다.

다양한 형식을 날짜를 Calendar클래스로 저장하고 CalendarDate 클래스의 날짜를 다양한 형식으로 변환 출력한다.

String pattern = "G"; // BC AD
String pattern = "y"; // 년도
String pattern = "M"; // 월
String pattern = "w"; // 주(월단위)
String pattern = "W"; // 주(년단위)
String pattern = "d"; // 일(월단위)
STring pattern = "D"; // 일(년단위)
STring pattern = "H"; // 시(24시)
STring pattern = "h"; // 시(12시)
String pattern = "E"; // 요일
Date now = new Date();
String pattern = "yyyy년 MM월 dd일 HH'h' mm'm' ss's' (E)"; 
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
System.out.println( sdf.format(now) ); // 2019년 02월 02일 03h 48m 21s (토)

날짜 객체를 문자열로 변환해서 출력도 가능하지만 문자열로 날짜 객체를 만들 수 있다.

String strDate = "2019년 02월 02일";
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy년 M월 d일"); //부모클레스인 DateFormat에 UpCasting
Date date = sdf2.parse(strDate);
System.out.println(date.toLocaleString()); // 1.    2. 2 오전 12:00:00
System.out.println(sdf2.format(date)); // 2018년 2월 2일

만약 문자열이 잘못된 포멧이면 ParseException 예외를 발생한다. SimpleDateFormatparse 메서드로 생성되는 날짜 객체 역시 설정되지 않은 시, 분, 초는 0으로 세팅된다.

String pattern = "y/M/d";
Scanner sc = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Date inDate = null;
System.out.print("날짜입력 y/M/d: ");

while (true) {
    try {
        inDate = sdf.parse(sc.nextLine());
        break;
    } catch (ParseException e) {
        System.out.print("잘못된 날짜 포멧... 다시입력: (y/M/d): ");
    }
}    
/* 
날짜입력 y/M/d: 2019.01.1
잘못된 날짜 포멧... 다시입력: (y/M/d): 2019/1/01
1.    1. 1 오전 12:00:00 

날짜입력 y/M/d: 2019/13/41
1.    2. 10 오전 12:00:00
*/
// (13월 41일) 을 자동으로 이뤌한다.  

Calendar

Calendar는 추상클래스로 new를 통해 객체생성 불가능하다.
Calendar.getInstance() 정적 메서드로 인스턴스를 가져올 수 있음.

Calendar cal = Calendar.getInstance(); //현재날짜....생성

추상클래스인 Calender 를 구현하는 클래스는 아래 3개, 기본 GregorianCalendar 를 사용한다.

  • BuddhistCalendar
  • JapaneseImperialCalendar
  • GregorianCalendar

다음과 같이 GregorianCalendar 생성자에 날짜, 시간을 설정해 생성 가능.
설정하지 않은 시간, 초, 밀리초는 모두 0으로 초기화된다.

Calendar cal = new GregorianCalendar(2018,5-1,20);
System.out.println(cal.get(Calendar.YEAR));    // 2019
System.out.println(cal.get(Calendar.MONTH)+1); // 2, 0~11
System.out.println(cal.get(Calendar.DATE));    //
System.out.println( cal.get(Calendar.DAY_OF_WEEK) ); // 1, /*일(1) 월(2) 화(3) 수(4) 목(5) 금(6) 토(7)*/
System.out.println("일월화수목금토".charAt(cal.get(Calendar.DAY_OF_WEEK)-1)); // 일
System.out.println(cal.get(Calendar.HOUR));        // 9,  12시 기준
System.out.println(cal.get(Calendar.HOUR_OF_DAY)); // 21, 24시기준
System.out.println(cal.get(Calendar.MINUTE));      // 59
System.out.println(cal.get(Calendar.SECOND));      // 27
cal.set(Calendar.YEAR, 2019); // 년만 변경
cal.set(Calendar.MONTH, 1-1); // 월만 변경
cal.set(Calendar.DATE, 1);    // 일만 변경
cal.set(2018, 12-1, 21);      // 한꺼번에 변경

cal.add(Calendar.DATE, 100);  // 100일 후 날짜
cal.add(Calendar.DATE, -100); // 100일 전 날짜

2월의 경우 28일까지 밖에 없음으로 set 메서드로 달 설정시 28일을 넘지 않도록 주의.
마지막 날짜를 설정하고 싶다면 Calendar.DAY_OF_MONTH 값을 사용

설령 2월에 30일을 설정한다 하더라도 내부 validation 을 통해 자동 이월된다.

Calendar cal = Calendar.getInstance();
cal.set(2018, 1-1, 1); 
for (int i = 0; i < 12; i++) {
    cal.set(Calendar.MONTH, i);
    System.out.println(cal.getActualMaximum(Calendar.DAY_OF_MONTH));
    // 31 28 31 30 31 30 31 31 30 31 30 31 
}

cal = new GregorianCalendar(2019, Calendar.FEBRUARY, 1); // 2024년 2월 1일 설정
System.out.println("Original Date: " + cal.getTime()); // Original Date: Fri Feb 01 00:00:00 KST 2019
cal.set(Calendar.DATE, 30); // 2월에 30일을 설정
System.out.println("Updated Date: " + cal.getTime());  // Updated Date: Sat Mar 02 00:00:00 KST 2019

Date -> Calender, Calender -> Date 변환 함수


public static Calendar date2cal(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    return cal;
}

public static Date cal2date(Calendar cal) {
    return cal.getTime();
    // return new Date( cal.getTimeInMillis() );
}

날짜사이의 차이 구하기

getTimeInMillis() 메서드를 통해 두 날짜 사이의 ‘밀리초’를 가질고 날짜 차이를 얻기 때문에 ‘시’ 이하의 시간단위로 인해 미세한 오차가 생길수 있음으로 clone을 통해 ‘시’이하 단위를 일치화.

GregorianCalendar으로 생성하면 시, 분, 초 모두 0으로 설정되기 때문에 clone()으로 복제할 필요가 없으니 Calendar로 차이를 구해야 한다면 GregorianCalendar로 생성하자.

Calendar openingDay = new GregorianCalendar(2018, Calendar.DECEMBER, 21);
Calendar now = (Calendar) openingDay.clone();
now.add(Calendar.DATE, 25); // 25 일 뒤로 설정
// 두 날짜 간의 시간 차이를 밀리초 단위로 계산
long gapInMillis = now.getTimeInMillis() - openingDay.getTimeInMillis();
System.out.println(gapInMillis + " ms");  // 2160000000 ms
System.out.println((gapInMillis / 1000) + " s");  // 2160000 s
System.out.println((gapInMillis / 1000 / 60) + " m");  // 36000 m
System.out.println((gapInMillis / 1000 / 60 / 60) + " h");  // 600 h
System.out.println((gapInMillis / 1000 / 60 / 60 / 24) + " d");  // 25 일

Calendar객체를 사용한 달력그리기

...
int year = 2019;
int month = 3;
//달 1일 의 요일 필요, 마지막 일자 필요
printCalendar(year, month);
private static void printCalendar(int year, int month) {
    int week = 0;
    int endDay = 0;
    
    
    System.out.printf("%d년 %d월\n", year, month);
    String weeks = "일월화수목금토";
    for (int i = 0; i < weeks.length(); i++) {
        System.out.printf("%c\t", weeks.charAt(i));
    }
    System.out.println();
    
    Calendar cal = Calendar.getInstance();
    cal.set(year, month-1, 1);
    week = cal.get(Calendar.DAY_OF_WEEK);
    cal.add(Calendar.DATE, -week+1); //전달의 일요일 부터 출력.
    for (int i = 0; i < 42; i++) { 
        System.out.printf("%d\t", cal.get(Calendar.DATE));
        cal.add(Calendar.DATE, 1);
        if(i%7==6)
            System.out.println();
    }
}

%(데이터형) 따로 형식을 붙여줄 필요없이 MessageFormat을 사용하면 몇번째 인자인지만 알려주면 된다. 출력할 변수 타입이 뭔지 모를때 사용하면 편할듯 함.

LocalDate, LocalTime, LocalDateTime 클래스

jdk 1.8부터 추가된 java.time 패키지.
Date, Calendar 부족한 메서드들을 추가하여 시간 관련 연산을 더욱 편하게 하기 위해 만들어짐.

java.time - 날짜, 시간 다루는 핵심 클래스 대거 포함되어 있다.

  1. java.time.chrono - 표준이 아닌 달력 시스템을 위한 클래스 제공.
  2. java.time.format - 날짜 시간을 파싱해서 원하는 포멧으로 변환 출력.
  3. java.time.temporal - 날짜 시간 필드와 단위를 위한 클래스 제공
  4. java.time.zone - 시간대(timezone)과 관련된 클래스 제공

java.time 클래스들은 String클래스 처럼 불변하다.

Calendar cal = Calendar.getInstance();
cal.set(field, value); //cal객체의 정보가 바뀜

LocalDate now = LocalDate.now();
now = now.plusDays(1); //객체가 바뀌지 않아 다시 대입
System.out.println(now); //2019-02-01

Calendar는 기존 인스턴스의 정보를 바꾸지만 java.time패키지의 객체들은 기존의 인스턴스를 버리고 새 인스턴스를 참조한다.
now.plusDays(1) 하루 증가후 꼭 반환하는 값을 재참조 해야한다.

java.time 에서는 날짜를 다루는 클래스와 시간을 다루는 클래스들 분리해두었다.

날짜 - LocalDate
시간 - LocalTime
날짜시간 - LocalDateTime

LocalDate

날짜 생성

LocalDate now = LocalDate.now();
LocalDate dday = LocalDate.of(2019, 2, 4);

// 2010년도에서 daysOfYear 37일이 지난 날짜 객체를 반환한다.
// daysOfYear 가 366 일 경우 DateTimeException 에러발생
LocalDate date = LocalDate.ofYearDay(2010, 37); 
System.out.println(date); //2010-02-06 출력

날짜 읽기

/* public final class LocalDate extends Object
implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable */
LocalDate now = LocalDate.of(2019, 2, 4);
System.out.println(now); //2019-02-04

// java.time.temporal.ChronoField 를 사용한 get
System.out.println(now.get(ChronoField.YEAR));          //2019
System.out.println(now.get(ChronoField.MONTH_OF_YEAR)); //2
System.out.println(now.get(ChronoField.DAY_OF_MONTH));  //4
System.out.println(now.get(ChronoField.DAY_OF_WEEK));   //1, 0(일) ~ 6(토)

System.out.println(now.getYear());       // 2019
System.out.println(now.getMonthValue()); // 2
System.out.println(now.getDayOfMonth()); // 4
System.out.println(now.lengthOfMonth()); // 28, 마지막날짜 반환
System.out.println(now.lengthOfYear());  // 365, 년도의 일 수 반환

// 자매품 lengthOfYear 메서드 - 해당 년도의 일 수 를 반환  

날짜 변경

now = now.plusDays(1);
System.out.println(now); //2019-02-01
now = now.plusDays(-1);
System.out.println(now); //2019-01-31
now = now.minusDays(1);
System.out.println(now); //2019-01-30

now.plusMonths(long time);
now.plusWeeks(long time);
now.plusYears(long time);

now = now.withMonth(9);
now = now.withDayOfMonth(11);
System.out.println(now); //2019-09-11

now = now.with(ChronoField.MONTH_OF_YEAR, 9); 
now = now.with(ChronoField.DAY_OF_MONTH, 11);
System.out.println(now); //2019-09-11

날짜 비교

LocalDate now = LocalDate.of(2019, 2, 5);
LocalDate dday = LocalDate.of(2019, 2, 4);
System.out.println(now.compareTo(dday)); // 피연산자(now)가 같으면 0, 작으면 -1, 크면 1
System.out.println(now.isEqual(dday));   // false
System.out.println(now.isAfter(dday));   // true
System.out.println(now.isBefore(dday));  // false

LocalTime

시간 생성

/* public final class LocalTime extends Object
implements Temporal, TemporalAdjuster, Comparable<LocalTime>, Serializable */
LocalTime now = LocalTime.now();
System.out.println(LocalTime.now());       // 16:30:48.239, 시:분:초.밀리초
System.out.println(LocalTime.of(14, 52));  // 14:52
System.out.println(LocalTime.of(14, 52, 10, 999999999)); // 14:52:10.999999999

시간 읽기

LocalTime now = LocalTime.now();

int time = now.getHour();
int min = now.getMinute();
int second = now.getSecond();
int mils = now.getNano();
System.out.printf("%d시 %d분 %d.%d초", time, min, second, mils); // 14시 52분 10.999999999초

time = now.get(ChronoField.HOUR_OF_DAY);
min = now.get(ChronoField.MINUTE_OF_HOUR);
second = now.get(ChronoField.SECOND_OF_MINUTE);
mils = now.get(ChronoField.MILLI_OF_SECOND);
System.out.printf("%d시 %d분 %d.%d초", time, min, second, mils); // 14시 52분 10.999초

시간 변경

LocalTime now = LocalTime.now();
System.out.println(now); // 16:40:37.660521
System.out.println(now.truncatedTo(ChronoUnit.HOURS)); // 16:00

now = now.withHour(10);   // 시(hour)를 10으로 변경
now = now.withMinute(30); // 분(minute)을 30으로 변경
now = now.withSecond(45); // 초(second)를 45로 변경
now = now.withNano(500);  // 나노초(nano)를 500으로 변경
System.out.println(now); // 10:30:45.000000500

now = now.with(ChronoField.HOUR_OF_DAY, 10);      // 시(hour)를 10으로 변경
now = now.with(ChronoField.MINUTE_OF_HOUR, 30);   // 분(minute)을 30으로 변경
now = now.with(ChronoField.SECOND_OF_MINUTE, 45); // 초(second)를 45로 변경
now = now.with(ChronoField.NANO_OF_SECOND, 500);  // 나노초(nano)를 500으로 변경
System.out.println(now); // 10:30:45.000000500

LocalDateTime

날짜와 시간정보 모두 포함 LocalDate, LocalTime, LocalDateTime 모두 비슷한 상속관계를 가진다.

날짜 생성

/* public final class LocalDateTime extends Object
implements Temporal, TemporalAdjuster, ChronoLocalDateTime<LocalDate>, Serializable */
LocalDateTime ldt1 = LocalDateTime.of(2019, 8, 2, 14, 30);
LocalDateTime ldt2 = LocalDateTime.of(2019, 8, 2, 14, 30, 45);
LocalDateTime ldt3 = LocalDateTime.of(2019, 8, 2, 14, 30, 45, 123456789);
LocalDateTime ldt4 = LocalDateTime.of(2019, Month.AUGUST, 2, 14, 30);

LocalDate d = LocalDate.of(2015, 12, 31);
LocalTime t = LocalTime.of(12, 34, 56);
LocalDateTime ldt5 = LocalDateTime.of(d, t);

System.out.println(ldt1); // 2019-08-02T14:30
System.out.println(ldt2); // 2019-08-02T14:30:45
System.out.println(ldt3); // 2019-08-02T14:30:45.123456789
System.out.println(ldt4); // 2019-08-02T14:30
System.out.println(ldt5); // 2015-12-31T12:34:56

parse, format, DateTimeFormatter

  • parse : String -> [LocalDate, LocalTime, LocalDateTime] 변환
  • format : [LocalDate, LocalTime, LocalDateTime] -> String 변환

LocalDate 파싱

LocalDate.parse 메서드의 기본 문자열 패턴은 yyyy-MM-dd 이다.
DateTimeFormatter.ofPattern 메서드로 문자열 패턴 변경 가능하다.

// String -> LocalDate
LocalDate d1 = LocalDate.parse("2018-09-18");
LocalDate d2 = LocalDate.parse("2018년 09월 19일", 
            DateTimeFormatter.ofPattern("yyyy년 MM월 dd일"));
System.out.println(d1); // 2018-09-18
System.out.println(d2); // 2018-09-19

// LocalDate -> String
String date_str = d1.format(s, DateTimeFormatter.ofPattern("yyyy년 MM월 dd일"));
System.out.println(date_str); // 2018년 11월 11일

LocalTime.parse 메서드의 기본 문자열 패턴은 hh:mm:ss 이다(설정하지 않은 초 이하는 다 0으로 설정됨).
LocalTime 역시 DateTimeFormatter 를 사용해서 문자열 패턴 변경이 가능하다.

// String -> LocalTime
LocalTime t1 = LocalTime.parse("10:10:10");
LocalTime t2 = LocalTime.parse("10.10.10", 
            DateTimeFormatter.ofPattern("HH.mm.ss"));
System.out.println(t1); //10:10:10
System.out.println(t2); //10:10:10

// LocalTime -> String
String time_str = time.format(DateTimeFormatter.ofPattern("HH.mm.ss"));
System.out.println(time_str); //10.10.10

TemporalAdjusters - 시간조정자

해당일자로 다음주 토요일, 다음달 2번째 월요일 등, 까다로운 날짜 계산이 필요할때 TemporalAdjusters 를 사용하면 편하다.

위같은 연산을 plus, minus, with 메서드 로만 진행하면 분명 복잡한 연산이 필요하겠지만 TemporalAdjusters 에는 이미 해당 메서드들이 다 정의 되어있다.

LocalDate today = LocalDate.now();
System.out.println(today);

System.out.println(today.with(TemporalAdjusters.firstDayOfMonth())); //첫째날 반환
System.out.println(today.with(TemporalAdjusters.lastDayOfMonth()));  //마지막날 반환
System.out.println(today.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY))); //첫 화요일 반환
System.out.println(today.with(TemporalAdjusters.lastInMonth(DayOfWeek.TUESDAY))); //마지막 화요일 반환
System.out.println(today.with(TemporalAdjusters.previous(DayOfWeek.TUESDAY))); // 저번 화요일
System.out.println(today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY))); //다음 화요일
/* 
2019-02-07
--------------
2019-02-01
2019-02-28
2019-02-05
2019-02-26
2019-02-05
2019-02-12
*/

Period, Duration - 두 시간, 날짜 차이를 구하는 클래스

날짜 차이가 얼마나 나는지 구하고 싶을땐 Period 메서드를 사용하면 편하다.

LocalDate d1 = LocalDate.of(2014, 1, 1);
LocalDate d2 = LocalDate.of(2015, 12, 31);

Period pe = Period.between(d1, d2);
System.out.println(pe.getYears());  // 1
System.out.println(pe.getMonths()); // 11
System.out.println(pe.getDays());   // 30

총 1년 11개월 30일 차이난다는것을 알 수 있다.

시간 차이가 얼마나 나는지 구하고 싶을땐 Duration

LocalTime t1 = LocalTime.of(00, 00, 10);
LocalTime t2 = LocalTime.of(12, 34, 56);

Duration du = Duration.between(t1, t2);
System.out.printf("%d시 %d분 %d초\n",
        du.toHours(), 
        du.toMinutes()%60,
        du.getSeconds()%60
        ); // 12시 34분 46초

특정 값 하나만 얻어와도 된다면 until 메서드를 사용하면 편하다.

LocalDate endDay = LocalDate.of(2019, 7, 19);
LocalDate today = LocalDate.now();

long dday = today.until(endDay, ChronoUnit.DAYS);
System.out.println(dday); // 162

달려그리기

image02

윤년확인하기

먼저 윤년인지 여부를 구하는 함수를 작성, 내장함수와 직접구현 가능.

LocalDate now = LocalDate.now();
now.isLeapYear(); //객체의 년도가 윤년인지 true false반환

윤년을 구하는 공식을 간단하게 java코드로 만들어보자.
다른 언어에서도 마찬가지로 적용 가능할 것이다.

  • 서력 기원 연수가 4로 나누어떨어지는 해는 윤년으로 한다.
  • 서력 기원 연수가 4, 100으로 나누어떨어지는 해는 평년으로 한다.
  • 서력 기원 연수가 4, 100, 400으로 나누어떨어지는 해는 윤년으로 둔다.

즉 년도가 4로 나누어 떨어지면서 100으로 나뉘지 않으면서 그중에 400으로 나뉘어지면 윤년으로 본다!

public static int getLastDay(int year, int month) {
  int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  return isleapyear(year) && month==2 ? 29 : days[month-1];
}

public static boolean isleapyear(int year) {
  return year%100!=0 && year%4==0 || year%400==0 ? true : false;
}

시작요일 구하기

달의 시작요일이 무슨요일인지, 마지막일이 몇일인지 알아야한다.

마지막일은 윤년일 제외하곤 고정값이다(30, 31 반복).

public static int getLastDay(int year, int month) {
  int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  return isleapyear(year) && month==2 ? 29 : days[month-1];
}

시작요일을 구하는 공식은 1년 1월 1일이 월요일임으로, 원하는 날짜까지의 일 수 를 구한후 7로 나눈 나머지값이 요일이 된다.

그럼 2019-4-1 일의 요일을 구하고 싶다면 1-1-1부터 2019-4-1까지의 일 수 를 구하면 된다.

먼저 1년 ~ 2018년 까지의 일수를 구한다.
365 곱한것 뒤의 + (year-1)/4 - (year-1)/100 + (year-1)/400는 윤년에 해당하는 일수를 추가로 더해주기 위한것, 윤년의 경우 366일이니까!
그리고 4월 1일의 까지의 일수를 for문을 통해 추가로 더해준다.

public static int getDayOfWeek(int year, int month) {
  int totDays = (year-1)*365 + (year-1)/4 - (year-1)/100 + (year-1)/400;
  for (int i = 1; i < month; i++) {
    totDays+=getLastDay(year, i);
  }
  totDays+=1;

  return totDays%7;
}

미리 마지막일을 저장해 놓은 배열을 정의해두었다. (2월은 윤년의 경우 29일로 반환)

이제 달력 그리기를 위한 모든 값이 구해졌으니 달력을 그려보자!

public class Calender {
    public static void main(String[] args) throws IOException {
        int year, month;
        System.out.print("년 월 입력하세요: "); //2010 5
        Scanner sc = new Scanner(System.in);
        year = sc.nextInt();
        month = sc.nextInt();
        createCalendar(year, month);
    }
    
    //시작 요일과 마지막 날짜를 구하는 메서드
    public static void createCalendar(int year, int month) {
        int dayOfWeek = getDayOfWeek(year, month);
        int lastDay = getLastDay(year, month);
        printCalender(year, month, dayOfWeek, lastDay);
    }
    
    //단순 선 긋는 함수 
    public static void drawLine(int n) { 
        for (int i = 0; i < n; i++) 
        System.out.print('-');
        System.out.println();
    }
    
    public static void printCalender(int year, int month, int dayOfWeek, int lastDay) {
        System.out.printf("\t\t%d년 %d월\n",year,month);
        String week = "일월화수목금토";
        drawLine(50);
        for (int i = 0; i < 7; i++)
            System.out.printf("%c \t", week.charAt(i));
        System.out.println();
        drawLine(50);
        drawLine(50);
    
        int cnt=0;
        for (int i = 0; i < dayOfWeek; i++) {
            System.out.print('\t');
            cnt++;
        } //시작 요일 수만큼 공백을 준다.  
    
        for (int d = 1; d <= lastDay; d++) {
            System.out.printf("%d \t", d);
            if(cnt%7==6)
                System.out.println();
            cnt++;
        } //차례대로 요일 출력 1~30
    }

    public static int getDayOfWeek(int year, int month) {
        int totDays = (year-1)*365 + (year-1)/4 - (year-1)/100 + (year-1)/400;
        for (int i = 1; i < month; i++) {
            totDays+=getLastDay(year, i);
        }
        totDays+=1;

        return totDays%7;
    }
    
    public static int getLastDay(int year, int month) {
        int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        return isleapyear(year) && month==2 ? 29 : days[month-1];
    }

    public static boolean isleapyear(int year) {
        return year%100!=0 && year%4==0 || year%400==0 ? true : false;
    }
}

출력

년 월 입력하세요: 2019 4
        2019년 4월
--------------------------------------------------
일     월     화      수      목     금      토     
--------------------------------------------------
--------------------------------------------------
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     

모든 2019년에 해당하는 달력을 출력해보자(1~12 모두 출력)
3차원 배열을 사용해 출력해보자.

위 그림의 달력 형식을 보면 총 6행 7열까지 나올 수 있다.
위 사진의 경우 5행 7열이지만 1일이 일요일부터 시작한다던가 하는 경우 6줄이 출력될 수 있다.

그럼으로 int형 배열 [6][7]짜리 2차원 배열을 사용해야 1달을 출력할 수 있다.

12달을 출력하려면 [12][6][7] 3차월 배열이 필요

import java.util.Scanner;

public class Calender {
    public static void main(String[] args) {
        //달력 출력시 필요한것, 마지막 날짜, 시작 요일
        Scanner sc = new Scanner(System.in);
        int year = sc.nextInt();
        int[][][] dal = new int[12][6][7];
        createDaliek(dal, year);
        printDaliek(dal, year, 4); //4행으로 출력
    }

    private static void createDaliek(int[][][] dal, int year) {
        int[] lastDays = new int[12];
        int[] dayOfWeek = new int[12];
        int day = 1;
        //먼저 매 달의 시작요일, 마지막일을 저장할 배열을 초기화
        for (int i = 0; i < lastDays.length; i++) {
            lastDays[i] = getLastDay(year, i);
            dayOfWeek[i] = getDayOfWeek(year, i);
        }

        for (int i = 0; i < dal.length; i++) { //12개월
            day = 1;
            for (int j = 0; j < dal[0].length; j++) { //6반복
                for (int k = 0; k < dal[0][0].length; k++) { //7반복
                    // 시작요일이 배열 인덱스 j*7+k보다 크다면 -1로 설정
                    // day가 마지막일보다 커진다면 -1로 설정, -1은 출력되지 않는다.
                    if (j * 7 + k < dayOfWeek[i] || day > lastDays[i])
                        dal[i][j][k] = -1;
                    else
                        dal[i][j][k] = day++;
                }
            }
        } //end first for
    }

    private static void printDaliek(int[][][] dal, int year, int num) {
        String week = "일월화수목금토";
        for (int z = 0; z < 12 / num; z++) {
            for (int i = 0; i < num; i++) {
                System.out.printf("\t%d년 %d월\t\t", year, z * num + i + 1);
            }
            System.out.println();

            for (int i = 0; i < num; i++) {
                drawLine(27);
            }
            System.out.println();

            for (int i = 0; i < num; i++) {
                for (int p = 0; p < week.length(); p++) {
                    System.out.printf("%c  ", week.charAt(p));
                }
                System.out.print("\t");
            }
            System.out.println();

            for (int j = 0; j < 2; j++) {
                for (int i = 0; i < num; i++) {
                    drawLine(27);
                }
                System.out.println();
            }

            //눈을 크게 뜨고 확인하자....
            for (int i = 0; i < dal[0].length; i++) {
                for (int j = num * z; j < num + num * z; j++) {
                    for (int k = 0; k < dal[0][0].length; k++) {
                        if (dal[j][i][k] == -1)
                            System.out.print("    ");
                        else
                            System.out.printf("%2d  ", dal[j][i][k]);
                    }
                    System.out.print("\t");
                }
                System.out.println();
            }
            System.out.println();
        }
    }

    public static void drawLine(int n) {
        for (int i = 0; i < n; i++)
            System.out.print('-');
        System.out.print("\t");
    }

    public static int getDayOfWeek(int year, int month) {
        int totDays = (year - 1) * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
        for (int i = 0; i < month; i++) //0~11
            totDays += getLastDay(year, i);
        totDays += 1;
        return totDays % 7;
    }

    public static int getLastDay(int year, int month) {
        int[] days = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        return isleapyear(year) && month == 1 ? 29 : days[month]; //0 = 1월, 11=12월
    }

    public static boolean isleapyear(int year) {
        return year % 100 != 0 && year % 4 == 0 || year % 400 == 0 ? true : false;
    }
}

일단 3차원 배열로 저장해 놓았다면 출력에서 for문막 약간 변경시켜주면 된다.

출력

2019
    2019년 1월            2019년 2월            2019년 3월            2019년 4월        
---------------------------    ---------------------------    ---------------------------    ---------------------------    
일  월  화  수  목  금  토      일  월  화  수  목  금  토      일  월  화  수  목  금  토      일  월  화  수  목  금  토      
---------------------------    ---------------------------    ---------------------------    ---------------------------    
---------------------------    ---------------------------    ---------------------------    ---------------------------    
         1   2   3   4   5                           1   2                           1   2           1   2   3   4   5   6      
 6   7   8   9  10  11  12       3   4   5   6   7   8   9       3   4   5   6   7   8   9       7   8   9  10  11  12  13      
13  14  15  16  17  18  19      10  11  12  13  14  15  16      10  11  12  13  14  15  16      14  15  16  17  18  19  20      
20  21  22  23  24  25  26      17  18  19  20  21  22  23      17  18  19  20  21  22  23      21  22  23  24  25  26  27      
27  28  29  30  31              24  25  26  27  28              24  25  26  27  28  29  30      28  29  30                      
                                                                31                                                              

    2019년 5월            2019년 6월            2019년 7월            2019년 8월        
---------------------------    ---------------------------    ---------------------------    ---------------------------    
일  월  화  수  목  금  토      일  월  화  수  목  금  토      일  월  화  수  목  금  토      일  월  화  수  목  금  토      
---------------------------    ---------------------------    ---------------------------    ---------------------------    
---------------------------    ---------------------------    ---------------------------    ---------------------------    
             1   2   3   4                               1           1   2   3   4   5   6                       1   2   3      
 5   6   7   8   9  10  11       2   3   4   5   6   7   8       7   8   9  10  11  12  13       4   5   6   7   8   9  10      
12  13  14  15  16  17  18       9  10  11  12  13  14  15      14  15  16  17  18  19  20      11  12  13  14  15  16  17      
19  20  21  22  23  24  25      16  17  18  19  20  21  22      21  22  23  24  25  26  27      18  19  20  21  22  23  24      
26  27  28  29  30  31          23  24  25  26  27  28  29      28  29  30  31                  25  26  27  28  29  30  31      
                                30                                                                                              

    2019년 9월            2019년 10월            2019년 11월            2019년 12월        
---------------------------    ---------------------------    ---------------------------    ---------------------------    
일  월  화  수  목  금  토      일  월  화  수  목  금  토      일  월  화  수  목  금  토      일  월  화  수  목  금  토      
---------------------------    ---------------------------    ---------------------------    ---------------------------    
---------------------------    ---------------------------    ---------------------------    ---------------------------    
 1   2   3   4   5   6   7               1   2   3   4   5                           1   2       1   2   3   4   5   6   7      
 8   9  10  11  12  13  14       6   7   8   9  10  11  12       3   4   5   6   7   8   9       8   9  10  11  12  13  14      
15  16  17  18  19  20  21      13  14  15  16  17  18  19      10  11  12  13  14  15  16      15  16  17  18  19  20  21      
22  23  24  25  26  27  28      20  21  22  23  24  25  26      17  18  19  20  21  22  23      22  23  24  25  26  27  28      
29  30                          27  28  29  30  31              24  25  26  27  28  29  30      29  30  31                      

카테고리:

업데이트: