[250903] _ 날짜와 시간

호두·2025년 9월 7일

✔️ Java / Spring

목록 보기
24/25

✅ 날짜와 시간 라이브러리

java.time 패키지 ➡️ 표준적인 날짜와 시간 라이브러리


LocalDate : 특정 지역의 날짜를 표현

LocalTime : 특정 지역의 시간을 표현

LocalDateTime : 특정 지역의 날짜와 시간을 표현

⚠️ 세계 시간대를 고려하지 않고 그 지역의 날짜와 시간을 표현하기 때문에 Local 은 타임존이 포함되어있지 않다.
(애플리케이션 개발시 국내서비스만 고려할 때)

💡 타임존이란?
세계는 다양한 타임존을 가지고 있으며 각각의 타임존은 UTC(협정세계시) 로부터 시간차이로 결정된다.
예 ) Asia/Seoul + 09:00

ZoneDateTime
: 시간대(특정지역) 를 고려한 날짜와 시간을 표현 ( 타임존 포함❗️ )

OffsetDateTime
: 시간대를 고려한 날짜와 시간을 표현 ( 타임존 포함 ❌ )

Instant
: 1970년 1월 1일 0시 0분 0초 를 기준으로 해서 특정 시점까지의 경과시간을 초단위로 계산


🕰️ 시간의 2가지 개념

1️⃣ 특정한 시점의 시간
2️⃣ 시간의 간격 (기간)

Period(날짜) 와 Duration(시간) 은 시간의 간격❗️을 표현하는데 사용한다.


✅ 기본 날짜와 시간

날짜 LocalDate 인터페이스 사용 ⬇️

package time;

import java.time.LocalDate;

public class LocalDateMain {

    public static void main(String[] args) {
        LocalDate nowDate = LocalDate.now();
        LocalDate ofDate = LocalDate.of(2023, 11, 21);
        System.out.println("오늘 날짜 : " + nowDate);
        System.out.println("지정 날짜 : " + ofDate);

        LocalDate plusDays = ofDate.plusDays(10);
        System.out.println("지정 날짜 + 10d = "+ plusDays);

    }
}

시간 LocalTime 인터페이스 사용⬇️

package time;

import java.time.LocalTime;

public class LocalTimeMain {
    public static void main(String[] args) {
        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("지정 시간 + 30d = "+ofTimePlus);
    }
}

날짜, 시간 LocalDateTime 인터페이스 사용 ⬇️

package time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class LocalDateTimeMain {

    public static void main(String[] args) {

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

        // 비교
        System.out.println("현재 날짜시간이 지정 날짜시간보다 이전인가? = " + nowDt.isBefore(ofDt));
        System.out.println("현재 날짜시간이 지정 날짜시간 이후인가? = " + nowDt.isAfter(ofDt));
        System.out.println("현재 날짜시간과 지정 날짜시간이 같은가? = " + nowDt.isEqual(ofDt));
    }
}

➡️ toXXX() 는 "XXX로 변환한다" 라는 뜻을 가진 새로운 타입/표현으로 바꾸는 메서드이다.


✅ 타임존 - ZonedDateTime

타임존은 Asia/Seoul 처럼 대륙과 도시를 문자로 표현한 걸 말한다. 타임존에는 일광 절약 시간제에 대한 정보와 UTC + 9:00 같은 UTC 로 부터의 시간차이인 오프셋 정보를 모두 포함하고 있다.

16:00 ➡️ 시간만 봐서는 어느지역인지 알수 없다.
2025-09-04 12:30 UTC + 9 [Asia/Seoul] ➡️ 전세계 누구든 UTC로 환산해서 정확히 동일한 시간을 알 수 있다.

📌 자바는 타임존을 ZoneId 클래스로 제공


📍 해당 지역의 UTC 시간차 : ZoneId

☝️ ZondId 는 자바 날짜, 시간 API(java.time) 에서 "시간대(time zone)" 를 표현하는 클래스이다.

헷갈리지 말기 ‼️

  • LocalDateTime(날짜,시각)
    : 그냥 연·월·일·시·분·초만 있는 값 → “어느 나라 시간인지” 정보 없음.
  • ZoneId(지역/시간차 not 지역의 현재시각)
    : “이건 서울(Asia/Seoul) 기준이야” 같은 지역 시간대 정보. (지역 + 지역이 가지는 UTC 시간차)
  • ZonedDateTime(날짜,시각,시간차,지역의 현재시각 모두)
    : LocalDateTime + ZoneId → 절대적인 시각을 알 수 있음.
package time;

import java.time.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());
        }

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

        ZoneId seoulZoneId = ZoneId.of("Asia/Seoul");
        System.out.println("seoulZoneId = "+ seoulZoneId);
    }
}

➡️ ZoneId.getAvailableZoneIds() : 시스템이 사용하는 기본 ZoneId를 반환
➡️ ZoneId.of() : 타임존을 직접 제공해서 ZoneId를 반환

✅ 절대적인 시각 VS 상대적인 시각

  • 상대적인 시각은 숫자표기만 있는 시각으로 어느라라 기준인지 모르면 다르게 해석된다. 서울의 12시와 뉴욕의 12시는 같은 숫자지만 실제 순간이 다르다.
  • 절대적인 시각은 UTC 기준으로 유일하게 하나만 존재하는 순간을 말한다. 한국의 낮 12시와 뉴욕의 23시는 표기가 다르지만 같은 순간을 가리킨다.


📍 ZonedDateTime

날짜, 시간의 LocalDateTime 에 타임존인 ZoneId합쳐진 것이다. 시간대를 고려한 날짜와 시간을 표현할 때 사용
(절대적인 시각을 표현할 수 있는 타입)

📌 타임존 (time zone, 시간대)
특정 지역의 이름을 기준으로 시간대를 정의하는 것. Asia/Seoul, America/New_York
타임존은 오프셋을 포함한 채 언제, 어떻게 적용할지 규칙까지 담고있는 상위개념이다.

package time;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class ZonedDateTimeMain {
    public static void main(String[] args) {

        ZonedDateTime nowZt = ZonedDateTime.now();
        System.out.println("nowZt = " + nowZt);

        LocalDateTime ldt = LocalDateTime.of(2030, 1, 1, 13, 30, 50);
        ZonedDateTime zdt1 = ZonedDateTime.of(ldt, ZoneId.of("Asia/Seoul")); 
        // ☝️ **날짜/시간 과 타임존을 넣어서 ZonedDateTime 을 생성함**
        
        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);
    }
}

➡️ withZoneSameInstant(ZoneId) 타임존을 변경한다. 그에 맞게 시간도 함께 변경되며 지금 다른 나라는 몇시인지 확인할 수 있다.


📍 OffsetDateTime

LocalDateTime 에 UTC 오프셋 정보인 ZoneOffset 이 합쳐진 것이다. 날짜/시간 과 오프셋 정보가 있으니 타임존의 정보는 없다.

📌 UTC오프셋 (offset)
단순히 "UTC와 몇 시간 차이✨ 나는가?" 를 수치로 나타낸것. 그냥 숫자 차이일 뿐, 어느나라/지역인지는 알 수 없음

package time;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class OffsetDateTimeMain {
    public static void main(String[] args) {
        OffsetDateTime nowOdt = OffsetDateTime.now();
        System.out.println("nowOdt = "+nowOdt);

        LocalDateTime Idt = LocalDateTime.of(2030,1,1,13,30,50);
        System.out.println("Idt = "+ Idt);
        OffsetDateTime odt = OffsetDateTime.of(Idt, ZoneOffset.of("+01:00")); 
        // 날짜,시간 과 오프셋정보를 담을 수 있는 OffsetDateTime 객체
        System.out.println("odt = "+odt);
    }
}

📍 Instant

UTC 를 기준으로하는 시간의 어떤 시점을 말한다. 쉽게 이야기해서 UTC로부터 어떤 시점까지의 경과 시간을 알아내려 할 때 사용한다. 이 Instant 내부에는 초단위만 들어가 있다.

Epoch time 🕙
컴퓨터 시스템에서 시간을 나타내는 방법 중 하나로 UTC 이후 경과한 전체 초의 수이다. 시간대에 영향을 받지 않는 절대적인 시간표현방식이다. epoch 는 어떤 중요한 사건이 발생한 시점을 기준으로 삼는 시작점을 뜻한다.

☝️장점 : 시간대의 영향을 받지 않음

☝️단점 :
1. 초 단위라서 사람이 읽고 바로 이해하기에는 직관적이지 않다.
2. 시간대 정보가 없어 특정 지역의 날짜와 시간으로 변환하려면 추가적인 작업이 필요하다.


사용 예시 🗣️

  • 시간대 변환 없이 시간 계산 필요 시
  • 데이터베이스에 저장하거나 정보를 교환할 때 Instant를 사용하면 모든 시스템에서 데이터를 일관성있게 유지하기 좋다.
package time;

import java.time.Instant;
import java.time.ZonedDateTime;

public class InstantMain {
    public static void main(String[] args) {

        Instant now = Instant.now(); // 객체 생성
        System.out.println("now = " + now);

        ZonedDateTime zbt = ZonedDateTime.now(); // 시간대+날짜/시간+오프셋
        Instant from = Instant.from(zbt); 
        // UTC 이후 경과시간 초단위로 변경
        System.out.println("from = "+ from);



        Instant epochStart = Instant.ofEpochSecond(0); 
        // epoch시간을 기준으로 ()의 Instant를 생성함
        System.out.println("epochStart = " + epochStart);

        Instant later = epochStart.plusSeconds(3600); // 
        System.out.println("later = "+ later);

        long laterEpochSecond = later.getEpochSecond();
        System.out.println("laterEpochSecond = "+laterEpochSecond);
    }
}

⚠️ Instantepoch을 기준으로 몇초가 흘렀는지를 저장하는 클래스다.
(Epoch 는 기준점이고, Instant 는 그 기준으로 측정된 실제 시각‼️)


➡️ ofEpochSecond() : 에포크 시간을 기준으로 Instant 를 생성한다.
➡️ plusSeconds() : 초를 더한다.
➡️ getEpochSecond() : 에포크시간을 기준으로 흐른 초를 반환한다.

➡️ withZoneSameInstant(ZoneId.of(지역이름))
: 현재 시각을 그대로 유지한 채로, 기준이 되는 타임존만 바꿔서 새로운 ZoneDateTime 객체를 반환한다. 순간(Instant) 만 고정하고 그 순간을 다른 시간대로 표현한다.

➡️ withZoneSameLocal(ZoneId zone)
: 표현 그대로 두고, 순간을 바꿈. 즉, 시각 텍스트는 유지되지만 실제 시간은 바뀜.



✅ Duration, Period

시간은 특정 시점의 시간시간의 간격(기간) 으로 나눌 수 있다. 이 중에 시간의 간격을 표현하는 두 가지 개념이 존재한다❗️

Period : 두 날짜 사이의 간격

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


    }
}

✔️ ofXXX() : 특정 기간을 지정해서 Period 를 생성한다. ( ofDays(), of(년,월,일) 등 )
✔️ 기간 차이 : Period.between(날짜1,날짜2) 로 구한다.

🌟 기간을 연/월/일 단위로 나눠서 저장한다. getDays() 로 호출할 때 기간이 일 수로 변경되서 나오는게 아니라 나눠진 단위에 있는 숫자를 반환한다. 즉, 총 기간을 나타내지 못한다.

💡 Tip
getXXX() 메서드는 내가 가지고 있는 속성을 반환하고, toXXX() 메서드는 뭔가를 계산해서 반환한다.


Duration : 두 시간 사이의 간격

package time;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;

public class DurationMain {
    public static void main(String[] args) {

        Duration duration = Duration.ofMinutes(30);
        System.out.println("duration = " + duration); // 계산 시 사용가능한 30분 생성
        
        

        LocalTime It = LocalTime.of(1, 0);
        System.out.println("기준 시간 : " + It);

        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️⃣ 특정 시점의 시간

: Temporal 인터페이스를 구현한다. (TemporalAccessor 포함)

이 인터페이스는 LocalDateTime, LocalDate, LocalTime, ZonedDateTime, OffsetDateTime, Instant 클래스들로 구현된다.

2️⃣ 시간의 간격 (기간)

: TemporalAmout 인터페이스를 구현한다.

이 인터페이스는 Period, Duration 클래스가 있다.

TemporalAccessor 읽기 전용 인터페이스

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

Temporal 조작 가능한 인터페이스

: TemporalAccessor 인터페이스의 하위 인터페이스로, 날짜와 시간을 💥조작💥 (추가,빼기 등) 하기 위한 기능을 제공

TemporalAmount 인터페이스

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

TemporalUnit 인터페이스는 "시간 단위" 가 갖춰야 할 규격만 정의. ChronoUnit 은 이 규격을 충족시키도록 이미 Enum으로 구현이 돼 있는 상태. 우리는 가져다가 쓰는 것이다.



📌 시간 단위와 시간 필드


✔️ ChronoUnit 시간 단위

  • TemporalUnit 인터페이스의 Enum 구현체로 다양한 시간 단위를 제공하며 💥조작의 기능을 사용할 수 있다.
  • 단순히 몇 일을 더하고 빼는 단위 개념을 정의하는 역할. 즉 몇 단위로 시간을 조작할 것인가 를 결정할 때 사용됨

📍 TemporalUnit 시간 단위 인터페이스➿

날짜와 시간을 측정하는 단위를 나타내는 인터페이스다. 이 인터페이스는 java.time.temporal.ChronoUnit 열거형 (enum) 으로 구현된다. 다양한 시간 단위를 상수로 제공한다.

package time;

import java.time.LocalTime;
import java.time.temporal.ChronoUnit;

public class ChronoUnitMain {

    public static void main(String[] args) {
        ChronoUnit[] values = ChronoUnit.values(); // 모든 시간 단위 호출
        for (ChronoUnit value : values) {
            System.out.println("value = "+value); // 하나씩 출력
        }
        System.out.println("HOURS = " + ChronoUnit.HOURS); // 시간 단위 묶음 안에서 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 It1 = LocalTime.of(1, 10, 0);
        LocalTime It2 = LocalTime.of(1, 20, 0);

        long secondsBetween = ChronoUnit.SECONDS.between(It1, It2);
        System.out.println("secondsBetween ="+secondsBetween);

        long minutesBetween = ChronoUnit.MINUTES.between(It1, It2);
        System.out.println("minutesBetween = "+minutesBetween);
    }
}

모든 시간들의 단위별 조작을 하려면 ChronoUnit 을 사용해야한다.
(초, 분, 시간, 일, 주, 년 ...)

➡️ 이 ChronoUnit 을 이용하면 모든 날짜와 시간 데이터를 하나의 단위로 변환해서 계산할 수 있다 ‼️


✔️ ChronoField 시간 필드

TemporalField 인터페이스의 enum 구현체이다. 다양한 필드를 통해 날짜와 시간의 특정 부분을 나타낸다.

(⚠️ 단순히 시간의 단위 하나하나를 뜻하는 ChronoUnit 과 다르게 각각 필드들이 유기적으로 연결되어 하나의 특정 시점을 표현한다. 16일 이 아니라 2025년 6월 16일 처럼 그 시점을 뜻한다.)

  • ChronoUnit = "시간을 자르는 잣대(단위)"
    🗣️ "며칠/몇 달을 더하거나 뺄까?"
  • ChronoField = "그 잣대로 잘라낸 조각들의 실제 값(속성/정보)"
    🗣️ "이 날짜의 값은 뭐지?"

📍 TemporalField 시간 속성 인터페이스➿

시간을 다루는 각각의 필드(속성)를 뜻한다. 단위를 나타내는 것이 아니라, 어떤 값을 읽거나 쓸 것인지를 정의하는 역할이다. 즉 시간 속성 하나하나에 접근해 조회할 때 사용한다.

package time;

import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;

public class ChronoFieldMain {
    public static void main(String[] args) {
        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());

    }
}

➡️ range() 는 그 단위의 범위를 조회해준다. 예) 1일 = 0~23시


✨정리
TemporalUnit(ChronoUnit)TemporalField(ChronoField) 는 단독으로 사용하기보다 주로 날짜와 시간을 조회하거나 조작할 때 사용한다.


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


1. 날짜와 시간 조회하기

날짜와 시간을 조회하려면 날짜와 시간 항목중에 어떤 필드를 조회할 지 선택해야 한다.

package time;

import java.time.LocalDateTime;
import java.time.temporal.ChronoField;

public class GetTimeMain {

    public static void main(String[] args) {
        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("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));

    }
}

🖍️ 시간, 날짜 생성은 LocalXXX 를 사용해서 한다

  • get(시간 단위) : 모든 시간, 날짜를 호출할 수 있다. ➡️ 편의 메서드로 자주 사용하는 기능은 getDayOfMonth() 처럼 매개변수 없이 한번에 호출할 수도 있다.


2. 날짜와 시간 조작하기

날짜와 시간을 조작하려면 어떤 시간 단위를 변경할 지 선택해야 한다.

package time;

import java.time.LocalDateTime;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class ChangeTimePlusMain {
    public static void main(String[] args) {
        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);
    }
}

⭐️ 시간 단위 선택 ‼️ ⭐️

  • plus(더할 양, 시간 단위) : 더하기 할 숫자와 시간의 단위를 전달하면 된다. 이때 ChronoUnit 을 인수로 전달하면 된다.

  • 마찬가지로 편의 메서드로 plusYears(숫자) 로 하면 바로 10년이 더해진다.


이때 ! 날짜 정보를 담는 LocalDate 에서 시간을 찾으면 예외가 발생할 수 있다 ☝️

➡️ 그래서 특정 시간 단위나 필드를 사용할 수 있는지 확인하는 메서드 isSupported() 를 제공해준다.

package time;

import java.time.LocalDate;
import java.time.temporal.ChronoField;

public class IsSupportedMain2 {
    public static void main(String[] args) {

        LocalDate now = LocalDate.now();
        // boolean 타입이다! 맞다&아니다 둘 중 하나 선택이니까‼️
        boolean supported = now.isSupported(ChronoField.SECOND_OF_MINUTE);
        System.out.println("supported = "+supported);
        if (supported) {
            int minute = now.get(ChronoField.SECOND_OF_MINUTE);
            System.out.println("minute = "+minute);
        }
    }
}

3. 날짜와 시간을 조작하는 with() 메서드

package time;

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;

public class ChangeTimeWithMain {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2018, 1, 1, 13, 30, 59);
        System.out.println("dt = " + dt);

        LocalDateTime changeDt1 = dt.with(ChronoField.YEAR, 2020);
        System.out.println("changeDt1 = " + changeDt1);

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

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

💡 with(바꿀 단위, 숫자) 메서드

: 날짜와 시간의 특정 필드의 값만 변경할 수 있다. 불변이라 값을 반환 받아야한다.

마찬가지로 편의 메서드 withYear() 이 있다.

TemporalAdjustor = 복잡한 날짜 계산 인터페이스

: with 는 단순한 날짜만 변경할 수 있다. 다음 금요일, 이번 달 마지막 같은 복잡한 조작은 TemporalAdjustor 을 사용해야한다. 이 인터페이스의 구현체는 TemporalAdjustors 이다. 클래스들로 다 만들어두었다.


TemporalAdjustors 클래스가 제공하는 주요기능 ⬇️


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

package time;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class FormattingMain2 {
    public static void main(String[] args) {

        // 포멧팅 : 날짜와 시간을 문자열로
        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 parseDateTime = LocalDateTime.parse(dateTimeString, formatter);
        System.out.println("문자열 파싱 날짜와 시간 : "+parseDateTime);
    }
}

➡️ DateTimeFormatter.ofPattern() - 날짜와 시간의 객체를 원하는 형태의 문자열로 변경하려면 이 클래스를 이용하면된다. 이 클래스 안의 ofPattern(원하는 형태) 로 지정하면 이에 맞춰서 지정된다.


🤜 포멧팅 (Formatting)

Date/Time -> String
: 날짜와 시간 형태를 문자열로 변경하는 기능
➡️ format() - 포멧팅 메서드 ( 안에 문자열 형태 지정한 변수 )

🤜 파싱 (Parsing)

String -> Date/Time
: 문자열을 날짜와 시간으로 변경하는 기능
➡️ 파싱할 때, 어떤 부분이 년이고 월인지 parse( 바꿀 문자열, 문자열 형태) 안의 문자열형태의 "yyyy년 MM월 dd일" 을 보고 판단하기 때문에 꼭 넣어야 한다.



문제와 풀이 📝

package time.test;

import java.time.LocalDateTime;

public class TestPlus {
    public static void main(String[] args) {

        LocalDateTime time = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
        System.out.println("기준 시각 : "+time);
        LocalDateTime timePassed = time.plusYears(1).plusMonths(2).plusDays(3).plusHours(4);
        System.out.println("1년 2개월 3일 4시간 후의 시각 : "+timePassed);
    }
}

package time.test;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class TestLoopPlus {
    public static void main(String[] args) {

        LocalDate date = LocalDate.of(2024, 1, 1 );
        for (int i = 0; i < 5; i++) {
            LocalDate nextDate = date.plus(2 * i,ChronoUnit.WEEKS ); // plus ( 더할 숫자 , 더할 날짜단위 )
            System.out.println("날짜 "+(i+1)+" : "+nextDate);
        }
    }
}

package time.test;

import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class TestBetween {
    public static void main(String[] args) {

        LocalDate startDate = LocalDate.of(2024, 1, 1);
        LocalDate endDate = LocalDate.of(2024, 11, 21);

        Period period = Period.between(startDate, endDate); // 날짜의 간격은 period로!!
        long daysBetween = ChronoUnit.DAYS.between(startDate,endDate); // ChronoUnit 은 초단위로만 표현할 수 있기 때문에 long 사용
        // 시간이나 날짜를 조작할 때 사용하는 ChronoUnit 클래스

        System.out.println("시작 날짜 : "+startDate);
        System.out.println("목표 날짜 : " + endDate);
        System.out.println("남은 기간 : "+period.getYears()+"년 "+period.getMonths()+"월 "+period.getDays()+"일 ");
        // period 의 날짜는 한번에 출력되는게 아니라 하나하나 날짜단위씩 가져와서 출력해야한다.

        System.out.println("디데이 : "+daysBetween +"일 남음");
    }
}

package time.test;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;

public class TestAdjusters {
    public static void main(String[] args) {
        int year = 2024;
        int month = 1;

        LocalDate date = LocalDate.of(year, month, 1);
        DayOfWeek firstDayOfWeek = date.getDayOfWeek();
        DayOfWeek lastDayOfWeek = date.with(TemporalAdjusters.lastDayOfMonth()).getDayOfWeek();
        // TemporalAdjusters 는 날짜와 시간을 읽을 수 있는 클래스다.
        // with() 메서드는 날짜와 시간의 특정 필드를 변경할 수 있는 메서드다.
        // date 의 날짜를 읽어서 그 달의 마지막날을 반환하고, 요일을 확인한다.

        System.out.println("firstDayOfWeek = "+firstDayOfWeek);
        System.out.println("lastDayOfWeek = "+lastDayOfWeek);

    }
}

package time.test;

import java.time.*;

public class TestZone {
    public static void main(String[] args) {


        ZonedDateTime seoulTime = ZonedDateTime.of(LocalDate.of(2024, 1, 1), LocalTime.of(9, 0, 0), ZoneId.of("Asia/Seoul"));
        // ZoneDateTime 안에 날짜와 시간을 LocalDate 와 LocalTime 으로 설정해서 넣어야 한다.

        ZonedDateTime londonTime = seoulTime.withZoneSameInstant(ZoneId.of("Europe/London"));
        ZonedDateTime nyTime = seoulTime.withZoneSameInstant(ZoneId.of("America/New_York"));

        System.out.println("서울의 회의 시간 : "+seoulTime);
        System.out.println("런던의 회의 시간 : "+londonTime);
        System.out.println("뉴욕의 회의 시간 : "+nyTime);

    }
}

👾 달력 출력 문제 ( 난이도 있음 )

package time.test;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.util.Scanner;

public class TestCalenderPrinter { // 개어려움 🥲

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("년도를 입력하세요 : ");
        int year = scanner.nextInt();
        System.out.print("월을 입력하세요 : ");
        int month = scanner.nextInt();

        printCalendar(year,month);
    }

    public static void printCalendar(int year, int month) {
        LocalDate firstDay = LocalDate.of(year, month, 1);
        LocalDate firstDayOfNextMonth = firstDay.plusMonths(1);

        int offsetWeekDays = firstDay.getDayOfWeek().getValue() % 7;

        System.out.println("Su Mo Tu We Th Fr Sa");

        for (int i = 0; i < offsetWeekDays; i++) {
            System.out.print("   ");
        }

        LocalDate dayIterator = firstDay;
        while (dayIterator.isBefore(firstDayOfNextMonth)) {
            System.out.printf("%2d ", dayIterator.getDayOfMonth());
            if (dayIterator.getDayOfWeek() == DayOfWeek.SATURDAY) {
                System.out.println();
            }
            dayIterator = dayIterator.plusDays(1);
        }
    }
}

0개의 댓글