[Java/자바] 날짜와 시간 (Date, calendar)

Date와 Calendar

Date 클래스 날짜와 시간에 관한 정보를 표현하는 클래스로 JDK 1.0부터 제공되어온 클래스이며

Calendar 클래스는 Date 클래스의 부족한 기능이 추가되어 날짜 연산을 유용하게 할 수 있도록 JDK 1.1부터 제공하기 시작했습니다.

 

Calendar이 Date보다는 훨씬 나았지만 점점 몇가지 단점들이 발견되었고, 후에 JDK 1.8부터 'java.time 패키지'로 기존의 단점들을 개선한 새로운 클래스들이 추가되었습니다.

 

p.s : 두 클래스의 차이점을 알아보니 date와 calendar를 사용함으로써 문제점들이 발생해  많은 사람들이 "Joda-Time"이라는 오픈소스 라이브러리를 사용한다고 합니다.


Calendar 객체 생성

Calendar는 추상클래스이기 때문에 객체를 생성할 수 없고 getInstance() 메서드를 통해 완전히 구현된 클래스의 인스턴스를 얻어야 합니다.

Calendar cal = new Calendar(); // 에러!

Calendar cal = Calendar.getInstance(); // OK

Date와 Calendar간의 변환

Calendar가 새로 추가되면서 Date는 대부분의 메서드가 "deprecated(사라지게 된)"되어서 잘 사용되지 않다고 합니다.

그럼에도 Date를 필요로 하는 메서드들이 있기 때문에 Calendar와 Date간의 변환이 필요한 일이 생깁니다.

// Calendar를 Date로 변환
Calendar cal = Calendar.getInstance();
	...
Date d = new Date((cal.getTimeInMillis());

// Date를 Calendar로 변환
Date d = new Date();
	...
Calendar cal = Calendar.getInstance();
cal.setTime(d);

Calendar 클래스 메서드 예제

getInstance()를 통해서 얻은 인스턴스는 기본적으로 현재 시스템의 날짜와 시간에 대한 정보를 담습니다.

만약 원하는 날짜나 시간으로 설정하고 싶다면 set메서드를 사용하면 됩니다.

void set(int field, int value)
void set(int year, int month, int date)
void set(int year, int month, int date, int hourOfDay, int minute)
void set(int year, int month, int date, int hourOfDay, int minute, int second)

 

여기서 한가지 주의 해야 할 점은 today.get(Calendar.MONTH)로 얻어오는 값으 범위가 1 ~ 12가 아닌 0 ~ 11이라는 점입니다.

그래서 얻어오는 값이 0이면 1월, 11이면 12월을 의미합니다.

 

 

Calendar 클래스의 메서드와 상수
import java.util.*;

class  CalendarEx1 {
    public static void main(String[] args) {
        // 기본적으로 현재날짜와 시간으로 설정된다.
        Calendar today = Calendar.getInstance();
        System.out.println("이 해의 년도 : " + today.get(Calendar.YEAR));
        System.out.println("월(0~11, 0:1월): "	+ today.get(Calendar.MONTH));
        System.out.println("이 해의 몇 째 주: "		+ today.get(Calendar.WEEK_OF_YEAR));
        System.out.println("이 달의 몇 째 주: "		+ today.get(Calendar.WEEK_OF_MONTH));

        // DATE와 DAY_OF_MONTH는 같다.
        System.out.println("이 달의 몇 일: "		+ today.get(Calendar.DATE));
        System.out.println("이 달의 몇 일: "		+ today.get(Calendar.DAY_OF_MONTH));
        System.out.println("이 해의 몇 일: "		+ today.get(Calendar.DAY_OF_YEAR));
        System.out.println("요일(1~7, 1:일요일): " + today.get(Calendar.DAY_OF_WEEK)); // 1:일요일, 2:월요일, ... 7:토요일
        System.out.println("이 달의 몇 째 요일: "	+ today.get(Calendar.DAY_OF_WEEK_IN_MONTH));
        System.out.println("오전_오후(0:오전, 1:오후): " + today.get(Calendar.AM_PM));
        System.out.println("시간(0~11): "	+ today.get(Calendar.HOUR));
        System.out.println("시간(0~23): "	+ today.get(Calendar.HOUR_OF_DAY));
        System.out.println("분(0~59): "	+ today.get(Calendar.MINUTE));
        System.out.println("초(0~59): "	+ today.get(Calendar.SECOND));
        System.out.println("1000분의 1초(0~999): " + today.get(Calendar.MILLISECOND));

        // 천분의 1초를 시간으로 표시하기 위해 3600000으로 나누었다.(1시간 = 60 * 60초)
        System.out.println("TimeZone(-12~+12): " + (today.get(Calendar.ZONE_OFFSET)/(60*60*1000)));
        System.out.println("이 달의 마지막 날: " + today.getActualMaximum(Calendar.DATE) ); // 이 달의 마지막 일을 찾는다.
    }
}

 

 

두 날짜간의 차이

두 날짜간의 차이를 구하기 위해서는 두 날짜를 최소단위인 초 단위로 변경한 다음 그 차이를 구합니다.

getTimeMillis()는 1/1000초 단위로 값을 반환하기 때문에 초단위로 얻기 위해서는 1000으로 나눠주고,

일단위로 얻기 위해서는 "24(시간) * 60(분) * 60(초) * 1000"으로 나누어 줍니다.

import java.util.*;

class CalendarEx2 {
    public static void main(String[] args) {
        // 요일은 1부터 시작하기 때문에, DAY_OF_WEEK[0]은 비워두었다.
        final String[] DAY_OF_WEEK = {"", "일", "월", "화", "수", "목", "금", "토"};

        Calendar date1 = Calendar.getInstance();
        Calendar date2 = Calendar.getInstance();

        // month의 경우 0부터 시작하기 때문에 8월인 경우, 7로 지정해야한다.
        // date1.set(2015, Calendar.AUGUST, 15);와 같이 할 수도 있다.
        date1.set(2020, 11, 26); // 2015년 8월 15일로 날짜를 설정한다.
        System.out.println("set(date1)은 " + toString(date1) + DAY_OF_WEEK[date1.get(Calendar.DAY_OF_WEEK)] + "요일이고,");
        System.out.println("오늘(date2)은 " + toString(date2) + DAY_OF_WEEK[date2.get(Calendar.DAY_OF_WEEK)] + "요일입니다.");

        // 두 날짜간의 차이를 얻으려면, getTimeInMillis() 천분의 일초 단위로 변환해야한다.
        long difference = (date2.getTimeInMillis() - date1.getTimeInMillis());
        System.out.println("그 날(date1)부터 지금(date2)까지 " + difference / 1000 + "초가 지났습니다.");
        System.out.println(
                "일(day)로 계산하면 " + difference / (24 * 60 * 60 * 1000) + "일입니다.");    // 1일 = 24 * 60 * 60 * 1000
    }

    public static String toString(Calendar date) {
        return date.get(Calendar.YEAR) + "년 " + (date.get(Calendar.MONTH) + 1) + "월 " + date.get(Calendar.DATE) + "일 ";
    }
}

 

초단위의 차이를 시, 분, 초로 출력

가장 큰 단위인 시간 단위(3600초)로 나누고 남은 나머지를 다시 분 단위(60초)로 나누면 그 나머지는 초 단위의 값이 됩니다.

import java.util.*;

class CalendarEx3 {
    public static void main(String[] args) {
        final int[] TIME_UNIT = {3600, 60, 1}; // 큰 단위를 앞에 놓는다.
        final String[] TIME_UNIT_NAME = {"시간 ", "분 ", "초 "};

        Calendar time1 = Calendar.getInstance();
        Calendar time2 = Calendar.getInstance();

        // time1의 시간을 10시 20분 30초로 설정한다.
        time1.set(Calendar.HOUR_OF_DAY, 10);
        time1.set(Calendar.MINUTE, 20);
        time1.set(Calendar.SECOND, 30);

        // time2의 시간을 20시 30분 10초로 설정한다.
        time2.set(Calendar.HOUR_OF_DAY, 20);
        time2.set(Calendar.MINUTE, 30);
        time2.set(Calendar.SECOND, 10);

        System.out.println(
                "time1 :" + time1.get(Calendar.HOUR_OF_DAY) + "시 " + time1.get(Calendar.MINUTE) + "분 " + time1.get(
                        Calendar.SECOND) + "초");
        System.out.println(
                "time2 :" + time2.get(Calendar.HOUR_OF_DAY) + "시 " + time2.get(Calendar.MINUTE) + "분 " + time2.get(
                        Calendar.SECOND) + "초");

        long difference = Math.abs(time2.getTimeInMillis() - time1.getTimeInMillis()) / 1000;
        System.out.println("time1과 time2의 차이는 " + difference + "초 입니다.");

        String tmp = "";

        for (int i = 0; i < TIME_UNIT.length; i++) {
            tmp += difference / TIME_UNIT[i] + TIME_UNIT_NAME[i];
            difference %= TIME_UNIT[i];
        }

        System.out.println("시분초로 변환하면 " + tmp + "입니다.");
    }
}

 

 

add(int field, int amount) 메서드와 roll(int field, int amount) 메서드의 사용 예제

add() 메서드는 지정한 필드의 값을 원하는 만큼 증가, 감소 시켜 특정 날짜 또는 시간을 기점으로 일정기간 전후의 날짜와 시간을 알아 낼 수 있습니다.

 

roll()메서드 또한 지정한 필드의 값을 증가, 감소시킬 수 있는데, add()와 달리 다른 필드에 영향을 미치지 않습니다.

단, Calendar.DATE가 말일(그 달의 마지막 일)일 때, roll메서드를 이용해 월 필드(Calendar.MONTH)를 변경하면 일 필드(Calendar.DATE)에 영향을 미칠 수 있습니다.

import java.util.Calendar;

class  CalendarEx4 {
    public static void main(String[] args) {
        Calendar date = Calendar.getInstance();

        date.set(2005, 7, 31);	// 2005년 8월 31일

        System.out.println(toString(date));
        System.out.println("= 1일 후 =");
        date.add(Calendar.DATE, 1);
        System.out.println(toString(date));

        System.out.println("= 6달 전 =");
        date.add(Calendar.MONTH, -6);
        System.out.println(toString(date));

        System.out.println("= 31일 후(roll) =");
        date.roll(Calendar.DATE, 31);
        System.out.println(toString(date));

        System.out.println("= 31일 후(add) =");
        date.add(Calendar.DATE, 31);
        System.out.println(toString(date));
    }

    public static String toString(Calendar date) {
        return date.get(Calendar.YEAR)+"년 "+ (date.get(Calendar.MONTH)+1) +"월 " + date.get(Calendar.DATE) + "일";
    }
}

 

아래 코드는 roll()을 호출해 월 필드를 1 증가 시켯을 때, 월 필드가 2월이 되는데 2월에는 31일이 없기 때문에 2월의 말일인 28일로 일 필드가 변경되었습니다.

add()도 이와 같은 경우 자동으로 변경됩니다.

import java.util.Calendar;

class  CalendarEx5 {
    public static void main(String[] args) {
        Calendar date = Calendar.getInstance();

        date.set(2022, 0, 31);	// 2022년 1월 31일
        System.out.println(toString(date));
        date.roll(Calendar.MONTH, 1);
        System.out.println(toString(date));
    }

    public static String toString(Calendar date) {
        return date.get(Calendar.YEAR)+"년"+ (date.get(Calendar.MONTH)+1) + "월" + date.get(Calendar.DATE) + "일";
    }
}

 

달력 만들기 예제

년도와 달의 입력값을 받아 해당 년도에 해당되는 달의 달력을 만듭니다.

import java.util.Calendar;
import java.util.Scanner;

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

        System.out.println("년도(Year)를 입력하세요.\nEx:) 2022");
        int year = scanner.nextInt();

        System.out.println("달(Month)를 입력하세요.\nEx:) 12");
        int month = scanner.nextInt();

        scanner.close();

        int START_DAY_OF_WEEK = 0;
        int END_DAY = 0;

        Calendar startDay = Calendar.getInstance();       // 시작일
        Calendar endDay = Calendar.getInstance();       // 끝일

        // 월의 경우 0 부터 11까지의 값을 가지므로 1을 빼주어야 한다.
        // 예를 들어, 2004년 11월 1일은 sDay.set(2004, 10, 1);과 같이 해줘야 한다.
        startDay.set(year, month - 1, 1);
        endDay.set(year, month, 1);

        // 다음달의 첫날에서 하루를 빼면 현재달의 마지막 날이 된다.
        // 12월 1일에서 하루를 빼면 11월 30일이 된다.
        endDay.add(Calendar.DATE, -1);

        // 첫 번째 요일이 무슨 요일인지 알아낸다.
        START_DAY_OF_WEEK = startDay.get(Calendar.DAY_OF_WEEK);
        // endDay에 지정된 날짜를 얻어온다.
        END_DAY = endDay.get(Calendar.DATE);

        System.out.println("      " + year + "년 " + month + "월");
        System.out.println(" SU MO TU WE TH FR SA");

        // 해당 월의 1일이 어느 요일인지에 따라서 공백을 출력한다.
        // 만일 1일이 목요일이라면 공백을 4번 찍는다.(일요일부터 시작)
        for (int i = 1; i < START_DAY_OF_WEEK; i++) {
            System.out.print("   ");
        }

        for (int i = 1, n = START_DAY_OF_WEEK; i <= END_DAY; i++, n++) {
            System.out.print((i < 10) ? "  " + i : " " + i);
            if (n % 7 == 0) {
                System.out.println();
            }
        }
    }
}


년과 월의 차이 계산
class CalendarEx8 {
    public static void main(String[] args) {
        String date1 = "201508";
        String date2 = "201405";

        // 년과 월을 substring으로 잘라서 정수로 변환한다.
        // 년에 12를 곱해서 월로 변환한 다음에 뺄셈을 하면 개월차를 구할 수 있다.
        int month1 = Integer.parseInt(date1.substring(0, 4)) * 12 + Integer.parseInt(date1.substring(4));
        int month2 = Integer.parseInt(date2.substring(0, 4)) * 12 + Integer.parseInt(date2.substring(4));

        System.out.println(date1 + "과 " + date2 + "의 차이는 " + Math.abs(month1 - month2) + "개월 입니다.");
    }
}


참고자료 : 자바의 정석3