java.util.Calendar는 java.util.Date의 문제점(가변성, 부족한 기능, 불편한 API 등)을 개선하기 위해 Java 1.1부터 도입된 추상 클래스.
Calendar는 특정 시점의 날짜와 시간을 표현하고, 날짜/시간 필드(년, 월, 일, 시, 분, 초 등)를 조작하고, 날짜/시간 계산을 수행하는 기능을 제공.
java.util.Calendar의 특징:
- 추상 클래스 (Abstract Class): Calendar는 추상 클래스이므로 직접 인스턴스를 생성할 수 없다. 대신 Calendar.getInstance() 팩토리 메서드를 사용하여 특정 로케일과 타임존에 맞는 Calendar의 하위 클래스(예: GregorianCalendar)의 인스턴스를 얻어야 한다.
- 가변성 (Mutable): java.util.Date와 마찬가지로 Calendar 객체도 가변(mutable). set(), add(), roll() 등의 메서드를 사용하여 객체의 값을 변경할 수 있다. 이 때문에 멀티스레드 환경에서 주의해서 사용해야 한다.
- 타임존 지원: Calendar는 타임존(TimeZone)을 지원. getInstance() 메서드에 TimeZone 객체를 전달하여 특정 타임존의 Calendar를 얻을 수 있다.
- 로케일 지원: Calendar는 로케일(Locale)을 지원. getInstance() 메서드에 Locale 객체를 전달하여 특정 언어와 국가에 맞는 Calendar를 얻을 수 있다 (예: 주의 시작 요일, 월 이름 등).
- 다양한 필드: 년(YEAR), 월(MONTH), 일(DATE/DAY_OF_MONTH), 요일(DAY_OF_WEEK), 시(HOUR/HOUR_OF_DAY), 분(MINUTE), 초(SECOND), 밀리초(MILLISECOND) 등 다양한 날짜/시간 필드를 지원.
- 날짜/시간 계산: add() 메서드를 사용하여 특정 필드의 값을 더하거나 뺄 수 있다. roll() 메서드를 사용하면 상위 필드에 영향을 주지 않고 특정 필드의 값을 순환시킬 수 있다 (예: 월을 12에서 1로 변경할 때 연도는 변경되지 않음).
- Lenient Mode: Calendar는 기본적으로 lenient mode로 동작. 잘못된 날짜를 입력했을 때, 자동으로 날짜를 조정. 예를 들어, 13월과 같이 없는 달을 입력하면 다음 해의 1월로 자동으로 조정. setLenient(false)로 설정하면, 유효하지 않은 날짜를 입력할 경우 예외(IllegalArgumentException)가 발생.
java.util.Calendar의 주요 메서드:
- getInstance(): 현재 날짜와 시간, 기본 로케일, 기본 타임존으로 설정된 Calendar 객체를 반환.
- getInstance(TimeZone zone): 지정된 타임존과 기본 로케일로 설정된 Calendar 객체를 반환.
- getInstance(Locale aLocale): 지정된 로케일과 기본 타임존으로 설정된 Calendar 객체를 반환.
- getInstance(TimeZone zone, Locale aLocale): 지정된 타임존과 로케일로 설정된 Calendar 객체를 반환.
- get(int field): 지정된 필드(년, 월, 일, 시, 분, 초 등)의 값을 반환.
- set(int field, int value): 지정된 필드의 값을 설정.
- set(int year, int month, int date): 년, 월, 일을 설정.
- set(int year, int month, int date, int hourOfDay, int minute): 년, 월, 일, 시, 분을 설정.
- set(int year, int month, int date, int hourOfDay, int minute, int second): 년, 월, 일, 시, 분, 초를 설정.
- add(int field, int amount): 지정된 필드의 값을 주어진 양만큼 더하거나 뺀다. (음수도 가능). 날짜/시간 계산에 사용. 상위 필드에 영향을 줌. (예: 일을 더하면 월이 바뀔 수 있음).
- roll(int field, int amount): add()와 유사하지만, 상위 필드에 영향을 주지 않는다. (예: 월을 12에서 1로 변경해도 연도는 변경되지 않음)
- getTime(): 이 Calendar 객체가 나타내는 시점을 java.util.Date 객체로 반환.
- setTime(Date date): 이 Calendar 객체의 시간을 주어진 java.util.Date 객체로 설정.
- getTimeInMillis(): 이 Calendar 객체가 나타내는 시점을 1970년 1월 1일 00:00:00 GMT로부터의 밀리초로 반환.
- setTimeInMillis(long millis): 이 Calendar 객체의 시간을 1970년 1월 1일 00:00:00 GMT로부터 주어진 밀리초로 설정.
- getActualMaximum(int field): 지정된 필드가 가질 수 있는 최대값을 반환 (예: 월의 마지막 일). 달력 시스템(GregorianCalendar 등)과 다른 필드의 값에 따라 달라질 수 있다.
- getActualMinimum(int field): 지정된 필드가 가질 수 있는 최소값을 반환.
- getMaximum(int field): 지정된 필드가 가질 수 있는 이론적 최대값을 반환. getActualMaximum과 다를 수 있다. (e.g. DAY_OF_MONTH의 최댓값)
- getMinimum(int field): 지정된 필드가 가질 수 있는 이론적 최솟값을 반환.
- getTimeZone(): 이 Calendar 객체의 타임존을 반환.
- setTimeZone(TimeZone value): 이 Calendar 객체의 타임존을 설정.
- before(Object when): 이 Calendar 객체가 주어진 객체(일반적으로 다른 Calendar 객체)보다 이전 시점인지 확인.
- after(Object when): 이 Calendar 객체가 주어진 객체보다 이후 시점인지 확인.
- equals(Object obj): 이 Calendar 객체와 주어진 객체가 같은 시점을 나타내는지 비교.
- compareTo(Calendar anotherCalendar): 이 Calendar 객체를 주어진 Calendar 객체와 비교하여 순서를 정함.
- clear(): 모든 필드의 값을 초기화(undefined 상태로).
- clear(int field): 지정된 필드의 값을 초기화.
- isSet(int field): 지정된 필드의 값이 설정되어 있는지 확인. (clear() 호출 후에는 설정되지 않은 상태)
- setLenient(boolean lenient): 날짜/시간 해석을 엄격하게(false) 또는 유연하게(true, 기본값) 할지 설정.
Calendar 필드 상수 (주요 상수):
Calendar 클래스는 날짜/시간 필드를 나타내는 정수 상수들을 제공. get(), set(), add(), roll() 등의 메서드에서 사용.
- Calendar.YEAR: 년
- Calendar.MONTH: 월 (0부터 시작, 0: 1월, 1: 2월, ..., 11: 12월)
- Calendar.DATE 또는 Calendar.DAY_OF_MONTH: 일
- Calendar.DAY_OF_WEEK: 요일 (1: 일요일, 2: 월요일, ..., 7: 토요일)
- Calendar.DAY_OF_YEAR: 년의 몇 번째 날
- Calendar.WEEK_OF_YEAR: 년의 몇 번째 주
- Calendar.WEEK_OF_MONTH: 월의 몇 번째 주
- Calendar.AM_PM: 오전/오후 (0: AM, 1: PM)
- Calendar.HOUR: 시 (12시간제, 0-11)
- Calendar.HOUR_OF_DAY: 시 (24시간제, 0-23)
- Calendar.MINUTE: 분
- Calendar.SECOND: 초
- Calendar.MILLISECOND: 밀리초
- Calendar.ZONE_OFFSET: GMT로부터의 타임존 오프셋 (밀리초)
- Calendar.DST_OFFSET: 일광 절약 시간(Daylight Saving Time) 오프셋 (밀리초)
java.util.Calendar 사용 예시:
Java
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public class CalendarExample {
public static void main(String[] args) {
// 현재 날짜/시간으로 Calendar 객체 생성 (기본 로케일, 기본 타임존)
Calendar now = Calendar.getInstance();
// 년, 월, 일, 시, 분, 초 얻기
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH) + 1; // 월은 0부터 시작하므로 +1
int day = now.get(Calendar.DAY_OF_MONTH);
int hour = now.get(Calendar.HOUR_OF_DAY);
int minute = now.get(Calendar.MINUTE);
int second = now.get(Calendar.SECOND);
System.out.printf("Current Date: %d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);
// 특정 날짜/시간으로 설정
now.set(2023, 11, 25); // 2023년 12월 25일 (월은 0부터 시작)
System.out.println("Specific Date: " + now.getTime());
// 날짜 계산 (add 메서드 사용)
now.add(Calendar.DAY_OF_MONTH, 7); // 7일 후
System.out.println("After adding 7 days: " + now.getTime());
now.add(Calendar.MONTH, -2); // 2달 전
System.out.println("After subtracting 2 months: " + now.getTime());
// 특정 타임존의 Calendar 얻기
Calendar japanTime = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"));
System.out.println("Japan Time: " + japanTime.getTime());
// Calendar 객체를 Date 객체로 변환
Date date = now.getTime();
System.out.println("Date from Calendar: " + date);
//특정 필드의 최댓값, 최솟값
int maxDay = now.getActualMaximum(Calendar.DAY_OF_MONTH);
int minDay = now.getActualMinimum(Calendar.DAY_OF_MONTH);
System.out.println("Max day of month = " + maxDay);
System.out.println("Min day of month = " + minDay);
// roll() 메서드 사용 예제: 월을 변경하되 연도는 변경하지 않음
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MONTH, Calendar.DECEMBER); // 12월로 설정
System.out.println("Before roll: " + calendar.getTime());
calendar.roll(Calendar.MONTH, 1); // 월을 1 증가 (12 -> 0, 즉 1월로)
System.out.println("After roll: " + calendar.getTime()); // 연도는 그대로
// lenient 모드
Calendar cal = Calendar.getInstance();
cal.setLenient(false); // 엄격 모드
cal.set(Calendar.MONTH, 13); // 13월 (잘못된 값)
try {
System.out.println(cal.getTime());//예외 발생
} catch (IllegalArgumentException e) {
System.out.println("IllegalArgumentException caught: " + e.getMessage());
}
}
}
GregorianCalendar:
GregorianCalendar는 Calendar의 구체적인 하위 클래스 중 하나로, 우리가 일반적으로 사용하는 그레고리력을 구현한 클래스. Calendar.getInstance()를 호출하면 대부분의 경우 GregorianCalendar 객체가 반환된다 . GregorianCalendar는 윤년(leap year) 계산 등 그레고리력에 특화된 기능을 제공.
Java
//GregorianCalendar 사용
Calendar cal = new GregorianCalendar(2024, 11, 25); // 2024년 12월 25일
System.out.println(cal.getTime());
//윤년 확인
GregorianCalendar gCal = new GregorianCalendar();
if(gCal.isLeapYear(2024)){
System.out.println("2024 is leap year");
}
주의사항 및 권장 사항:
- 가변성: Calendar는 가변 객체이므로 멀티스레드 환경에서 주의해서 사용해야 한다. 여러 스레드에서 동시에 Calendar 객체를 변경하면 문제가 발생할 수 있다.
- 월(Month)은 0부터 시작: 월은 0부터 시작합니다 (0: 1월, 1: 2월, ..., 11: 12월). 이 점을 잊으면 실수를 하기 쉽다.
- SimpleDateFormat과 함께 사용 시 주의: SimpleDateFormat은 thread-safe하지 않으므로 멀티스레드 환경에서 공유하면 문제가 발생할 수 있다. 각 스레드마다 별도의 SimpleDateFormat 객체를 생성하거나, ThreadLocal을 사용하거나, Java 8 이상의 java.time.format.DateTimeFormatter를 사용하는 것이 좋다.
- Lenient Mode: setLenient(false)로 설정하여 잘못된 날짜 입력에 대해 예외를 발생시켜 실수를 방지하는 것이 좋다.
- Java 8 이상에서는 java.time 패키지 사용: java.util.Calendar는 여전히 유용하지만, Java 8부터는 java.time 패키지(LocalDate, LocalTime, LocalDateTime, ZonedDateTime 등)를 사용하는 것이 훨씬 권장. java.time 패키지는 불변 객체, 명확한 타임존 처리, 더 직관적인 API 등 여러 장점을 제공.
.