자바에서는 날짜를 표현할 때 여러가지 방법으로 표현할 수 있다. 기본적으로 String으로 표현하는 방법도 나쁘지 않겠지만 다만 String으로 날짜와 시간을 나타낼 경우 개발자마다 표현하는 방식이 다를 수가 있어 일정한 규칙을 가지고 표현하는 방법이 있으면 참 좋을 것 같다. 그렇기 때문에 자바에서는 날짜와 시간에 대한 처리를 유연하게 할 수 있도록 다양한 클래스를 제공한다. 이번에 알아볼 클래스는 다음과 같다.
java.util
Date
Calendar
java.sql
Timestamp
java.time
LocalDate
LocalTime
LocalDateTime
java.text
SimpleDateFormat - 이건 포맷팅 관련이어서 나중에 따로 다룰 예정
Date
Date 클래스는 1970년 1월 1일 00:00:00 GMT (그리니치 표준시)부터 현재 시간까지의 밀리초를 저장하고 관리하는 클래스이다.
위 이미지의 설명에도 나와 있듯이 long 값으로 반환된다.
생성 / 시간 추출
Date date = new Date();
@Test
public void printDate() {
System.out.println(date.getTime());
}
실행한 결과를 보면 현재 시간이 long 값으로 표현되는 것을 볼 수 있다.
그럼 생성자에는 어떤 값을 넣어서 초기화 할 수 있을까?
기본 생성자를 호출하면 시스템의 현재 시간을 밀리초로 저장하게 된다.
그 다음은 long 값을 직접 입력하여 사용한다. 그 이후에 년월일 등을 지정해서 사용하는 부분은 @Deprecated 애너테이션이 붙은 걸보니 사용하지 않는 것이 좋겠다. 설명을 보면 JDK 1.1부터 Deprecated 되었다고 한다.
그러니 결국
Date()
Date(long date)
이 두 가지만 사용하면 되겠다.
Calendar
Calendar는 추상 클래스이기 때문에 직접 인스턴스를 만들 수는 없고 구현된 객체를 할당해주어야 한다. 쉽게 사용할 수 있는 방법은 Calendar 클래스 내의 getInstance() 메서드를 이용하는 것이다.
생성
정적 메서드이기 때문에 그냥 클래스 단위로 불러오면 된다. 로케일 설정을 이용해서 시간을 만들어 내는 듯 하다.
일단은 getInstance()를 이용해서 출력해보자.
Calendar calendar = Calendar.getInstance();
@Test
public void printCalendar() {
System.out.println(calendar.getTime());
}
Date와는 다르게 어떤 format이 있는 것을 알 수 있다.
단순히 getTime() 말고 직접 가져오고 싶은 것을 지정할 수도 있다.
날짜 / 시간 추출
Calendar 클래스 내에 상수로 미리 어떤 숫자들이 설정되어 있는 것을 볼 수 있다. 이 값들을 이용해서 특정 부분만 얻어올 수 있는 것이다.
get() 메서드를 이용할 때 위에서 봤던 값들을 넣으면 된다.
Calendar calendar = Calendar.getInstance();
@Test
public void printCalendar() {
System.out.println("년: " + calendar.get(Calendar.YEAR));
System.out.println("월: " + calendar.get(Calendar.MONTH));
System.out.println("일: " + calendar.get(Calendar.DAY_OF_MONTH));
}
실행했더니 결과가 뭔가 이상하다. 작성 날짜 기준 오늘은 12월이다. 근데 11로 출력된 것이 아닌가..!
MONTH 부분에 살포시 마우스를 올려보자.
그렇다. 1월이 0으로 출력된다고 한다. 그러니까 12월은 11로 출력됐었던 것이다.
다음은 날짜와 시간을 설정하는 법을 알아보자.
앞서 살펴본 것처럼 set() 메서드에 인자를 적절하게 원하는대로 설정하면 된다.
Calendar calendar = Calendar.getInstance();
@Test
public void printCalendar() {
calendar.set(Calendar.YEAR, 2008);
calendar.set(Calendar.MONTH, 5); // 실제로는 6월임
calendar.set(Calendar.MONTH, Calendar.FEBRUARY); // 이렇게 월로 직접 지정도 가능
calendar.set(Calendar.DAY_OF_MONTH, 21);
System.out.println("년: " + calendar.get(Calendar.YEAR));
System.out.println("월: " + calendar.get(Calendar.MONTH));
System.out.println("일: " + calendar.get(Calendar.DAY_OF_MONTH));
}
생각했던대로 잘 출력된다. 앞서 살펴봤듯이 월의 경우 1 적게 표현되므로 주의해야 한다. 이렇게 헷갈리는 경우에 쉽게 사용할 수 있는 방법이 있는데 위의 코드에서 처럼 직접 영문 월을 기입해주면 된다. 이미 클래스 내에서 상수로 다 지정되어 있기 때문에 그냥 가져다 사용하면 된다.
직접 들어가서 보면 2월인데 1로 미리 지정되어 있는 것을 볼 수 있다.
자 이제 날짜 연산에 대해서 알아보자.
수정
calendar.add(Calendar.YEAR, 1);
System.out.println("년: " + calendar.get(Calendar.YEAR));
System.out.println("월: " + calendar.get(Calendar.MONTH));
System.out.println("일: " + calendar.get(Calendar.DAY_OF_MONTH));
덧셈, 뺄셈의 경우 add() 메서드를 통해서 할 수 있다. 사용 방법은 get()과 유사하게 특정 년월일 등을 지정하고 더하고 빼고 싶은 int 값을 넣어주면 된다.
2023년에 1을 더해서 2024로 만들었다. 내년 이맘때 쯤에는 과연 무엇을 하고 있을까..?
calendar.add(Calendar.YEAR, -1);
System.out.println("년: " + calendar.get(Calendar.YEAR));
System.out.println("월: " + calendar.get(Calendar.MONTH));
System.out.println("일: " + calendar.get(Calendar.DAY_OF_MONTH));
빼는 것은 단순히 음수로 바꿔서 지정하면 된다.
Calendar를 사용해보니까 확실히 Date를 사용한 것보다 여러가지 다양한 날짜 지정과 출력을 할 수 있었다!

Timestamp
Timestamp는 java.util 패키지의 Date 클래스를 상속받아 만든 클래스이다. 해당 클래스의 장점은 나노 초까지 표시할 수 있다는 점이 있다. 나노 초까지 표시할 일이 무엇이 있을까 싶기도 한데 일단은 그렇다고 한다. Timetamp의 경우는 상당히 익숙하다. DB에서 자주 사용하기 때문이다.
일단 먼저 생성자를 살펴보자.
생성
앞에서 살펴봤던 Date도 그렇고 년월일 등을 직접 지정해서 초기화하는 방식은 지양하는 쪽으로 바뀌고 있는 것 같다. 웬만해서는 long 형식을 이용해서 생성자를 호출하는 것이 좋겠다.
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
@Test
public void printTimestamp() {
System.out.println(timestamp);
}
Timestamp는 값을 자동으로 설정해주는 생성자가 없기 때문에 long 값을 이용해서 생성자를 호출해야한다. 위의 결과에서 밀리초까지만 출력된 이유는 초기 값을 시스템의 밀리초 값으로 주었기 때문이다.
Timestamp도 일정한 형식으로 출력돼서 나중에 날짜와 시간을 다룰 때 꽤 유용할 것 같다.
그럼 이제 날짜와 시간을 구별해서 출력해보자.
날짜 / 시간 추출
음... 뭔가가 이상하다..!

죄다 뭔가 줄이 그어져 있다! 그 이유가 무엇일까?
앞서서 Timestamp 클래스는 Date 클래스를 상속받아 만들었다고 했었다. 사실 저 메서드들은 Date 클래스에 정의되어있는 메서드이다. Date 클래스는 복잡한 날짜와 시간 계산을 하기에는 여러 기능을 제공하지 않고 직관적이지 않고, 윤년이나 서머타임 등의 요소를 고려하며 사용하기에는 어려움이 있기 때문에 되도록 사용하지 말라고 하는 것이다. 그리고 Calendar처럼 월이 0부터 시작하기 때문에 개발자 입장에서도 사용할 때 실수할 여지가 있다.
그렇기 때문에 굳이 각각 가져오고 싶다면 String으로 바꿔서 substring하는 방법 등을 이용해서 사용하는 것이 좋겠다.
이런 포맷팅 처리에 관해서는 이후에 나오는 SimpleDateFormat에서 자세히 알아 보고 일단 String으로 변환해보자.
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
@Test
public void printTimestamp() {
String strTimestamp = timestamp.toString();
System.out.println(strTimestamp);
}
사실 실행 결과로 봐서는 별 차이가 없다. toString()을 이용하면 yyyy-MM-dd HH:mm:ss.fffffffff 이 형식으로 변환 때문에 잘라서 사용하기에 귀찮은 것 빼고는 편하긴 하다.
이것과는 반대로 문자열을 Timestamp로 변경할 수 있는 방법이 있다.
Timestamp timestamp = Timestamp.valueOf("2023-12-25 16:20:30.123456789");
@Test
public void printTimestamp() {
System.out.println(timestamp);
}
Long.valueOf()처럼 Timestamp에도 해당 타입으로 변환시키는 valueOf() 메서드가 있다. 이 메서드를 이용해서 위에서 살펴봤던 포맷팅 형식인 문자열을 집어넣으면 Timestamp로 변환시킬 수 있다.
java.time 패키지의 내용으로 들어가기 전에 앞서 살펴보았던 클래스들과 이제부터 살펴볼 java.time 패키지 클래스들의 차이점이 있다. 만약 멀티스레드 환경에서 동시에 어떤 데이터에 접근하면 어떻게 될까? 변수에 담는 시간은 계속 바뀔 가능성이 있다. 그렇기 때문에 원하지 않은 결과가 발생할 수도 있다. 이제부터 살펴볼 java.time 패키지의 클래스들은 불변객체(Immutable Object)로 생성되기 때문에 객체 안의 내용을 변경할 수 없다. String을 생각해보면 쉽게 이해가 될 것이다. 문자열을 연결하는 것은 값을 변경하는 것이 아니라 새로 만들어서 재할당하는 것이다.
LocalDate
LocalDate로는 날짜를 표현할 수 있다. 년월일까지만 표시하며, 생성자 대신 now()라는 메서드로 현재 날짜를 지정할 수 있다.
생성
LocalDate localDate = LocalDate.now();
@Test
public void printLocalDate() {
System.out.println(localDate);
}
만약 오늘 날짜가 아니라 직접 날짜를 지정하고 싶다면 of() 메서드를 사용하면 된다.
LocalDate localDate = LocalDate.of(2023, 7, 12);
@Test
public void printLocalDate() {
System.out.println(localDate);
}
위 처럼 사용하면 된다.
LocalDate에서는 Date에서 Deprecated 되었던 그 메서드들과 비슷한 것들을 사용할 수 있다.
날짜 추출
LocalDate localDate = LocalDate.now();
@Test
public void printLocalDate() {
System.out.println("년: " + localDate.getYear());
System.out.println("월: " + localDate.getMonth());
System.out.println("일: " + localDate.getDayOfMonth());
}
이렇게 년월일을 잘 가져와서 출력했다.
참고로 era의 경우는 뭔지 잘몰랐는데 특정 연대를 표현하는 것이라고 한다. IsoEra.CE 서기 표시가 출력된다고 하는데 크게 신경은 안 써도 될 듯하다.
날짜를 비교하는 메서드들도 있지만 이는 ChronoLocalDate라는 인터페이스를 LocalDate에서 구현하여 사용하는 것이므로 매개변수로 ChronoLocalDate 타입 객체가 필요하다. 자세히 알고 싶으면 ChronoLocalDate까지 찾아보면 될 것 같다. 일단은 그냥 넘어가도록 하겠다...
날짜 연산은 편하게 할 수 있다.
수정
LocalDate localDate = LocalDate.now().plusDays(1);
@Test
public void printLocalDate() {
localDate = localDate.minusMonths(5);
System.out.println(localDate);
}
오늘 날짜에서 1일을 더하고 5달 전인 날짜를 출력해보았다. 메서드명이 상당히 직관적이라 사용하기 편하다.
주의할 점은 앞서 언급했던 것처럼 불변객체이기 때문에 객체 내의 값이 바뀌는 것이 아니라 재할당하는 것이다. 그렇기 때문에 반환받아 저장하는 과정이 있어야 제대로 출력된다. 위의 메서드의 반환 부분을 계속 따라가다 보면 마지막에 new LocalDate()로 새로운 객체를 생성하는 부분이 있다. 생성자를 사용할 수 있는 이유는 private으로 지정되어 있기 때문에 해당 클래스 내에서는 사용할 수 있는 것이다.
LocalDate도 마찬가지로 toString() 메서드로 문자열로 변환하는 것과 parse() 메서드로 문자열에서 LocalDate 타입으로 변환하는 메서드를 지원한다.
LocalDate localDate = LocalDate.now();
@Test
public void printLocalDate() {
String strLocalDate = LocalDate.now().toString();
System.out.println(strLocalDate);
LocalDate localDate2 = LocalDate.parse("2012-08-13");
System.out.println(localDate2);
}
주의해야할 점은 yyyy-MM-dd라는 포맷을 지켜서 사용해야 한다는 점이다.
LocalTime
LocalTime은 LocalDate와 크게 다르지 않다. 다른 점이라면 날짜 대신 시간을 다룬다는 점이다.
생성
LocalTime localTimeNow = LocalTime.now();
LocalTime localTimeOf = LocalTime.of(11, 46, 27);
@Test
public void printLocalTime() {
System.out.println(localTimeNow);
System.out.println(localTimeOf);
}
해당 클래스도 마찬가지로 now() 메서드로 현재 시간을, of() 메서드로 시분초를 지정하여 나타낼 수 있다.
기본적으로 나노초까지를 표현할 수 있으며, 시분초만 지정했을 때는 지정한 값만 출력된다.
값을 가져올 때는 다음과 같이 시분초와 나노초를 각각 가져올 수 있다.
시간 추출
LocalTime localTimeNow = LocalTime.now();
@Test
public void printLocalTime() {
System.out.println(localTimeNow);
System.out.println(localTimeNow.getHour());
System.out.println(localTimeNow.getNano());
}
다음은 시간 연산을 하는 방법을 알아보자.
수정
LocalTime localTimeNow = LocalTime.now();
@Test
public void printLocalTime() {
System.out.println(localTimeNow);
// 덧셈
System.out.println(localTimeNow.plusHours(2).plusMinutes(3).plusSeconds(4));
System.out.println(localTimeNow.plus(Duration.ofHours(2).plusMinutes(3).plusSeconds(4)));
// 뺄셈
System.out.println(localTimeNow.minusHours(2).minusMinutes(3).minusSeconds(4));
System.out.println(localTimeNow.minus(Duration.ofHours(2).plusMinutes(3).plusSeconds(4)));
}
plusHours(), minusHours() 등의 메서드를 사용하면 쉽게 시간을 더하고 뺄 수 있다. 메서드명도 직관적이라 사용하기 편하다. plus(), minus() 메서드는 파라미터로 TemporalAmount 인터페이스의 구현체를 받는데 그 구현체 중 하나인 Duration을 이용해서 지정하는 방법도 있다. 다만 해당 방법 사용시 뺄셈의 경우 Duration 초기값을 지정할 때 분과 초를 빼는 것이 아니라 더해주어야 한다는 점을 조심해야 한다.
LocalTime localTimeNow = LocalTime.now();
@Test
public void printLocalTime() {
System.out.println(localTimeNow);
System.out.println(localTimeNow.toString());
System.out.println(LocalTime.parse("12:22:45"));
}
LocalTime도 마찬가지로 문자열로 변환하거나 문자열로 된 시간을 LocalTime으로 변환할 수 있다. 포맷 기준은 ISO-8601 시간 표준에 맞춰서 사용하면 된다.
LocalDateTime
이름에서부터 짐작할 수 있듯이 위에서 살펴봤던 LocalDate와 LocalTime을 합친 것과 같은 그런 클래스이다. 년, 월, 일, 시, 분, 초, 나노초를 저장하여 사용할 수 있다.
생성
LocalDateTime localDateTime1 = LocalDateTime.now();
LocalDateTime localDateTime2 = LocalDateTime.of(2024, 1, 24, 16, 27);
@Test
public void myTest() {
System.out.println("now() : " + localDateTime1);
System.out.println("of() : " + localDateTime2);
}
해당 메서드 역시 현재 날짜&시간인 now() 메서드나 of() 메서드로 직접 날짜와 시간을 지정해서 사용할 수 있다.
설명을 보면 ISO-8601에 따라 날짜와 시간을 표시 한다고 한다. 중간에 T는 날짜와 시간을 구분하는 역할을 한다.
만약 T를 없애고 싶다면 따로 포맷팅 처리를 하면 된다.
날짜 / 시간 추출
LocalDateTime localDateTime = LocalDateTime.now();
@Test
public void myTest() {
int year = localDateTime.getYear(); // 년도
Month month = localDateTime.getMonth(); // 월
int day = localDateTime.getDayOfMonth(); // 일
int hour = localDateTime.getHour(); // 시
int minute = localDateTime.getMinute(); // 분
int second = localDateTime.getSecond(); // 초
int nano = localDateTime.getNano(); // 나노초
log.info("{}년 {}월 {}일 {}시 {}분 {}초 {}나노초", year, month.getValue(), day, hour, minute, second, nano);
}
각 요소를 가져오는 것도 가능하다. 이번에는 표시할 내용이 많아서 롬복을 이용했다. 그런데 의문인점이 있다. 월만 Month라는 것으로 반환된 것이다. 이는 getMonth()가 java.time.Month를 반환하기 때문이다. Month의 설명을 보면 아래와 같이 나와 있다.
int 값으로 월을 나타내기보다 보다 명확하게 월을 파악하기 위해 enum Month의 값을 사용하라고 한다. 위에서 1월이 0인 경우도 있고 해서 헷갈릴 여지가 있으니 그런 것 같다.
다음은 수정 하는 법을 알아보자.
수정
LocalDateTime localDateTime = LocalDateTime.now();
@Test
public void myTest() {
LocalDateTime modifiedDateTime = localDateTime.plusDays(5); // 5일 후
System.out.println(modifiedDateTime);
modifiedDateTime = localDateTime.minusHours(2); // 2시간 전
System.out.println(modifiedDateTime);
}
딱히 설명을 하지 않아도 무엇을 사용해야 할 지 알 것만 기분이 든다. 그냥 보이는 대로 사용하면 된다. 굉장히 직관적인 메서드명이다.
시간 / 날짜 비교
LocalDateTime localDateTime1 = LocalDateTime.now();
LocalDateTime localDateTime2 = LocalDateTime.of(2024, 1, 24, 13, 30);
@Test
public void myTest() {
System.out.println(localDateTime1.isBefore(localDateTime2));
System.out.println(localDateTime1.isEqual(localDateTime2));
System.out.println(localDateTime1.isAfter(localDateTime2));
}
시간/날짜가 어느것이 이전인지, 같은지, 이후인지 확인할 수 있는 메서드도 있다. boolean으로 결과를 반환받을 수 있다.
여러 시간과 날짜를 다루는 클래스들을 간단하게 살펴보았다. 어느 것을 사용해야 한다고 딱 집어서 말할수는 없지만 자신이 원하는 목적에 따라 적절하게 선택하여 사용하도록 하자.

- 끝 -
'공부 > Java' 카테고리의 다른 글
[Java] 람다식 (Lambda expression) (0) | 2024.02.23 |
---|---|
[Java] 생성자 (Constructor) (0) | 2024.02.22 |
[Java] JDBC (Java Database Connectivity) (0) | 2023.12.22 |
[Java] 예외처리(Exception handling) (0) | 2023.06.23 |
[Java] 컬렉션 프레임워크(Collections Framework) (0) | 2023.05.21 |