(참고) Java의 날짜와 시간 라이브러리 역사
위의 글들을 먼저 보고 개념을 잡도록 하자!
시간과 날짜 계산은 상상하는 것만큼 훨씬 이상으로 복잡하다. 현대 개발 환경에서는 날짜와 시간을 처리하기 위해 잘 설계된 라이브러리를 적극적으로 사용해야 한다. 이러한 라이브러리는 위에서 언급한 복잡한 계산을 추상화하여 제공하므로, 개발자는 보다 안정적이고 정확하며 효율적인 코드를 작성할 수 있다.
[국내 서비스만 고려 시 사용되는 클래스]
- LocalDate: 날짜만 표현할 때 사용
- LocalTime: 시간만 표현할 때 사용
(ms, ns 표현 가능)
- LocalDateTime: 날짜 + 시간
// LocalDateTime 클래스
public class LocalDateTime {
private final LocalDate date;
private final LocalTime time;
...
}
package time.local;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class LocalMain {
public static void main(String[] args) {
// LocalDate 클래스
LocalDate nowDate = LocalDate.now();
LocalDate ofDate = LocalDate.of(2021, 7, 25);
System.out.println("오늘 날짜 = " + nowDate);
System.out.println("지정 날짜 = " + ofDate);
// 계산(불변 주의!)
ofDate = ofDate.plusDays(10);
System.out.println("지정 날짜 + 10d = " + ofDate);
System.out.println();
// LocalTime 클래스
LocalTime nowTime = LocalTime.now();
LocalTime ofTime = LocalTime.of(9, 10, 30);
System.out.println("현재 시간 = " + nowTime);
System.out.println("지정 시간 = " + ofTime);
// 계산(불변 주의!)
LocalTime ofTimePlus = ofTime.plusSeconds(31);
System.out.println("지정 시간 + 30s = " + ofTimePlus);
System.out.println();
// LocalDateTime 클래스
LocalDateTime nowDt = LocalDateTime.now();
LocalDateTime ofDt = LocalDateTime.of(2020, 1, 13, 13, 00, 00);
System.out.println("현재 날짜시간 = " + nowDt);
System.out.println("지정 날짜시간 = " + ofDt);
// 날짜와 시간 분리
LocalDate localDate = ofDt.toLocalDate();
LocalTime localTime = ofDt.toLocalTime();
System.out.println("localDate = " + localDate);
System.out.println("localTime = " + localTime);
// 날짜와 시간 합체
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
System.out.println("localDateTime = " + localDateTime);
// 계산(불변 주의!)
LocalDateTime ofDtPlus = ofDt.plusDays(1000);
System.out.println("지정 날짜시간 + 1000d = " + ofDtPlus);
LocalDateTime ofDtPlus1Year = ofDt.plusYears(1);
System.out.println("지정 날짜시간 + 1y = " + ofDtPlus1Year);
// 비교
System.out.println("현재 날짜시간이 지정 날짜시간보다 이전인가? " + nowDt.isBefore(ofDt));
System.out.println("현재 날짜시간이 지정 날짜시간보다 이후인가? " + nowDt.isAfter(ofDt));
System.out.println("현재 날짜시간이 지정 날짜시간이 같은가? " + nowDt.isEqual(ofDt));
}
}
(DayOfWeek 와 같이 월, 화, 수, 목, 금, 토, 일을 나타내는 클래스도 있다)
**isEqual() vs equals()**
- isEqual(): 단순히 시간적으로 같으면 true를 반환
e.g. 서울의 9시와 UTC의 0시는 시간적으로 같다. 이 둘을 비교하면 true를 반환한다.
- equals(): 객체의 타입, 타임존 등등 내부 데이터의 모든 구성요소가 같아야 true를 반환
e.g. 서울의 9시와 UTC의 0시는 시간적으로 같다. 하지만 이 둘은 타임존의 데이터가 다르기 때문에 false를 반환한다.
[해외 서비스도 고려시 사용되는 클래스]
- ZonedDateTime: 시간대를 고려한 날짜와 시간을 표현할 때 사용 (시간대를 표현하는 타임존이 포함, 그리고 그 타임존에 맞는 오프셋 포함)
타임존이 있기 때문에 그 지역에 대한 썸머 타임 적용 o
// ZonedDateTime 클래스
public class ZonedDateTime {
private final LocalDateTime dateTime;
private final ZoneOffset offset;
private final ZoneId zone;
}
- OffsetDateTime: 시간대를 고려한 날짜와 시간을 표현할 때 사용 (타임존이 없고, UTC로부터의 시간대 차이인 고정 오프셋이 포함)
타임존이 없기 때문에 그 지역에 대한 썸머 타임 적용 x
// OffsetDateTime 클래스
public class OffsetDateTime {
private final LocalDateTime dateTime;
private final ZoneOffset offset;
}
package time.global;
import java.time.ZoneId;
// 자바는 타임존을 ZoneId 클래스로 제공한다
public class ZoneIdMain {
public static void main(String[] args) {
for (String availableZoneId : ZoneId.getAvailableZoneIds()) {
ZoneId zoneId = ZoneId.of(availableZoneId);
System.out.println(zoneId + " | " + zoneId.getRules());
}
System.out.println();
System.out.println("ZoneId.systemDefault = " + ZoneId.systemDefault()); // 시스템이 사용하는 기본 ZoneId를 반환
System.out.println("seoulZoneId = " + ZoneId.of("Asia/Seoul"));
}
}
.
.
.
package time.global;
import java.time.*;
public class DateTimeMain {
public static void main(String[] args) {
// ZonedDateTime 클래스
ZonedDateTime nowZdt = ZonedDateTime.now();
System.out.println("nowZdt = " + nowZdt);
LocalDateTime ldt = LocalDateTime.of(2030, 1, 1, 13, 30, 50);
ZonedDateTime zdt1 = ZonedDateTime.of(ldt, ZoneId.of("Asia/Seoul"));
System.out.println("zdt1 = " + zdt1);
ZonedDateTime zdt2 = ZonedDateTime.of(2030, 1, 1, 13, 30, 50, 0, ZoneId.of("Asia/Seoul"));
System.out.println("zdt2 = " + zdt2);
// 타임존과 그에 맞는 시간 변경
ZonedDateTime utcZdt = zdt2.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println("utcZdt = " + utcZdt);
System.out.println();
// OffsetDateTime 클래스
OffsetDateTime nowOdt = OffsetDateTime.now();
System.out.println("nowOdt = " + nowOdt);
ldt = LocalDateTime.of(2030, 1, 1, 13, 30, 50);
System.out.println("ldt = " + ldt);
OffsetDateTime odt = OffsetDateTime.of(ldt, ZoneOffset.of("+01:00"));
System.out.println("odt = " + odt);
}
}
[기계 중심의 시간]
- Instant: 날짜와 시간을 나노초 정밀도로 표현하며, 1970년 1월 1일 0시 0분 0초(UTC 기준)를 기준으로 경과한 시간이 계산된다.
쉽게 이야기해서 Instant 내부에는 초 데이터만 들어있다 (나노초 포함)
"전 세계 모든 서버 시간을 똑같이 맞출 수 있다" 항상 UTC 기준이므로 한국에 있는 Instant, 미국에 있는 Instant의 시간이 똑같다.
시간대의 변화 없이 순수하게 시간의 흐름(예: 지속 시간 계산)만을 다루고 싶을 때 Instant가 적합하다. 이는 시간대 변환의 복잡성 없이 시간 계산을 할 수 있게 해준다. 또는 데이터베이스에 날짜와 시간 정보를 저장하거나, 다른 시스템과 날짜와 시간 정보를 교환할 때 Instant를 사용하면, 모든 시스템에서 동일한 기준점(UTC)을 사용하게 되므로 데이터의 일관성을 유지하기가 쉽다.
활용 예: 서버 로그 기록, 트랜잭션 타임스탬프, 서버 간의 시간 동기화, epoch 시간 기반 계산이 필요할 때, 간단히 두 시간의 차이를 구할 때 등
// Instant 클래스
public class Instant {
private final long seconds;
private final int nanos;
...
}
package time;
import java.time.Instant;
import java.time.ZonedDateTime;
public class InstantMain {
public static void main(String[] args) {
// 생성
Instant now = Instant.now(); // UTC 기준
System.out.println("now = " + now);
ZonedDateTime zdt = ZonedDateTime.now();
Instant from = Instant.from(zdt);
System.out.println("from = " + from);
Instant epochStart = Instant.ofEpochSecond(0);
System.out.println("epochStart = " + epochStart);
// 계산
Instant later = epochStart.plusSeconds(3600);
System.out.println("later = " + later);
// 조회
long laterEpochSecond = later.getEpochSecond();
System.out.println("laterEpochSecond = " + laterEpochSecond);
}
}
(참고) Epoch 시간
Epoch time(에포크 시간) 또는 Unix timestamp는 컴퓨터 시스템에서 시간을 나타내는 방법 중 하나이다. 이는 1970년 1월 1일 00:00:00 UTC부터 현재까지 경과된 시간을 초 단위로 표현한 것이다. 즉, Unix 시간은 1970년 1월 1일 이후로 경과한 전체 초의 수로, 시간대에 영향을 받지 않는 절대적인 시간 표현 방식이다.
참고로 Epoch 라는 뜻은 어떤 중요한 사건이 발생한 시점을 기준으로 삼는 시작점을 뜻하는 용어이다.
Instant는 바로 이 Epoch 시간을 다루는 클래스이다.
[시간의 양 2가지 개념]
- Period: "특정 시점의 시간"
두 날짜 사이의 간격을 년, 월, 일 단위로 나타낸다
// Period 클래스
public class Period {
private final int years;
private final int months;
private final int days;
}
package time;
import java.time.LocalDate;
import java.time.Period;
public class PeriodMain {
public static void main(String[] args) {
// 생성
Period period = Period.ofDays(10);
System.out.println("period = " + period);
// 계산에 사용
LocalDate currentDate = LocalDate.of(2030, 1, 1);
LocalDate plusDate = currentDate.plus(period);
System.out.println("현재 날짜: " + currentDate);
System.out.println("더한 날짜: " + plusDate);
// 기간 차이
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2023, 4, 2);
Period between = Period.between(startDate, endDate);
System.out.println("기간: " + between.getMonths() + "개월 " + between.getDays() + "일");
}
}
- Duration: "시간의 간격"
두 시간 사이의 간격을 시, 분, 초(나노초) 단위로 나타낸다
// Duration 클래스
public class Duration {
private final long seconds;
private final int nanos;
}
package time;
import java.time.Duration;
import java.time.LocalTime;
public class DurationMain {
public static void main(String[] args) {
// 생성
Duration duration = Duration.ofMinutes(30);
System.out.println("duration = " + duration);
LocalTime lt = LocalTime.of(1, 0);
System.out.println("기준 시간 = " + lt);
// 계산에 사용
LocalTime plusTime = lt.plus(duration);
System.out.println("더한 시간 = " + plusTime);
// 시간 차이
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(10, 0);
Duration between = Duration.between(start, end);
System.out.println("차이: " + between.getSeconds() + "초");
System.out.println("근무 시간: " + between.toHours() + "시간 " + between.toMinutesPart() + "분");
}
}
<날짜와 시간의 핵심 인터페이스1>
<날짜와 시간의 핵심 인터페이스2>
참고 및 출처: 김영한의 실전 자바 - 중급 1편
'Language > Java' 카테고리의 다른 글
[Java API] 예외계층구조 핵심 그림 (0) | 2024.09.16 |
---|---|
[Java] 중첩 클래스에 대한 고찰 (0) | 2024.09.09 |
[Java API] enum 주요 메서드 정리 (0) | 2024.08.24 |
[Java API] Math 클래스, Random 클래스 (0) | 2024.08.23 |
[Java API] System 클래스 (0) | 2024.08.23 |