Search
Duplicate
📒

[Java Study] 05. 날짜와 시간

상태
완료
수업
Java Study
주제
Util
4 more properties
참고

자바의 날짜와 시간

NOTE
날짜와 시간을 계산하는 것은 단순하게 생각하면 쉬워보이지만, 실제로는 매우 어렵습니다.
날짜와 시간을 계산하는 것은 정말로 많은 요소를 고려해야 합니다 만약 2024년 1월 1일에서 2024년 3월 1일까지 몇일인지 계산하기 위해서는 아래의 요소들을 고려해야 합니다.
2024년이 윤년인가?
일괄 절약시간(DST)이 언제인가?
타임존은 어디인가?
자바에서는 이러한 날짜와 시간에 대한 라이브러리를 제공해왔고, java 1.8에 java.time 패키지를 공개하면서 사용성, 성능, 스레드 안전성, 타임존 처리 등에서 크게 개선된 API를 제공하고 있습니다.

LocalDate, LocalTime, LocalDateTime

NOTE
Local시리즈는 가장 많이 쓰이는 객체들이며 Local이 붙는 이유는 세계 시간대를 고려하지 않아서 타임존이 적용되지 않기 때문입니다.
// 생성 LocalDate now = LocalDate.now(); LocalDate localDate = LocalDate.of(2013, 11, 21); System.out.println("now = " + now); System.out.println("localDate = " + localDate); // 계산 LocalDate localDate1 = localDate.plusDays(10); System.out.println("localDate1 = " + localDate1);
Java
복사
LocalDate 예제
// 생성 LocalTime now = LocalTime.now(); LocalTime ofTime = LocalTime.of(9, 10, 30); System.out.println("now = " + now); System.out.println("ofTime = " + ofTime); // 계산 LocalTime localTime = ofTime.plusSeconds(30); System.out.println("localTime = " + localTime);
Java
복사
LocalTime 예제
public class LocalDateTime { private final LocalDate date; private final LocalTime time; // ... }
Java
복사
LocalDateTime = LocalDate + LocalTime
// 생성 LocalDateTime nowDt = LocalDateTime.now(); LocalDateTime ofDt = LocalDateTime.of(2016, 8, 16, 8, 10, 1); // LocalDateTime은 LocalDate + LocalTime으로 생성이 가능하다. LocalDate localDate = ofDt.toLocalDate(); LocalTime localTime = ofDt.toLocalTime(); LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); System.out.println("localDateTime = " + localDateTime); // 계산 LocalDateTime localDateTime1 = ofDt.plusDays(1000); LocalDateTime localDateTime2 = ofDt.plusYears(1); System.out.println("localDateTime1 = " + localDateTime1); System.out.println("localDateTime2 = " + localDateTime2); LocalDateTime futureDateTime = now.plusDays(1).plusHours(2); System.out.println("futureDateTime = " + futureDateTime); // 비교 System.out.println("시간 비교 " + nowDt.isAfter(ofDt)); System.out.println("시간 비교 " + nowDt.isBefore(ofDt)); System.out.println("시간 비교 " + nowDt.isEqual(ofDt)); // equals와 다르다. (단순 시간비교의 차이, equlas는 타임존도 비교해버림)
Java
복사
LocalDateTime 에제
비교에서 isEqual()는 비교 대상이 시간적으로 같다면 true를 반홥합니다. 하지만 equals() 비교는 객체의 타입, 타임존 등 내부 데이터의 모든 구성요소가 같아야 true를 반환합니다.

ZoneId, ZonedDateTime, OffsetDateTime

NOTE
LocalDateTime과 거의 유사하지만, “Asia/Seoul”과 같은 타임존에 대한 정보가 들어가 있습니다.
// 가능한 ZoneId를 모두 꺼내기 for (String availableZoneId : ZoneId.getAvailableZoneIds()) { System.out.println("availableZoneId = " + availableZoneId); ZoneId zoneId = ZoneId.of(availableZoneId); System.out.println("zoneId = " + zoneId.getRules()); } // 시스템 기본 ZoneId ZoneId zoneId = ZoneId.systemDefault(); System.out.println("zoneId = " + zoneId); // Asis/Seoul ZoneId seoulZoneId = ZoneId.of("Asia/Seoul"); System.out.println("seoulZoneId = " + seoulZoneId);
Java
복사
ZoneId는 타임존을 Enum으로 관리하는 객체입니다.
public class ZonedDateTime { private final LocalDateTime dateTime; private final ZoneOffset offset; private final ZoneId zone; }
Java
복사
ZonedDateTime 구조
// 생성 ZonedDateTime now = ZonedDateTime.now(); System.out.println("now = " + now); // LocalDateTime + ZoneId로도 생성이 가능합니다. LocalDateTime localDateTime = LocalDateTime.of(2030, 1, 1, 13, 30, 0); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("Asia/Seoul")); System.out.println("zonedDateTime = " + zonedDateTime); ZonedDateTime zonedDateTime1 = ZonedDateTime.of(2030, 1, 1, 13, 30, 50, 0, ZoneId.of("Asia/Seoul")); System.out.println("zonedDateTime1 = " + zonedDateTime1); // 타임존 변경 ZonedDateTime utc = zonedDateTime1.withZoneSameInstant(ZoneId.of("UTC")); System.out.println("utc = " + utc);
Java
복사
ZonedDateTime 예제
ZonedDateTime은 구체적인 지역 시간대를 다룰 때 사용하며, 일광 절약 시간을 자동으로 처리할 수 있습니다.
사용자 지정 시간대에 따른 시간 계산이 필요할 때 적합합니다.
public class OffsetDateTime { private final LocalDateTime dateTime; private final ZoneOffset offset; }
Java
복사
OffsetDateTime 구조
// 생성 OffsetDateTime nowOdt = OffsetDateTime.now(); System.out.println("nowOdt = " + nowOdt); LocalDateTime localDateTime = LocalDateTime.of(2030, 1, 1, 13, 30, 50); System.out.println("localDateTime = " + localDateTime); // LocalDateTime + ZoneOffset으로 생성이 가능합니다. OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, ZoneOffset.of("+01:00")); System.out.println("offsetDateTime = " + offsetDateTime);
Java
복사
OffsetDateTime 예제
OffsetDateTime은 UTC와의 시간 차이만을 나타낼 때 사용하며, 지역 시간대의 복잡성을 고려하지 않습니다.
시간대 변환 없이 로그를 기록하고, 데이터를 저장하고 처리할 때 적합합니다.

Instant

NOTE
Instant는 날짜와 시간을 나노초 정밀도로 표현하며, 1970년 1월 1일 0시 0분 0초 기준으로 경과한 시간을 초단위로 표현합니다.
// 생성 Instant now = Instant.now(); System.out.println("now = " + now); // UTC 기준 // ZonedDateTime => Instant ZonedDateTime zonedDateTime = ZonedDateTime.now(); Instant from = Instant.from(zonedDateTime); System.out.println("from = " + from); // 시간으로 생성 Instant epochSecond = Instant.ofEpochSecond(10); System.out.println("instant = " + epochSecond); // 계산 Instant instant = epochSecond.plusSeconds(3600); System.out.println("instant = " + instant); // 조회 long epochSecond1 = instant.getEpochSecond(); System.out.println("epochSecond1 = " + epochSecond1);
Java
복사
Instant 예제
Instant는 UTC를 기준으로 하므로, 전 세계에서 일관된 시점을 표현할 때 사용하기 좋습니다.
에포크 시간으로부터 흐른 시간을 초 단위로 저장할 수 있습니다.

Perioid, Duration

NOTE
시간의 개념은 특정 시점의 시간, 시간의 간격 2가지로 나뉘며 Period, Duration는 시간의 간격을 표현하는데 사용됩니다.
// 생성 Period period = Period.ofDays(10); System.out.println("period = " + period); // 계산에 사용 LocalDate localDate = LocalDate.of(2030, 1, 1); LocalDate plus = localDate.plus(period); System.out.println("localDate = " + localDate); System.out.println("plus = " + plus); // 간격 LocalDate localDate1 = LocalDate.of(2023, 1, 1); LocalDate localDate2 = LocalDate.of(2024, 1, 2); Period between = Period.between(localDate1, localDate2); System.out.println("between = " + between); System.out.println("기간: " + between.getMonths() + "개월 " + between.getDays() + "일");
Java
복사
Period 예제
Period는 날짜 단위에 주로 사용됩니다.
ex) 년, 월, 일
// 생성 Duration duration = Duration.ofMinutes(30); System.out.println("duration = " + duration); // 계산 단위로 사용 LocalTime lt = LocalTime.of(1, 0); LocalTime plus = lt.plus(duration); System.out.println("plus = " + plus); // 간격 LocalTime start = LocalTime.of(9, 0); LocalTime end = LocalTime.of(10, 0); Duration between = Duration.between(start, end); System.out.println("between = " + between.getSeconds()); System.out.println("근무 시간: " + between.toHours() + "시간" + between.toMinutesPart() + "분");
Java
복사
Duration 예제
Durition은 시간 단위에 주로 사용됩니다.
ex) 시간, 분, 초, 나노초

날짜와 시간의 핵심 인터페이스

NOTE
앞에서 시간의 개념은 특정 시점의 시간, 시간의 간격 2가지로 나뉜다고 언급했습니다. 이는 해당 개념을 가진 인터페이스를 구현함으로서 역할이 구분될 수 있습니다.
특정 시점의 시간을 표현하는 객체는 Temporal를 구현합니다.
LocalDateTime , LocalDate, LocalTime, ZonedDateTime, OffsetDateTime, Instant
시간의 간격을 표현하는 객체는 TemporalAmount를 구현합니다.
ex) Period, Duration

시간의 단위와 시간 필드

LocalDateTime dt = LocalDateTime.of(2018, 1, 1, 13, 30, 59); System.out.println("dt = " + dt); // dt = 2018-01-01T13:30:59 // ChronoUnit을 통한 날짜계산 LocalDateTime plus1 = dt.plus(10, ChronoUnit.YEARS); System.out.println("plus = " + plus1); // plus = 2028-01-01T13:30:59(10년 추가) // 편의 메서드 제공 LocalDateTime plus2 = dt.plusYears(10); System.out.println("plus2 = " + plus2); // plus2 = 2028-01-01T13:30:59(10년 추가) // Period 사용 Period period = Period.ofYears(10); LocalDateTime plus3 = dt.plus(period); System.out.println("plus3 = " + plus3); // plus3 = 2028-01-01T13:30:59
Java
복사
ChronoUnit 예제
ChronoUnit: 다양한 시간 단위(SECONDS, HOURS, DAYS ..)
LocalDateTime dt = LocalDateTime.of(2030, 1, 1, 13, 30, 59); // ChronoFiled를 통한 값 출력 System.out.println("YEAR = " + dt.get(ChronoField.YEAR)); System.out.println("MONTH_OF_YEAR = " + dt.get(ChronoField.MONTH_OF_YEAR)); System.out.println("DAY_OF_MONTH = " + dt.get(ChronoField.DAY_OF_MONTH)); System.out.println("HOUR_OF_DAY = " + dt.get(ChronoField.HOUR_OF_DAY)); System.out.println("MINUTE_OF_HOUR = " + dt.get(ChronoField.MINUTE_OF_HOUR)); System.out.println("SECOND_OF_MINUTE = " + dt.get(ChronoField.SECOND_OF_MINUTE)); // 편의 메서드 제공 System.out.println("YEAR = " + dt.getYear()); System.out.println("MONTH_OF_YEAR = " + dt.getMonthValue()); System.out.println("DAY_OF_MONTH = " + dt.getDayOfMonth()); System.out.println("HOUR_OF_DAY = " + dt.getHour()); System.out.println("MINUTE_OF_HOUR = " + dt.getMinute()); System.out.println("SECOND_OF_MINUTE = " + dt.getSecond()); // 편의 메서드가 없는 필드 System.out.println("MINUTE_OF_DAY = " + dt.get(ChronoField.MINUTE_OF_DAY)); System.out.println("SECOND_OF_DAY = " + dt.get(ChronoField.SECOND_OF_DAY));
Java
복사
ChoronoField 예제
ChronoField: 날짜 및 시간을 나타내는 데 사용되는 열거형이다.
LocalDate now = LocalDate.now(); // 해당 Unit이 허용된 객체에서만 동작하게 하자. if (now.isSupported(ChronoField.SECOND_OF_MINUTE)) { int i = now.get(ChronoField.SECOND_OF_MINUTE); System.out.println("i = " + i); }
Java
복사
TemporalField, TemporalUnit를 사용할 때 지원하지 않는 타입이 존재할 수 있다.
ex) LocalData에서 분을 조회하는 경우.
이런 문제를 예방하기 위해 isSupported()를 제공해서 지원여부를 확인할 수 있다.

with, TemporalAdjusters(시간조정)

NOTE
with()는 날짜와 시간의 필드 값을 변경할 수 있으며, TemporalAdjusterTemporal 객체를 조정하는데 사용되어 복잡한 날짜계산을 편리하게 해줍니다.
public static void main(String[] args) { LocalDate today = LocalDate.now(); // 날짜의 연도 변경: 2024년으로 변경 LocalDate changedYear = today.with(ChronoField.YEAR, 2024); LocalDate changedYear2 = today.withYear(2024); // 날짜의 월 변경: 8월로 변경 LocalDate changedMonth = today.with(ChronoField.MONTH_OF_YEAR, 8); LocalDate changedMonth2 = today.withMonth(Month.AUGUST.getValue()); // 날짜의 일 변경: 15일로 변경 LocalDate changedDay = today.withDayOfMonth(15); LocalDate changedDay2 = today.with(ChronoField.DAY_OF_MONTH, 15); // 특정 날짜를 위한 커스텀 조정자 구현 LocalDate specificDate = today.with((date) -> date.plusDays(10)); System.out.println("오늘 날짜: " + today); System.out.println("연도 변경: " + changedYear); System.out.println("월 변경: " + changedMonth); System.out.println("일 변경: " + changedDay); System.out.println("10일 추가: " + specificDate); }
Java
복사
with 예제
public static void main(String[] args) { LocalDate today = LocalDate.now(); // 이번 달의 첫째 날로 이동 LocalDate firstDay = today.with(TemporalAdjusters.firstDayOfMonth()); // 이번 달의 마지막 날로 이동 LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth()); // 다음 화요일로 이동 LocalDate nextTuesday = today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)); System.out.println("오늘 날짜: " + today); System.out.println("이번 달의 첫째 날: " + firstDay); System.out.println("이번 달의 마지막 날: " + lastDay); System.out.println("다음 화요일: " + nextTuesday); }
Java
복사
TemporalAdjusters 예제

날짜와 시간 문자열 파싱과 포맷팅

NOTE
날짜와 시간 문자열의 파싱과 포맷팅은 DateTimeFormatters를 사용하여 간편하게 할 수 있습니다. 이 클래스를 통해 날짜와 시간의 형식을 정의하고, 날짜와 시간 사이의 변환을 수행할 수 있습니다.
// 포매팅(Date → String) LocalDate localDate = LocalDate.of(2024, 12, 31); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일"); String format = date.format(formatter); System.out.println("format = " + format); // 2024년 12월 31일 System.out.println("localDate = " + localDate); // 2024-12-31 // 파싱(String -> Date) String input = "2030년 01월 01일"; LocalDateTime parseDateTime = LocalDateTime.parse(dateTimeString, formatter); System.out.println("parseDateTime = " + parseDateTime); // parseDate = 2030-01-01
Java
복사
format, parse 예제 1
// 포매팅(Date → String) LocalDateTime now = LocalDateTime.of(2024, 12, 31, 13, 30, 59); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String format = now.format(formatter); System.out.println("format = " + format); System.out.println("now = " + now); // 파싱(String -> Date) String dateTimeString = "2023-01-01 11:30:00"; LocalDateTime parseDateTime = LocalDateTime.parse(dateTimeString, formatter); System.out.println("parseDateTime = " + parseDateTime);
Java
복사
format, parse 예제 2