날짜와 시간

고동현·2024년 7월 16일
0

JAVA

목록 보기
17/22

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

LocalDate,LocalTime,LocalDateTime

  • LocalDate: 날짜만 표현
  • LocalTime: 시간만 표현
  • LocalDateTime: LocalDate와 LocalTime을 합한 개념 ex) 2024-04-11T08:20:39.214

Local이므로 세계 시간대를 고려하지 않아, 타임존 적용 x

ZonedDateTime,OffsetDateTime

  • ZonedDateTime: 시간대를 고려한 날짜와 시간, 타임존 고려
    ex). 2024-12-21T08:20:30.213+9:00[Asia/Seoul]
    타임존을 고려하므로, 일광 절약 시간 적용
  • OffsetDateTime: 시간대를 고려한 날짜와 시간, 타임존 x
    ex). 2024-12-21T08:20:30.213+9:00
    타임존 없이, UTC로 부터 시간대 차이인 고정된 오프셋만 포함
    일광 절약시간 적용 x

Year,Month,YearMonth,MonthDay
년,월,년일,달월을 다룰때 사용, 자주 사용x

Instant
1970년1월1일0시0분0초로부터 경과한 초데이터가 들어있음

Period,Duration

  • Period: 두 날짜 사이의 간격을 년,월,일 단위로 나타냄
  • Duration: 두시간 사이의 간격을 시,분,초단위로 나타냄

LocalDateTime

LocalDate

  LocalDate now = LocalDate.now();
  LocalDate of = LocalDate.of(2024, 7, 1);
  //불변임
  LocalDate localDate = of.plusDays(10);

LocalDate: 날짜만 표현할때 사용, 년,월,일을 다룬다.
of.plusDays(10)을 한다고 of의 날짜가 바뀌지 않는다. 불변이기 때문에, 그래서 변경이 발생하는 경우 새로운 객체를 생성해서 반환받아야한다.

LocalTime

LocalTime now1 = LocalTime.now();
LocalTime of1 = LocalTime.of(9, 10, 30);
LocalTime localTime = of1.plusSeconds(34); //불변임

시간만을 나타낸다. 시분초를 다룬다.

LocalDateTime

LocalDateTime now2 = LocalDateTime.now();
LocalDateTime of2 = LocalDateTime.of(2022, 3, 24, 8, 10, 2);
LocalDate localDate1 = of2.toLocalDate();
LocalTime localTime1 = of2.toLocalTime();

LocalDateTime of3 = LocalDateTime.of(localDate1, localTime1);
LocalDateTime localDateTime = of2.plusDays(1000);
LocalDateTime localDateTime1 = of3.plusYears(10);

boolean before = now2.isBefore(of3);
boolean after = now2.isAfter(of3);
boolean equal = now2.isEqual(of3);

toLocalDate로 날짜를 분리하고, LocalTime으로 시간을 분리할 수 있다.

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

equals() 객체의 타입, 타임존등등 내부 데이터 구성요소가 모두 같아야 true반환

타임존 - ZonedDateTime

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

타임존 목록 예시
GMT,UTC,America/New_York-05:00,Asia/Seoul+09:00

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

for(String availableZoneId : ZoneId.getAvailableZoneIds()){
            ZoneId zoneId = ZoneId.of(availableZoneId);
            System.out.println(zoneId);
        }

        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId);

        ZoneId of = ZoneId.of("Asia/Seoul");

ZoneId목록 확인법: getAvailableZoneIds()
ZoneId 얻는법
ZoneId.of("Asia/Seoul"): 직접 입력
ZoneId.systemDefault: 운영체제에 있는 zoneid를 반환

ZoneId는 내부에 일광 절약 시간 관련 정보, UTC와의 오프셋 정보를 포함하고 있다.

ZonedDateTime
ZoneDateTime = LocalDateTIme+ZoneId
시간대를 고려한 날짜와 시간을 표현할때 사용, 시간대를 표현하는 타임존 포함
ex) 2024-12-21T08:20:30.213+9:00[Asia/Seoul]
+9:00 UTC로 부터 시간차이,오프셋
Asia/Seoul 타임존, 타임존을 통해 오프셋도 알 수 있다.
타임존을 통해 일광 절약 시간제에 대한 정보도 알수 있다.

ZonedDateTime now = ZonedDateTime.now();
LocalDateTime ldt = LocalDateTime.of(2024, 7, 23, 13, 22, 22);
ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.of("Asia/Seoul"));

ZonedDateTime zdt2 = ZonedDateTime.of(2024, 7, 23, 12, 33, 33, 0, ZoneId.of("Asia/Seoul"));

ZonedDateTime utc = zdt2.withZoneSameInstant(ZoneId.of("UTC"));

ZoneDateTime얻는법
ZonedDateTime.now()
ZonedDateTime.of(LocalDateTime,ZonedId);

타임존 변경
만약 지금 현재 시간에, 런던의 시간이 궁금하면, withZoneSameInstant(ZoneId)를 사용한다.

OffsetDateTime
OffsetDateTime = LocalDateTime + ZoneOffset
시간대를 고려한 날짜와 시간을 표현할 때 사용한다. 타임존은 없고, 오프셋만 포함한다.
ex) 2024-12-21T08:20:30.213+9:00
당연히 ZoneId가 없으니까 일광절약 시간제가 적용되지 않는다.

 ///OffsetDateTime
 OffsetDateTime now1 = OffsetDateTime.now();
 OffsetDateTime of1 = OffsetDateTime.of(ldt, ZoneOffset.of("+9:00"));

ZonedDateTime vs OffsetDateTime

  • ZonedDateTimed은 구체적인 지역 시간대를 다룰때 사용한다.
    일광절약 시간을 자동으로처리, 사용자 지정시간대에 따른 시간 계산
  • OffsetDateTime은 UTC와의 시간 차이만을 나타내며, 지역 시간대의 복잡성을 고려하지 않는다.

Instant

Instant: UTC를 기준으로하는 시간의 한 지점을 나타낸다.
1970년 1월 1일 0시 0분 0초로 부터 경과한 시간을 계산한다.

Instant내부에는 초데이터만 들어있으므로 날짜계산에는 적합하지 않다.

장점:

  • 시간대 독립성: Instant는 UTC를 기준으로, 시간대에 영향을 받지 않는다. 전세계 어디서나 동일한 시점을 가르킨다.
  • 고정된 기준점: UTC 1970년1월1일을 기준점으로 사용가능하다.

단점

  • 초단위만 있으므로 사용자 친화적이지 않다.

언제쓰냐?

  • 전 세계적인 시간 기준이 필요할때
  • 시간대 변화없이 순수하게 시간의 흐름만을 다루고 싶을때(지속시간 계산)
public class InstantMain {
    public static void main(String[] args) {
        //생성
        Instant now = Instant.now();
        System.out.println("now = " + now);
        ZonedDateTime now1 = ZonedDateTime.now();
        Instant from = Instant.from(now1);
        System.out.println("from = " + from);

        //1970-01-01T00:00:00Z
        Instant instant = Instant.ofEpochSecond(0);
        System.out.println("instant = " + instant);
        //1970-01-01T00:00:10Z
        Instant instant1 = instant.plusSeconds(10);
        System.out.println("instant1 = " + instant1);

        //10
        long epochSecond = instant1.getEpochSecond();
        System.out.println("epochSecond = " + epochSecond);
    }
}
now = 2024-07-16T08:22:25.582133600Z
from = 2024-07-16T08:22:25.603137800Z
instant = 1970-01-01T00:00:00Z
instant1 = 1970-01-01T00:00:10Z
epochSecond = 10
  • now: UTC를 기준으로 현재시간의 Instant를 생성한다.
  • from: 다른 타입의 날짜와 시간으로 Instant를 생성한다.
    Instant는 UTC를 기준으로 하기 때문에 시간대 정보가 필요하므로, LocalDateTime은 사용이 불가능하다.
  • ofEpochSecond(): 에포크 시간을 기주으로 Instant를 생성한다. 0초를 선택하면 에포크 시간인 1970년 1월1일 0시 0분 0초를 기준으로 생성된다.
  • getEpochSecond: 에포크 시간인 1970년 1월1일 0시 0분 0초를 기준으로 흐른 초를 반환한다. 앞서 에포크 시간에 10초를 더했으므로 10이 반환된다.

Duration,Period

Period,Duration은 시간의 간격(amount of Time)을 나타내기위해서 쓰인다.

Period

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

Period

public class PeriodMain {
    public static void main(String[] args) {
        Period period = Period.ofDays(10);
        LocalDateTime current = LocalDateTime.of(2024,7,17,8,19);
        LocalDateTime plus = current.plus(period);

        LocalDate start = LocalDate.of(2023, 2, 4);
        LocalDate end = LocalDate.of(2023, 3, 4);
        Period between = Period.between(start, end);
        System.out.println(between.getMonths()+"개월 "+between.getDays()+"일");
    }
}
  • Period는 of를 사용해서 생성한다.
  • LocalDateTime,LocalDate등의 특정 날짜 객체를 만들고, plus를 통해서 Period를 더할 수 있다.
  • 특정 날짜에서 between메서드를 통해서 간격을 구할 수 있다.

Duration
시,분,초 단위로 두 시간 사이의 간격을 나타낸다.

    Duration duration = Duration.ofMinutes(30);

        LocalTime lt = LocalTime.of(1, 0);
        LocalTime plus1 = lt.plus(duration);

        LocalTime start1 = LocalTime.of(9, 0);
        LocalTime start2 = LocalTime.of(10, 0);
        Duration between1 = Duration.between(start1, start2);

Duration도 동일하다. of를 통해서 생성하고, plus를 통해서 Duration 크기 만큼증가, 특정 시간대의 간격 등을 구할 수 있다.

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

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

  • TemporalAccessor
    특정 시점의 날짜와 시간을 읽을 수 있는 최소한의 기능만 제공
  • Temporal
    TemporalAccessor를 상속받아, 날짜와 시간을 조작할 수 있는 기능을 제공한다.
    즉 날짜와 시간을 조작+읽을 수 있다.
  • TemporalAmount
    시간의 간격을 나타내며, 특정 날짜에 일정기간을 더하거나 뺄때 쓴다.

시간단위

  • ChronoUnit
    시간의 단위를 나타낸다.
    SECONDS 초
    MINUTES 분
    YEARS 년
    ...
    이렇게 10초,60분 10년등의 시간의 단위를 나타낸다.
  • ChronoField
    날짜와 시간의 특정 부분을 나타낸다.
    각 특정 부분을 필드로 나타낼 수 있는데, 2024년 8월 16일 이라고 하면,
    YEAR:2024
    MONTH_OF_YEAR:8 -> range: 0~12
    DAY_OF_MONTH:16 -> range: 0~31
    ChronoField.DayOfWeek -> range: 0~7
    ChronoField.MINUTE_OF_DAY -> range: 0~1439

ChronoUnit처럼 10초,60분 같은 단위 하나하나가 아니라, 1년중에 몇월 이런식으로 특정 부분을 나타낸다.

public class GetTime{
	public static void main(String[] args){
    	//LocalDateTime에서 특정 시간, 날짜등을 가지고 오고 싶을때 -> ChronoField사용
        LocalDateTime dt = LocalDateTime.of(2024,7,19,6,32,42);
        //dt에서 년도를 가지고 오고 싶으면 특정 필드인 ChronoField이용
        dt.get(ChronoField.YEAR);
        //편의 메서드 지원
        dt.getYear();
        
        //만약6시32분42초인데, 총 분을 계산하고싶으면
        dt.get(ChronoField.MINUTE_OF_DAY);
        ->6*60+32=332분이 나옴
        
        //LocalDateTime에서 특정 날짜를 조작하고 싶을때 -> ChronoUnit사용
        //10을 더하긴 할껀데, 10년을 더할꺼냐? 10분을 더할꺼냐? 10초를 더할꺼냐?
        dt.plus(10,ChronoUnit.YEARS);
        //편의메서드 지원
        dt.plusYears(10);
        //다른방법
        Period period = Period.ofYears(10);
        dt.plus(period);
    }
}

정리
결국, LocalDateTime을 통해 날짜를 설정하면
원래는 날짜에서 특정 필드를 가져올때 ChronoField를 사용, 날짜를 조작할때 ChronoUnit을 사용해야한다.
그러나 TemporalAccessor.get(),Temporal.plus()같은 편의 메서드를 제공한다.

또한, 2시30분이라면, 단순히 2시,30분을 가져오는게 아니라 2시30분중에서 분을 가져오고 싶으면 dt.get(ChronoField.MINUTE_OF_DAY);등을 통해 구할 수 있다.

반대로, 모든 시간 필드를 조회할 수 는 없는데
LocalDate에는 년 월 일만 있는데,

LocalDate now = LocalDate.now();
int second = now.get(ChronoField.SECOND_OF_MINUTE);

없는 필드인 초를 확인하려하면 UnsupportedTemporalTypeException이 발생한다.
고로 현재 타입에서 특정 시간 단위나 필드를 사용할 수 있는지 확인하기 위해서, isSupported메서드를 활용하면 된다.

날짜를 그냥 바꾸는법
날짜를 조작할때 더하는게 아니라 그냥 2018년에서 2024년으로 바꾸고싶다면?

LocalDateTime dt = LocalDateTime.of(2022,3,2,12,33,24);

//2021년으로 바꾸기
LocalDateTime chdt = dt.with(ChronoField.YEAR,2021);
//편의메서드 제공
LocalDateTime chdt2 = dt.withYear(2021);

//TemporalAdjuster 사용
//다음 금요일
LocalDateTime with1 = dt.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
//이번달의 마지막 일요일
LocalDateTime with2 = dt.with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));

LocalDateTime은 불변객체이므로, 바꾸려면 with를 사용하여서 반환값을 받아야한다.

다음주 금요일, 마지막 일요일등 복잡한 날짜를 계산하고 싶다면, TemporalAdjuster를 사용하면된다.

DayOfWeek: 월,화,수,목,금,토,일을 나타내는 열거형이다.

파싱과 포멧팅

  • 포멧팅: 날짜와 시간 데이터 -> 문자
  • 파싱: 문자 -> 날짜와 시간 데이터
public class FormattingMain1{
	public static void main(String[]args){
    	//1. 패턴 설정
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
        
        //날짜와 시간 -> 문자
        LocalDate date = LocalDate.of(2024,7,19);
        String formattedDate = date.format(formatter);
        
        //문자 -> 날짜와 시간
        String input = "2024년 07월 19일";
        LocalDate parsedDate = LocalDate.parse(input,formatter);
    }
}

파싱과 포멧팅을 하기 위해서 원하는 패턴을 ofPattern으로 정하면된다.
기준은 y,M,d,H,m,s 이다.

profile
항상 Why?[왜썻는지] What?[이를 통해 무엇을 얻었는지 생각하겠습니다.]

0개의 댓글