1. 날짜와 시간 더하기/빼기
프로그래밍에서 날짜와 시간을 다루는 일은 단순히 “화면에 날짜를 예쁘게 출력”하는 것만이 아닙니다. 매우 자주 이런 작업이 필요합니다:
- 중요한 이벤트까지 며칠이 남았는지 확인하기;
- 사용자의 나이를 계산하기;
- 두 이벤트 사이에 얼마나 시간이 흘렀는지 구하기;
- 마감일이 이미 지났는지 확인하기;
- 특정 일수, 주, 개월 또는 시간을 날짜에 더하기.
예를 들어, 우리 앱에서 작업 마감까지 3일 남았다고 사용자에게 알림을 보내고 싶을 수 있습니다. 또는 생일이 이미 지났다면 축하 메시지를 보내고 싶을 수 있습니다.
이를 위해 java.time에는 이런 계산을 간단하고 안전하게 만들어 주는 전용 클래스와 메서드가 있습니다.
덧셈 및 뺄셈을 위한 기본 메서드
다음의 날짜/시간 클래스들(LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant)에는 공통적으로 다음 메서드가 있습니다:
- plusXxx() — 더하기(일, 월, 년, 시, 분 등)
- minusXxx() — 빼기(동일)
import java.time.LocalDate;
LocalDate today = LocalDate.now(); // 오늘 날짜
LocalDate tomorrow = today.plusDays(1); // 내일
LocalDate nextMonth = today.plusMonths(1); // 한 달 후
LocalDate lastWeek = today.minusWeeks(1); // 일주일 전
System.out.println("오늘: " + today);
System.out.println("내일: " + tomorrow);
System.out.println("한 달 후: " + nextMonth);
System.out.println("일주일 전: " + lastWeek);
이 메서드들은 모두 원본을 변경하지 않고 새로운 객체를 반환합니다. 불변성은 매우 중요합니다!
import java.time.LocalTime;
LocalTime now = LocalTime.now();
LocalTime inTwoHours = now.plusHours(2);
LocalTime tenMinutesAgo = now.minusMinutes(10);
System.out.println("지금: " + now);
System.out.println("2시간 후: " + inTwoHours);
System.out.println("10분 전: " + tenMinutesAgo);
조합 메서드
LocalDate vacation = today.plusMonths(2).plusDays(10);
System.out.println("휴가: " + vacation);
2. 날짜 간 차이 구하기: Period와 Duration
두 날짜나 시점 사이에 얼마나 시간이 지났는지 알아야 할 때 두 주인공이 등장합니다 — Period와 Duration. 자세히 살펴보겠습니다.
Period와 Duration의 차이점은?
- Period — 날짜 단위(년, 월, 일)에 사용. 예: “3년 2개월 5일”.
- Duration — 시간 단위(시, 분, 초, 나노초)에 사용. 예: “5시간 30분”.
| 클래스 | 용도 | 사용 예 |
|---|---|---|
|
날짜 간 차이 | 두 날짜 사이의 년/개월/일 |
|
시점 간 차이 | 두 사건 사이의 시간/분/초 |
예: 사용자 나이 계산
import java.time.LocalDate;
import java.time.Period;
LocalDate birthday = LocalDate.of(2000, 1, 15); // 생일
LocalDate today = LocalDate.now();
Period age = Period.between(birthday, today);
System.out.println("나이: " + age.getYears() + "년, " +
age.getMonths() + "개월, " +
age.getDays() + "일");
비유하자면: Period는 두 날짜 사이의 “사람이 이해하기 쉬운” 차이입니다(“나는 24년 5개월 12일이다” 같은 표현).
예: 마감까지 남은 시간
import java.time.LocalDate;
LocalDate deadline = LocalDate.of(2025, 7, 1);
LocalDate today = LocalDate.now();
if (today.isBefore(deadline)) {
Period left = Period.between(today, deadline);
System.out.println("마감까지 남은 기간: " +
left.getMonths() + "개월과 " +
left.getDays() + "일");
} else {
System.out.println("마감이 이미 지났습니다!");
}
Duration: 시간 차이
import java.time.LocalDateTime;
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);
Duration duration = Duration.between(start, end);
System.out.println("지속 시간: " + duration.toHours() + "시간 " +
(duration.toMinutes() % 60) + "분");
Duration은 차이를 초, 분, 시, 일 단위로 측정합니다(단, ‘일’은 항상 24시간으로 간주하며 달력 특성은 고려하지 않습니다).
비교: Period는 언제, Duration은 언제?
- 날짜만 있다면(LocalDate) — Period를 사용하세요.
- 시간 정보가 있다면(예: LocalDateTime, Instant) — Duration을 사용하세요.
3. 날짜와 시간 비교: isBefore, isAfter, equals
가끔은 단지 이렇게 알고 싶을 때가 있습니다: 날짜가 이미 지났는가, 아직 아닌가? 사건이 과거인가 미래인가? 이를 위해 모든 날짜/시간 클래스에는 다음 메서드들이 있습니다:
- isBefore(otherDate)
- isAfter(otherDate)
- isEqual(otherDate) (또는 equals)
import java.time.LocalDate;
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);
if (today.isBefore(deadline)) {
System.out.println("아직 시간이 있습니다!");
} else if (today.isEqual(deadline)) {
System.out.println("오늘이 마감일입니다!");
} else {
System.out.println("마감이 이미 지났습니다 :(");
}
import java.time.LocalTime;
LocalTime now = LocalTime.now();
LocalTime lunch = LocalTime.of(13, 0);
if (now.isAfter(lunch)) {
System.out.println("점심시간이 이미 지났습니다!");
} else {
System.out.println("점심까지 아직 시간이 있습니다.");
}
중요: 같은 타입의 객체끼리만 비교할 수 있습니다(LocalDate는 LocalDate와, LocalDateTime은 LocalDateTime과 비교).
4. 실습: 차이 계산과 마감 확인
예: 작업 마감까지 며칠 남았는지
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2025, 7, 1);
long daysLeft = ChronoUnit.DAYS.between(today, deadline);
if (daysLeft > 0) {
System.out.println("마감까지 " + daysLeft + "일 남았습니다");
} else if (daysLeft == 0) {
System.out.println("오늘이 마감일입니다!");
} else {
System.out.println("마감이 " + Math.abs(daysLeft) + "일 전에 지났습니다");
}
ChronoUnit.DAYS.between()은 두 날짜 사이의 정확한 ‘일’ 수를 구하는 편리한 방법입니다(월/년 단위 계산 없이).
예: 두 이벤트 사이의 초 수
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 1, 15, 30, 0);
long seconds = ChronoUnit.SECONDS.between(start, end);
System.out.println("두 이벤트 사이에 " + seconds + "초가 지났습니다");
예: 작업의 경과 기간 계산
import java.time.LocalDate;
import java.time.Period;
LocalDate created = LocalDate.of(2025, 5, 20);
LocalDate today = LocalDate.now();
Period period = Period.between(created, today);
System.out.println("작업이 생성된 지 " +
period.getDays() + "일, " +
period.getMonths() + "개월 및 " +
period.getYears() + "년 지났습니다.");
예: 두 Instant 사이의 지속 시간
import java.time.Instant;
import java.time.Duration;
Instant start = Instant.now();
// ... 무언가를 수행 ...
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("실행에는 " + duration.toMillis() + "밀리초가 걸렸습니다");
5. Period와 Duration 사용 시 유의점
Period의 특징
- Period는 달력 기준으로 계산합니다: 2월 28일과 3월 1일 사이는 서로 다른 달이라도 1일입니다.
- 년, 월, 일을 개별적으로 얻을 수 있습니다: getYears(), getMonths(), getDays().
- 두 날짜 사이의 ‘총 일수’가 필요하다면 ChronoUnit.DAYS.between()을 사용하세요.
Duration의 특징
- Duration은 정확한 시간 단위(시, 분, 초)를 다룹니다.
- LocalTime, LocalDateTime, Instant와 함께 사용할 수 있습니다.
- 시간이 없는 날짜(LocalDate)에는 사용할 수 없습니다 — 예외가 발생합니다.
예: Period와 Duration의 차이
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.Period;
LocalDate date1 = LocalDate.of(2025, 2, 28);
LocalDate date2 = LocalDate.of(2025, 3, 1);
Period period = Period.between(date1, date2); // 2일(윤년)
long days = java.time.temporal.ChronoUnit.DAYS.between(date1, date2); // 2일
System.out.println("Period: " + period.getDays() + " 일");
System.out.println("ChronoUnit: " + days + " 일");
LocalDateTime dt1 = LocalDateTime.of(2025, 6, 1, 23, 0);
LocalDateTime dt2 = LocalDateTime.of(2025, 6, 2, 1, 0);
Duration duration = Duration.between(dt1, dt2); // 2시간
System.out.println("Duration: " + duration.toHours() + " 시간");
6. Duration과 Period로 읽기 쉬운 형태로 출력하기
사용자에게 날짜/시간 차이를 보여줄 때 종종 “123456초”처럼 보이는 값은 불편합니다. 보통 “2일 3시간 15분”처럼 표시하길 원합니다.
예: 날짜와 시간 차이 출력
import java.time.LocalDateTime;
import java.time.Duration;
LocalDateTime start = LocalDateTime.of(2025, 6, 1, 14, 0);
LocalDateTime end = LocalDateTime.of(2025, 6, 3, 16, 30);
Duration duration = Duration.between(start, end);
long days = duration.toDays();
long hours = duration.minusDays(days).toHours();
long minutes = duration.minusDays(days).minusHours(hours).toMinutes();
System.out.println("차이: " + days + "일 " +
hours + "시간 " + minutes + "분");
7. ZonedDateTime과 Instant를 사용할 때의 특징
- 서로 다른 타임존의 ZonedDateTime 두 개를 비교하면 각 로컬 시간의 “체감 시간”이 아니라 절대 시간 기준으로 차이가 계산됩니다.
- 서머타임(일광 절약 시간제) 전환은 날짜 사이의 시간 차이에 영향을 줄 수 있습니다.
예: ZonedDateTime 간 차이
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.Duration;
ZonedDateTime MinskTime = ZonedDateTime.of(2025, 6, 1, 12, 0, 0, 0, ZoneId.of("Europe/Minsk"));
ZonedDateTime nyTime = ZonedDateTime.of(2025, 6, 1, 5, 0, 0, 0, ZoneId.of("America/New_York"));
Duration diff = Duration.between(nyTime, MinskTime);
System.out.println("차이: " + diff.toHours() + " 시간");
8. 날짜/시간 계산에서 흔한 실수
오류 №1: Period와 Duration을 혼동.
Duration을 LocalDate에 사용하려 하면 예외가 발생합니다. 날짜에는 Period, 시간에는 Duration을 사용하세요.
오류 №2: 시간대를 고려하지 않음.
서로 다른 시간대의 날짜/시간을 비교하면 예상치 못한 결과가 나올 수 있습니다. 하나의 시간대로 통일하거나 Instant를 사용하는 것이 좋습니다.
오류 №3: plus/minus 메서드가 객체를 변경한다고 기대.
java.time의 모든 객체는 불변입니다! 결과를 새로운 변수에 할당하는 것을 잊지 마세요(예: today = today.plusDays(1)).
오류 №4: Period의 getDays()가 총 일수라고 착각.
실제로 이는 년과 월을 뺀 “나머지 일수”만 의미합니다. 총 일수는 ChronoUnit.DAYS.between()을 사용하세요.
오류 №5: 서로 다른 타입의 객체를 비교.
isBefore, isAfter, isEqual 메서드는 동일한 타입의 객체에 대해서만 동작합니다(예: LocalDate와 LocalDate).
GO TO FULL VERSION