[Java][time] LocalDateTime & ZonedDateTime 클래스, TemporalAdjusters 클래스

2022. 6. 20. 15:24JAVA/Language

1. LocalDateTime & ZonedDateTime 클래스

LocalDateTime과 ZonedDateTime 구성

LocalDate + LocalTime -> LocalDateTime
LocalDateTime + 시간대(time zone) -> ZonedDateTime

 

LocalDate와 LocalTime 클래스로 LocalDateTime 클래스 생성하기

1. of()와 now()를 이용하는 방법

		LocalDateTime dateTime = LocalDateTime.of(2015, 12, 31, 12, 34, 56);
		LocalDateTime today    = LocalDateTime.now();
		
		System.out.println(dateTime); // 2015-12-31T12:34:56
		System.out.println(today);    // 2022-06-16T17:27:08.260516800

2. LocalDate와 LocalTime을 이용하는 방법

		LocalDate date = LocalDate.of(2015, 12, 31);
		LocalTime time = LocalTime.of(12, 34, 56);
		
		LocalDateTime dt = LocalDateTime.of(date, time);
		LocalDateTime dt2 = date.atTime(time);
		LocalDateTime dt3 = time.atDate(date);
		LocalDateTime dt4 = date.atTime(12, 34, 56);
		LocalDateTime dt5 = time.atDate(LocalDate.of(2015, 12, 31));
		LocalDateTime dt6 = date.atStartOfDay(); // dt6 = date.atTime(0,0,0);

 

LocalDateTime을 LocalDate, LocalTime으로 변환 : toLocalDate(), toLocalTime()

		LocalDateTime dt = LocalDateTime.now();
		LocalDate date1 = dt.toLocalDate();
		LocalTime time1 = dt.toLocalTime();

 

LocalDateTime으로 ZonedDateTime 생성하기

LocalDateTime에 시간대(time-zone)를 추가하면, ZonedDateTime 클래스가 됩니다.

		// 1. ZoneId 클래스와 atZone(ZoneId) 메서드 활용
		LocalDateTime now = LocalDateTime.now();
		ZoneId zid = ZoneId.of("Asia/Seoul");
		ZonedDateTime zdt = now.atZone(zid);
		System.out.println(zdt); // 2022-06-16T17:44:45.823184500+09:00[Asia/Seoul]
		
		// 2. ZoneId 클래스와 atStartOfDay(ZoneId) 메서드 활용
		ZoneId zid2 = ZoneId.of("Asia/Seoul");
		ZonedDateTime zdt2 = LocalDate.now().atStartOfDay(zid2);
		System.out.println(zdt2); // 2022-06-16T00:00+09:00[Asia/Seoul]

 

ZoneOffset

ZoneOffset 클래스는 UTC로부터 얼마만큼 떨어져 있는지를 ZoneOffSet으로 표현하는 클래스입니다. 예를 들어 서울은 '+9'라는 것을 알 수 있습니다. 즉, UTC보다 9시간(60*60*9 = 32400초)이 빠릅니다.

		ZoneOffset krOffset = ZonedDateTime.now().getOffset();
		int krOffsetInSec = krOffset.get(ChronoField.OFFSET_SECONDS);
		System.out.println(krOffsetInSec); // 32400초

 

OffsetDateTime

ZonedDateTime 클래스는 ZoneId 클래스로 구역을 표현합니다. OffsetDateTime 클래스는 ZoneId 클래스가 아닌 ZoneOffset을 사용하여 표현하는 클래스입니다.

 

ZoneId 클래스는 일광절약시간처럼 시간대와 관련된 규칙들을 포함하고 있는데, ZoneOffset 클래스는 단지 시간대를 시간의 차이로만 구분합니다. 

 

컴퓨터에게 일광절약시간처럼 계절별로 시간을 더했다 뺏다하는 것과 같은 행위는 위험합니다. 아무런 변화 없이 일관된 시간체계를 유지하는 것이 더 안전합니다.

 

같은 지역 내의 컴퓨터간에 데이터를 주고받을 때, 전송시간을 표현하기에 LocalDateTime 클래스면 충분하지만 서로 다른 시간대에 존재하는 컴퓨터간의 통신에는 OffsetDateTime 클래스가 필요합니다.

		// OffsetDateTime : ZonedDatetime에 ZoneOffset을 적용하여 시간대를 시간의 차이로만 구분하는 클래스
		LocalDate date3 = LocalDate.now();
		LocalTime time3 = LocalTime.now();
		
		ZoneId zid3 = ZoneId.of("Asia/Seoul");
		ZonedDateTime zdt3 = ZonedDateTime.of(date3, time3, zid3); // ZoneId를 이용한 시간대 표현
		
		ZoneOffset krOffset3 = ZonedDateTime.now().getOffset();
		OffsetDateTime odt = OffsetDateTime.of(date3, time3, krOffset3); // 단순한 시간의 차이로만 표현
		
		System.out.println("zdt3 : " + zdt3); // zdt3 : 2022-06-16T18:13:27.532193600+09:00[Asia/Seoul]
		System.out.println("odt : " + odt);   // odt : 2022-06-16T18:13:27.532193600+09:00
		
		// OffsetDateTime 인스턴스를 얻는 2가지 방법
		// 1. OffsetDateTime = LocalDate + LocalTime + ZoneOffset
		OffsetDateTime odt2 = OffsetDateTime.of(date3, time3, krOffset3);
		// 2. OffsetDateTime = ZonedDateTime.toOffsetDateTime();
		OffsetDateTime odt3 = zdt3.toOffsetDateTime();

 

ZonedDateTime의 변환

ZonedDateTime도 LocalDateTime처럼 날짜와 시간에 관련된 다른 클래스로 변환하는 메서드들을 가지고 있습니다.

LocalDate       toLocalDate()
LocalTime       toLocalTime()
LocalDateTime   toLocalDateTime()
OffsetDateTime  toOffsetDateTime()
long            toEpochSecond()
Instant         toInstant()
		ZonedDateTime z = ZonedDateTime.now();
		System.out.println("ZonedDateTime의 변환");
		System.out.println("toLocalDate()      : " + z.toLocalDate());
		System.out.println("toLocalTime()      : " + z.toLocalTime());
		System.out.println("toLocalDateTime()  : " + z.toLocalDateTime());
		System.out.println("toOffsetDateTime() : " + z.toOffsetDateTime());
		System.out.println("toEpochSecond()    : " + z.toEpochSecond());
		System.out.println("toInstant()        : " + z.toInstant());
ZonedDateTime의 변환
toLocalDate()      : 2022-06-20
toLocalTime()      : 14:53:38.750017100
toLocalDateTime()  : 2022-06-20T14:53:38.750017100
toOffsetDateTime() : 2022-06-20T14:53:38.750017100+09:00
toEpochSecond()    : 1655704418
toInstant()        : 2022-06-20T05:53:38.750017100Z

 

2. TemporalAdjusters

TemporalAdjusters 클래스는 자주 쓰일만한 날짜 계산들을 대신 해주는 메서드를 정의해 놓은 클래스입니다. 예를 들어 지난 주 토요일이 며칠인지, 또는 이번 달의 3번째 금요일은 며칠인지를 알 수 있습니다.

LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

 

TemporalAdjusters의 메서드들 중 일부는 다음과 같습니다.

class DayAfterTomorrow implements TemporalAdjuster{

	@Override
	public Temporal adjustInto(Temporal temporal) {
		return temporal.plus(2, ChronoUnit.DAYS);
	}
}

public class NewTimeEx4 {
	public static void main(String[] args) {
		LocalDate today = LocalDate.now();
		LocalDate date = today.with(new DayAfterTomorrow());
		
		p(today);
		p(date);
		p(today.with(TemporalAdjusters.firstDayOfNextMonth())); 		    // 다음달의 첫날
		p(today.with(TemporalAdjusters.firstDayOfMonth()));                 // 이달의 첫날
		p(today.with(TemporalAdjusters.lastDayOfMonth()));                  // 이달의 마지막날
		p(today.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY)));   // 이 달의 첫번째 화요일
		p(today.with(TemporalAdjusters.lastInMonth(DayOfWeek.TUESDAY)));    // 이 달의 마지막 화요일
		p(today.with(TemporalAdjusters.previous(DayOfWeek.TUESDAY)));       // 제일 최근 이전의 화요일
		p(today.with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY))); // 오늘을 포함하여 제일 최근 이전의 화요일
		p(today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));			// 제일 최근 다음의 화요일
		p(today.with(TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY)));     // 오늘을 포함하여 제일 최근 다음의 화요일
		p(today.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.TUESDAY))); // 이 달의 4번째 화요일
			
	}
	
	public static void p(Object obj) {
		System.out.println(obj);
	}
}
class DayAfterTomorrow implements TemporalAdjuster{

	@Override
	public Temporal adjustInto(Temporal temporal) {
		return temporal.plus(2, ChronoUnit.DAYS);
	}
}

public class NewTimeEx4 {
	public static void main(String[] args) {
		LocalDate today = LocalDate.of(2022, 06, 20);
		LocalDate date = today.with(new DayAfterTomorrow());
		
		p(today);
		p(date);
		p(today.with(TemporalAdjusters.firstDayOfNextMonth())); 		    // 다음달의 첫날
		p(today.with(TemporalAdjusters.firstDayOfMonth()));                 // 이달의 첫날
		p(today.with(TemporalAdjusters.lastDayOfMonth()));                  // 이달의 마지막날
		p(today.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY)));   // 이 달의 첫번째 화요일
		p(today.with(TemporalAdjusters.lastInMonth(DayOfWeek.TUESDAY)));    // 이 달의 마지막 화요일
		p(today.with(TemporalAdjusters.previous(DayOfWeek.TUESDAY)));       // 제일 최근 이전의 화요일
		p(today.with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY))); // 오늘을 포함하여 제일 최근 이전의 화요일
		p(today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));			// 제일 최근 다음의 화요일
		p(today.with(TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY)));     // 오늘을 포함하여 제일 최근 다음의 화요일
		p(today.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.TUESDAY))); // 이 달의 4번째 화요일
			
	}
	
	public static void p(Object obj) {
		System.out.println(obj);
	}
}

 

References

source code : https://github.com/yonghwankim-dev/java_study/tree/main/ch10
[도서] Java의 정석, 남궁 성 지음