'김영한의 실전 자바 - 중급 1편' 강의를 들으면서 복습할만한 내용을 정리하였다.

6. 날짜와 시간

6.1 날짜와 시간 라이브러리가 필요한 이유

  1. 날짜와 시간 차이 계산

  2. 윤년 계산

  3. 일광 절약 시간 변환

  4. 타임존 계산

6.2 자바 날짜와 시간 라이브러리

원문

LocalDate, LocalTime, LocalDateTime

  • LocalDate : 날짜만 표현할 때 사용한다.

  • LocalTime : 시간만을 표현할 때 사용한다.

  • LocalDateTime : LocalDate와 LocalTime을 합한 개념이다.

ZonedDateTime, OffsetDateTime

  • ZonedDateTime : 타임존을 포함한 날짜와 시간을 표현할 때 사용한다.
    • 예) 2013-11-21T08:20:30.213+9:00[Asia/Seoul]
    • Asia/Seoul 은 타임존이라 한다. 이 타임존을 알면 오프셋과 일광 절약 시간제에 대한 정보를 알 수 있다.
  • OffsetDateTime : 시간대를 고려한 날짜와 시간을 표현할 때 사용한다. 여기에는 타임존은 없고, UTC로 부터의 시간대 차이인 고정된 오프셋만 포함한다.
    • 예) 2013-11-21T08:20:30.213+9:00

Year, Month, YearMonth, MonthDay

년, 월, 년월, 달일을 각각 다룰 때 사용한다.

DayofWeek 와 같이 월, 화, 수, 목, 금, 토, 일을 나타내는 클래스도 있다.

Instant

Instant 는 UTC를 기준으로 하는, 시간의 한 지점을 나타낸다. Instant 는 날짜와 시간을 나노초 정밀도로 표현하며, 1970년 1월 1일 0시 0분 0초(UTC)를 기준으로 경과한 시간으로 계산된다.

Instant 내부에는 초 데이터만 들어있다.(나노초 포함)

Period, Duration

시간의 개념은 크게 2가지로 표현할 수 있다.

  • 특정 시점의 시간(시각)

  • 시간의 간격(기간)

Period, Duration 은 시간의 간격(기간)을 표현하는데 사용된다.

Period

두 날짜 사이의 간격을 년, 월, 일 단위로 나타낸다.

Duration

두 시간 사이의 간견을 시, 분, 초(나노초) 단위로 나타낸다

6.3 기본 날짜와 시간 - LocalDateTime

  • LocalDate : 날짜만 표현할 때 사용한다.

  • LocalTime : 시간만을 표현할 때 사용한다.

  • LocalDateTime : LocalDate와 LocalTime을 합한 개념이다.

앞에 Local 이 붙는 이유는 세계 시간대를 고려하지 않아서 타임존이 적용되지 않기 때문이다.

LocalDate

LocalDate nowDate = LocalDate.now();
LocalDate ofDate = LocalDate.of(2013, 11, 21);
System.out.println("오늘 날짜 = " + nowDate);
System.out.println("지정 날짜 = " + ofDate);
 
// 계산(불변)
LocalDate plusDays = ofDate.plusDays(10);
System.out.println("지정 날짜+10d = " + plusDays);

실행 결과

오늘 날짜 = 2024-02-09
지정 날짜 = 2013-11-21
지정 날짜+10d = 2013-12-01
  • now() : 현재 시간을 기준으로 생성한다.

  • of(...) : 특정 날짜를 기준으로 생성한다.

  • plusDays() : 특정 일을 더한다. 다양한 plusXxx() 메서드가 존재한다.

LocalTime

LocalTime nowTime = LocalTime.now();
LocalTime ofTime = LocalTime.of(9, 10, 30);
System.out.println("현재 시간 = " + nowTime);
System.out.println("지정 시간 = " + ofTime);
 
// 계산(불변)
LocalTime ofTimePlus = ofTime.plusSeconds(30);
System.out.println("지정 시간+30s = " + ofTimePlus);

실행 결과

현재 시간 = 11:52:51.219602
지정 시간 = 09:10:30
지정 시간+30s = 09:11:00
  • now() : 현재 시간을 기준으로 생성한다.

  • of(...) : 특정 시간을 기준으로 생성한다. 시, 분, 초, 나노초를 입력할 수 있다.

  • plusSeconds() : 특정 초를 더한다. 다양한 plusXxx() 메서드가 존재한다.

주의! - 불변

모든 날짜 클래스는 불변이다. 따라서 변경이 발생하는 경우 새로운 객체를 생성해서 반환하므로 반환값을 꼭 받아야 한다.

LocalDateTime

LocalDateTimeLocalDateLocalTime 을 내부에 가지고 날짜와 시간을 모두 표현한다.

LocalDateTime nowDt = LocalDateTime.now();
LocalDateTime ofDt = LocalDateTime.of(2016, 8, 16, 8, 10, 1);
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("지정 날짜시간+1년 = " + ofDtPlus1Year);
 
// 비교
System.out.println("현재 날짜시간이 지정 날짜시간보다 이전인가? " + nowDt.isBefore(ofDt));
System.out.println("현재 날짜시간이 지정 날짜시간보다 이후인가? " + nowDt.isAfter(ofDt));
System.out.println("현재 날짜시간과 지정 날짜시간이 같은가? " + nowDt.isEqual(ofDt));

실행 결과

현재 날짜시간 = 2024-02-09T11:54:54.389163
지정 날짜시간 = 2016-08-16T08:10:01
localDate = 2016-08-16
localTime = 08:10:01
localDateTime = 2016-08-16T08:10:01
지정 날짜시간+1000d = 2019-05-13T08:10:01
지정 날짜시간+1년 = 2017-08-16T08:10:01
현재 날짜시간이 지정 날짜시간보다 이전인가? false
현재 날짜시간이 지정 날짜시간보다 이후인가? true
현재 날짜시간과 지정 날짜시간이 같은가? false

생성

  • now() : 현재 날짜와 시간을 기준으로 생성한다.
  • of(...) : 특정 날짜와 시간을 기준으로 생성한다.

분리

날짜(LocalDate)와 시간(LocalTime) 을 toXxx() 메서드로 분리할 수 있다.

합체

  • LocalDateTime.of(LocalDate, LocalTime) : 날짜와 시간을 사용해서 LocalDateTime을 만든다.

비교

  • isBefore() : 다른 날짜시간과 비교한다. 현재 날짜와 시간이 이전이라면 true 를 반환한다.

  • isAfter() : 다른 날짜시간과 비교한다. 현재 날짜와 시간이 이후이라면 true 를 반환한다.

  • isEqual() : 다른 날짜시간과 시간적으로 동일한지 비교한다. 시간이 같으면 true 를 반환한다.

    isEqual() vs equals()

  • isEqual() 는 단순히 비교 대상이 시간적으로 같으면 true 를 반환한다. 객체가 다르고, 타임존이 달라도 시간적으로 같으면 true 를 반환한다.

    • 예) 서울의 9시와 UTC의 0시는 시간적으로 같다. 이 둘을 비교하면 true 를 반환한다.
  • equals() 객체의 타입, 타임존 등등 내부 데이터의 모든 구성요소가 같아야 true 를 반환한다.

    • 예) 서울의 9시와 UTC의 0시는 시간적으로 같다. 이 둘을 비교하면 타임존의 데이터가 다르기 때문에 false 를 반환한다.

6.4 타임존 - ZonedDateTime

"Asia/Seoul" 같은 타임존 안에는 일광 절약 시간제에 대한 정보와 UTC+9:00 와 같은 UTC로 부터 시간 차이인 오프셋 정보를 모두 포함하고 있다.

타임존 목록 예시

  • Europe/London
  • GMT
  • UTC
  • US/Arizona -07:00
  • America/New_York -05:00
  • Asia/Seoul +09:00
  • Asia/Dubai +04:00
  • Asia/Istanbul +03:00
  • Asia/Shanghai +08:00
  • Europe/Paris +01:00
  • Europe/Berlin +01:00

ZoneId

자바는 타임존을 ZoneId 클래스로 제공한다.

for (String availableZoneId : ZoneId.getAvailableZoneIds()) {
	ZoneId zoneId = ZoneId.of(availableZoneId);
    System.out.println(zoneId + " | " + zoneId.getRules());
}
 
ZoneId zoneId = ZoneId.systemDefault();
System.out.println("ZoneId.systemDefault = " + zoneId);
 
ZoneId seoulZoneId = ZoneId.of("Asia/Seoul");
System.out.println("seoulZoneId = " + seoulZoneId);

실행 결과

Europe/London | ZoneRules[currentStandardOffset=Z]
UTC | ZoneRules[currentStandardOffset=Z]
GMT | ZoneRules[currentStandardOffset=Z]
Asia/Seoul | ZoneRules[currentStandardOffset=+09:00]
Asia/Dubai | ZoneRules[currentStandardOffset=+04:00]
US/Arizona | ZoneRules[currentStandardOffset=-07:00]
Asia/Istanbul | ZoneRules[currentStandardOffset=+03:00]
Asia/Shanghai | ZoneRules[currentStandardOffset=+08:00]
...
Europe/Paris | ZoneRules[currentStandardOffset=+01:00]
ZoneId.systemDefault = Asia/Seoul
seoulZoneId = Asia/Seoul

생성

  • ZoneId.systemDefault() : 시스템이 사용하는 기본 ZoneId 를 반환한다.
    • 각 PC 환경마다 다른 결과가 나올 수 있다.
  • ZoneId.of() : 타임존을 직접 제공해서 ZoneId를 반환한다.

ZonedDateTime

ZonedDateTimeLocalDateTime 에 시간대 정보인 ZoneId 가 합쳐진 것이다.

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);

실행 결과

nowZdt = 2024-02-09T12:02:13.457712+09:00[Asia/Seoul]
zdt1 = 2030-01-01T13:30:50+09:00[Asia/Seoul]
zdt2 = 2030-01-01T13:30:50+09:00[Asia/Seoul]
utcZdt = 2030-01-01T04:30:50Z[UTC]

타임존 변경

withZoneSameInstant(ZoneId) : 타임존을 변경한다. 타임존에 맞추어 시간도 함께 변경된다. 이 메서드를 사용하면 지금 다른 나라는 몇 시인지 확인할 수 있다.

OffsetDateTime

OffsetDateTimeLocalDateTime 에 UTC 오프셋 정보인 ZoneOffset 이 합쳐진 것이다.

OffsetDateTime nowOdt = OffsetDateTime.now();
System.out.println("nowOdt = " + nowOdt);
 
LocalDateTime 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);

실행 결과

nowOdt = 2024-02-13T15:03:36.422230+09:00
ldt = 2030-01-01T13:30:50
odt = 2030-01-01T13:30:50+01:00

6.5 기계 중심의 시간 - Instant

Instant 는 UTC를 기준으로 하는, 시간의 한 지점을 나타낸다. Instant 는 날짜와 시간을 나노초 정밀도로 표현하며, 1970년 1월 1일 0시 0분 0초(UTC)를 기준으로 경과한 시간으로 계산된다.

Instant 특징

  • 장점

    • 시간대 독립성 : Instant는 UTC를 기준으로 하므로, 시간대에 영향을 받지 않는다. 이는 전 세계 어디서나 동일한 시점을 가리키는데 유용한다.

    • 고정된 기준점 : 모든 Instant 는 1970년 1월 1일 UTC를 기준으로 하기 때문에, 시간 계산 및 비교가 명확하고 일관된다.

  • 단점

    • 사용자 친화적이지 않음 : Instant 는 기계적인 시간 처리에는 적합하지만, 사람이 읽고 이해하기에는 직관적이지 않다.

    • 시간대 정보 부재 : Instant 에는 시간대 정보가 포함되어 있지 않아, 특정 지역의 날짜와 시간으로 변환하려면 추가적인 작업이 필요하다.

6.6 기간, 시간의 간격 - Duration, Period

Period, Duration 은 시간의 간격(기간)을 표현하는데 사용된다.

Period

//생성
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() + "일");

실행 결과

period = P10D
현재 날짜: 2030-01-01
더한 날짜: 2030-01-11
기간: 3개월 1일

Duration

//생성
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() + "분");

실행 결과

duration = PT30M
기준 시간 = 01:00
더한 시간 = 01:30
차이: 3600초
근무 시간: 1시간 0분

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

날짜와 시간은 특정 시점의 시간(시각)과 시간의 간격(기간)으로 나눌 수 있다.

TemporalAccessor 인터페이스

  • 날짜와 시간을 읽기 위한 기본 인터페이스

  • 이 인터페이스는 특정 시점의 날짜와 시간 정보를 읽을 수 있는 최소환의 기능을 제공한다.

Temporal 인터페이스

  • TemporalAccessor 의 하위 인터페이스로, 날짜와 시간을 조작(추가, 빼기 등)하기 위한 기능을 제공한다. 이를 통해 날짜와 시간을 변경하거나 조작할 수 있다.

TemporalAmount 인터페이스

  • 시간의 간격(시간의 양, 기간)을 나타내며, 날짜와 시간 객체에 적용하여 그 객체를 조정할 수 있다. 예를 들어, 특정 날짜에 일정 기간을 더하거나 빼는 데 사용된다.

시간의 단위와 시간 필드

  • TemporalUnit : 시간의 단위

  • TemporalField : 시간 필드

시간의 단위 - TemporalUnit, ChronoUnit

  • TemporalUnit 인터페이스는 날짜와 시간을 측정하는 단위를 나타내며, 주로 사용되는 구현체는 java.time.temporal.ChronoUint 열거형으로 구현되어 있다.
  • ChronoUnit 은 다양한 시간 단위를 제공한다.
ChronoUnit[] values = ChronoUnit.values();
for (ChronoUnit value : values) {
	System.out.println("value = " + value);
}
System.out.println("HOURS = " + ChronoUnit.HOURS);
System.out.println("HOURS.duration = " + ChronoUnit.HOURS.getDuration().getSeconds());
System.out.println("DAYS = " + ChronoUnit.DAYS);
System.out.println("DAYS.duration = " + ChronoUnit.DAYS.getDuration().getSeconds());

//차이 구하기
LocalTime lt1 = LocalTime.of(1, 10, 0);
LocalTime lt2 = LocalTime.of(1, 20, 0);
long secondsBetween = ChronoUnit.SECONDS.between(lt1, lt2);
System.out.println("secondsBetween = " + secondsBetween);
long minutesBetween = ChronoUnit.MINUTES.between(lt1, lt2);
System.out.println("minutesBetween = " + minutesBetween);

실행 결과

value = Nanos
value = Micros
value = Millis
value = Seconds
value = Minutes
value = Hours
value = HalfDays
value = Days
value = Weeks
value = Months
value = Years
value = Decades
value = Centuries
value = Millennia
value = Eras
value = Forever
HOURS = Hours
HOURS.duration = 3600
DAYS = Days
DAYS.duration = 86400
secondsBetween = 600
minutesBetween = 10

시간 필드 - TemporalField, ChronoField

ChronoField 는 날짜 및 시간을 나타내느 데 사용되는 열거형이다. 이 열거형은 다양한 필드를 통해 날짜와 시간의 특정 부분을 나타낸다. 여기에는 연도, 월, 일, 시간, 분 등이 포함된다.

ChronoField[] values = ChronoField.values();
for (ChronoField value : values) {
	System.out.println(value + ", range = " + value.range());
}
System.out.println("MONTH_OF_YEAR.range() = " + ChronoField.MONTH_OF_YEAR.range());
System.out.println("DAY_OF_MONTH.range() = " + ChronoField.DAY_OF_MONTH.range());

실행 결과

 NanoOfSecond, range = 0 - 999999999
 NanoOfDay, range = 0 - 86399999999999
 MicroOfSecond, range = 0 - 999999
 MicroOfDay, range = 0 - 86399999999
 MilliOfSecond, range = 0 - 999
 MilliOfDay, range = 0 - 86399999
 SecondOfMinute, range = 0 - 59
 SecondOfDay, range = 0 - 86399
 MinuteOfHour, range = 0 - 59
 MinuteOfDay, range = 0 - 1439
 HourOfAmPm, range = 0 - 11
 ClockHourOfAmPm, range = 1 - 12
 HourOfDay, range = 0 - 23
 ClockHourOfDay, range = 1 - 24
 AmPmOfDay, range = 0 - 1
 DayOfWeek, range = 1 - 7
 AlignedDayOfWeekInMonth, range = 1 - 7
 AlignedDayOfWeekInYear, range = 1 - 7
 DayOfMonth, range = 1 - 28/31
 DayOfYear, range = 1 - 365/366
 EpochDay, range = -365243219162 - 365241780471
 AlignedWeekOfMonth, range = 1 - 4/5
 AlignedWeekOfYear, range = 1 - 53
 MonthOfYear, range = 1 - 12
 ProlepticMonth, range = -11999999988 - 11999999999
 YearOfEra, range = 1 - 999999999/1000000000
 Year, range = -999999999 - 999999999
 Era, range = 0 - 1
 InstantSeconds, range = -9223372036854775808 - 9223372036854775807
 OffsetSeconds, range = -64800 - 64800
 MONTH_OF_YEAR.range() = 1 - 12
 DAY_OF_MONTH.range() = 1 - 28/31

6.8 날짜와 시간 조회하고 조작하기

날짜와 시간 조회하기

날짜와 시간을 조회하려면 날짜와 시간 항목 중에 어떤 필드를 조회할지 선택해야 한다. 이때 날짜와 시간의 필드를 뜻하는 ChronoField 가 사용된다.

LocalDateTime dt = LocalDateTime.of(2030, 1, 1, 13, 30, 59);
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("편의 메서드 사용");
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("편의 메서드에 없음");
System.out.println("MINUTE_OF_DAY = " + dt.get(ChronoField.MINUTE_OF_DAY));
System.out.println("SECOND_OF_DAY = " + dt.get(ChronoField.SECOND_OF_DAY));

실행 결과

YEAR = 2030
MONTH_OF_YEAR = 1
DAY_OF_MONTH = 1
HOUR_OF_DAY = 13
MINUTE_OF_HOUR = 30
SECOND_OF_MINUTE = 59

편의 메서드 사용
YEAR = 2030
MONTH_OF_YEAR = 1
DAY_OF_MONTH = 1
HOUR_OF_DAY = 13
MINUTE_OF_HOUR = 30
SECOND_OF_MINUTE = 59

편의 메서드에 없음
MINUTE_OF_DAY = 810
SECOND_OF_DAY = 48659

TemporalAccessor.get(TemporalField field)

  • LocalDateTime 을 포함한 특정 시점의 시간을 제공하는 클래스는 모두 TemporalAccessor 인터페이스를 구현한다.

  • TemporalAccessor 는 특정 시점의 시간을 조회하는 기능을 제공한다.

  • get(TemporalField field) 을 호출할 때 어떤 날짜와 시간 필드를 조회할 지 TemporalField 의 구현체인 ChronoField 를 인수로 전달하면 된다.

날짜와 시간 조작하기

날짜와 시간을 조작하려면 어떤 시간 단위(Unit)를 변경할 지 선택해야 한다. 이때 날짜와 시간의 단위를 뜻하는 ChronoUnit 이 사용된다.

LocalDateTime dt = LocalDateTime.of(2018, 1, 1, 13, 30, 59);
System.out.println("dt = " + dt);
 
LocalDateTime plusDt1 = dt.plus(10, ChronoUnit.YEARS);
System.out.println("plusDt1 = " + plusDt1);

LocalDateTime plusDt2 = dt.plusYears(10);
System.out.println("plusDt2 = " + plusDt2);

Period period = Period.ofYears(10);
LocalDateTime plusDt3 = dt.plus(period);
System.out.println("plusDt3 = " + plusDt3);

실행 결과

dt = 2018-01-01T13:30:59
plusDt1 = 2028-01-01T13:30:59
plusDt2 = 2028-01-01T13:30:59
plusDt3 = 2028-01-01T13:30:59

Temporal plus(long amountToAdd, TemporalUnit unit)

  • LocalDateTime 을 포함한 특정 시점의 시간을 제공하는 클래스는 모두 Temporal 인터페이스를 구현한다.

  • Temporal 은 특정 시점의 시간을 조작하는 기능을 제공한다.

  • plus(long amountToAdd, TemporalUnit unit) 를 호출할 때 더하기 할 숫자와 시간의 단위(Unit)를 전달하면 된다. 이때 TemporalUnit 의 구현인 ChronoUnit 을 인수로 전달하면 된다.

  • 불변이므로 반환 값을 받아야 한다.

  • 참고로 minus() 도 존재한다.

with()

LocalDateTime dt = LocalDateTime.of(2018, 1, 1, 13, 30, 59);
System.out.println("dt = " + dt);
 
LocalDateTime changedDt1 = dt.with(ChronoField.YEAR, 2020);
System.out.println("changedDt1 = " + changedDt1);

LocalDateTime changedDt2 = dt.withYear(2020);
System.out.println("changedDt2 = " + changedDt2);

//TemporalAdjuster 사용
//다음주 금요일
LocalDateTime with1 = dt.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("기준 날짜: "  + dt);
System.out.println("다음 금요일: " + with1);

//이번 달의 마지막 일요일
LocalDateTime with2 = dt.with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
System.out.println("같은 달의 마지막 일요일 = " + with2);

실행 결과

dt = 2018-01-01T13:30:59
changedDt1 = 2020-01-01T13:30:59
changedDt2 = 2020-01-01T13:30:59
기준 날짜: 2018-01-01T13:30:59
다음 금요일: 2018-01-05T13:30:59
같은 달의 마지막 일요일 = 2018-01-28T13:30:59

Temporal with(TemporalField field, long newValue)

  • Temporal.with() 를 사용하면 날짜와 시간의 특정 필드의 값만 변경할 수 있다.

  • 불변이므로 반환 값을 받아야 한다.

TemporalAdjuster 사용

  • with() 는 아주 단순한 날짜만 변경할 수 있다. 다음 주 금요일, 이번 달의 마지막 일요일 같은 복잡한 날짜를 계산하고 싶다면 TemporalAdjuster 를 사용하면 된다.

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

  • 포맷팅 : 날짜와 시간 데이터를 원하는 포맷의 문자열로 변경하는 것 Date -> String

  • 파싱 : 문자열을 날짜와 시간 데이터로 변경하는 것 String -> Date

// 포맷팅: 날짜를 문자로
LocalDate date = LocalDate.of(2024, 12, 31);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
String formattedDate = date.format(formatter);
System.out.println("날짜와 시간 포맷팅: " + formattedDate);
        
// 파싱: 문자를 날짜로
String input = "2030년 01월 01일";
LocalDate parsedDate = LocalDate.parse(input, formatter);
System.out.println("문자열 파싱 날짜와 시간: " + parsedDate);

실행 결과

현재 날짜와 시간 포맷팅: 2024년 12월 31일
문자열 파싱 날짜와 시간: 2030-01-01

LocalDate 과 같은 날짜 객체를 원하는 형태의 문자로 변경하려면 DateTimeFormatter 를 사용하면 된다. 여기에 ofPattern() 으로 원하는 포맷을 지정하면 된다. 여기서는 yyyy년 MM월 dd일 포맷을 지정했다.

DateTimeFormatter 패턴 공식 사이트

문자열을 날짜와 시간으로 파싱

LocalDate.parse(input, formatter)

// 포맷팅: 날짜와 시간을 문자로
LocalDateTime now = LocalDateTime.of(2024, 12, 31, 13, 30, 59);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter);
System.out.println("날짜와 시간 포맷팅: " + formattedDateTime);

// 파싱: 문자를 날짜와 시간으로
String dateTimeString = "2030-01-01 11:30:00";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println("문자열 파싱 날짜와 시간: " + parsedDateTime);

실행 결과

현재 날짜와 시간 포맷팅: 2024-12-31 13:30:59
문자열 파싱 날짜와 시간: 2030-01-01T11:30

6.10 날짜와 시간의 주요 메서드 정리


profile
가오리의 개발 이야기

0개의 댓글