본문 바로가기

날짜 조회·조작·서식

날짜 파싱 실패를 재현하고 TemporalAdjusters와 DateTimeFormatter로 조회·조정·출력 규칙을 익힙니다.

java.time 값은 불변이므로 조회 메서드는 필드를 읽고 조작 메서드는 새 값을 반환합니다.

문자열 경계에서는 DateTimeFormatter가 문자 위치와 의미를 정의합니다.

파싱 형식과 출력 형식을 분리하고, 지원하지 않는 필드·잘못된 날짜·반환값 누락을 경계에서 처리해야 합니다.


날짜 서식과 파싱 실패

입력은 점으로 연·월·일을 나누지만 formatter는 하이픈을 기대합니다.

LocalDate는 어디까지 읽었는지를 포함한 DateTimeParseException을 던집니다.

lab/DatePatternMismatchFailure.java
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public final class DatePatternMismatchFailure {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate date = LocalDate.parse("2025.03.10", formatter);
        System.out.println(date);
    }
}
실패 관찰
Exception in thread "main" java.time.format.DateTimeParseException

사용자 입력 양식과 formatter를 같은 경계에 두고 예시를 제공합니다.

여러 입력 형식을 허용해야 하면 무작정 문자열 replace를 하기보다 허용 formatter 목록을 순서대로 시도하고 모호한 날짜를 거부합니다.


편의 조회와 동적 필드 조회

고정된 업무 코드에서는 getYear, getMonth, getDayOfWeek가 의도를 잘 드러냅니다.

범용 표 작성기처럼 필드가 데이터로 전달될 때는 ChronoField를 사용합니다.

src/DateTimeFieldQuery.java
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;

public final class DateTimeFieldQuery {
    public static void main(String[] args) {
        LocalDateTime value = LocalDateTime.of(2025, 3, 10, 14, 25, 30);

        System.out.println("year=" + value.getYear());
        System.out.println("day-of-week=" + value.getDayOfWeek());
        System.out.println("hour=" + value.get(ChronoField.HOUR_OF_DAY));
        System.out.println("day-range=" + value.range(ChronoField.DAY_OF_MONTH));
        System.out.println("offset-supported=" + value.isSupported(ChronoField.OFFSET_SECONDS));
    }
}
year=2025
day-of-week=MONDAY
hour=14
day-range=1 - 31
offset-supported=false

LocalDateTime에는 오프셋이 없으므로 OFFSET_SECONDS를 지원하지 않습니다.

get을 바로 호출하기보다 isSupported를 확인하거나 ZonedDateTime으로 정보 모델을 바꿉니다.

타입에 없는 정보를 기본값 0으로 꾸며 내지 않습니다.


plus와 with의 차이

plus는 현재 값에서 상대적인 양을 더하고, with는 특정 필드를 절대값으로 교체합니다.

둘 다 원본을 유지합니다.

src/DateTimeManipulation.java
import java.time.LocalDateTime;
import java.time.Period;

public final class DateTimeManipulation {
    public static void main(String[] args) {
        LocalDateTime original = LocalDateTime.of(2025, 1, 31, 9, 0);
        LocalDateTime plusMonth = original.plusMonths(1);
        LocalDateTime nextYear = original.plus(Period.ofYears(1));
        LocalDateTime changedHour = original.withHour(18);

        System.out.println("original=" + original);
        System.out.println("plus-month=" + plusMonth);
        System.out.println("next-year=" + nextYear);
        System.out.println("changed-hour=" + changedHour);
    }
}
original=2025-01-31T09:00
plus-month=2025-02-28T09:00
next-year=2026-01-31T09:00
changed-hour=2025-01-31T18:00

1월 31일에 한 달을 더하면 대상 월의 유효한 마지막 날로 조정됩니다.

“30일 뒤”가 필요하면 plusDays(30)이고 “다음 달 같은 청구일”은 별도 청구 정책이 필요합니다.

메서드 이름만 비슷하다고 업무 의미가 같지 않습니다.

같은 원본에서 상대 이동과 필드 교체를 비교한다

DateTimeManipulation의 같은 원본에서 계산한 세 반환값과 변경되지 않은 원본을 비교한다.

같은 원본에서 이동하거나 필드를 교체한 새 값을 만듭니다

DateTimeManipulation의 세 연산은 모두 original = 2025-01-31T09:00에서 각각 시작합니다. 앞 연산의 결과에 이어 더하는 체인이 아닙니다.

같은 원본에서 이동하거나 필드를 교체한 새 값을 만듭니다 같은 original2025-01-31 09에서1month02-28 09/1year2026-01-31 09/withHour18동일18로 각각 계산. original은 바뀌지 않는다. 같은 original 2025-01-31 / 09:00 plusMonths(1) 2025-02-28 / 09:00 plus(Period 1년) 2026-01-31 / 09:00 withHour(18) 2025-01-31 / 18:00 각 연산은 같은 원본에서 시작 · 한 달은 고정30일이 아님
같은 원본에서 이동하거나 필드를 교체한 새 값을 만듭니다 같은 original2025-01-31 09에서1month02-28 09/1year2026-01-31 09/withHour18동일18로 각각 계산. original은 바뀌지 않는다. 같은 original 2025-01-31 / 09:00 plusMonths(1) 2025-02-28 / 09:00 plus(Period 1년) 2026-01-31 / 09:00 withHour(18) 2025-01-31 / 18:00 original은 원래 값 유지 각 반환값은 독립 계산 결과
같은 원본에서 상대 이동과 필드 교체를 비교한다
조회·연산원문 프로그램 출력값의미
original2025-01-31T09:00원본은 그대로
original.plusMonths(1)2025-02-28T09:00한 달 이동 후 유효한 월말로 조정
original.plus(Period.ofYears(1))2026-01-31T09:00같은 원본에서 1년 이동
original.withHour(18)2025-01-31T18:00시 필드를 18로 교체
original
원문 프로그램 출력값: 2025-01-31T09:00
의미: 원본은 그대로
original.plusMonths(1)
원문 프로그램 출력값: 2025-02-28T09:00
의미: 한 달 이동 후 유효한 월말로 조정
original.plus(Period.ofYears(1))
원문 프로그램 출력값: 2026-01-31T09:00
의미: 같은 원본에서 1년 이동
original.withHour(18)
원문 프로그램 출력값: 2025-01-31T18:00
의미: 시 필드를 18로 교체

plusMonths(1)은 고정 30일을 더하는 연산이 아닙니다. 반환값을 받아야 변경된 값을 사용할 수 있으며, 별도 청구일 정책까지 이 연산이 결정하지는 않습니다.


달력 규칙과 TemporalAdjusters

마지막 날, 다음 월요일, 이번 달 마지막 금요일 같은 규칙은 직접 반복 계산하지 않고 표준 adjuster를 사용할 수 있습니다.

src/BoardDateAdjusters.java
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public final class BoardDateAdjusters {
    public static void main(String[] args) {
        LocalDate base = LocalDate.of(2025, 3, 10);
        LocalDate endOfMonth = base.with(TemporalAdjusters.lastDayOfMonth());
        LocalDate nextMonday = base.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
        LocalDate lastFriday = base.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));

        System.out.println("end=" + endOfMonth);
        System.out.println("next-monday=" + nextMonday);
        System.out.println("last-friday=" + lastFriday);
    }
}
end=2025-03-31
next-monday=2025-03-17
last-friday=2025-03-28

next는 기준일이 이미 월요일이어도 다음 주 월요일을 반환하고, nextOrSame은 기준일을 그대로 허용합니다.

일정 요구 문구와 일치하는 adjuster를 선택합니다.

다음 월요일은 기준일 포함 여부에 따라 달라진다

2025년3월10일 월요일을 기준으로 한 달력조정 결과와 nextOrSame의 기준일 포함 경계를 비교한다.

next는 기준일을 제외하고 nextOrSame은 포함합니다

기준일은 2025-03-10, 이미 월요일입니다.

next는 기준일을 제외하고 nextOrSame은 포함합니다 월요일03-10의 같은 값에서 next03-17/nextOrSame03-10/월말03-31/마지막금요일03-28을 각각 계산. 기준일 포함 여부를 구별한다. 기준: 월요일 2025-03-10 next(MONDAY) 2025-03-17 nextOrSame 2025-03-10 lastDayOfMonth 2025-03-31 lastInMonth 2025-03-28 / 금요일 화살표는 같은 기준값에서 시작 · 반환값을 연쇄 적용하지 않음
next는 기준일을 제외하고 nextOrSame은 포함합니다 월요일03-10의 같은 값에서 next03-17/nextOrSame03-10/월말03-31/마지막금요일03-28을 각각 계산. 기준일 포함 여부를 구별한다. 기준: 월요일 2025-03-10 next(MONDAY) 2025-03-17 nextOrSame 2025-03-10 lastDayOfMonth 2025-03-31 lastInMonth 2025-03-28 / 금요일 각 화살표는 같은 기준에서 독립적으로 계산 next는 기준일을 제외
다음 월요일은 기준일 포함 여부에 따라 달라진다
달력 규칙반환 날짜기준일과의 관계
lastDayOfMonth()2025-03-31같은 달의 마지막 날
next(MONDAY)2025-03-17기준일을 제외한 다음 월요일
lastInMonth(FRIDAY)2025-03-28같은 달의 마지막 금요일
nextOrSame(MONDAY)2025-03-10기준일도 허용하는 경계 비교
lastDayOfMonth()
반환 날짜: 2025-03-31
기준일과의 관계: 같은 달의 마지막 날
next(MONDAY)
반환 날짜: 2025-03-17
기준일과의 관계: 기준일을 제외한 다음 월요일
lastInMonth(FRIDAY)
반환 날짜: 2025-03-28
기준일과의 관계: 같은 달의 마지막 금요일
nextOrSame(MONDAY)
반환 날짜: 2025-03-10
기준일과의 관계: 기준일도 허용하는 경계 비교

앞의 세 항목은 BoardDateAdjusters의 출력입니다. 마지막 항목은 본문의 ‘이미 월요일이면 유지’ 설명을 같은 기준일에 적용한 추가 경계 비교입니다. 연습 코드의 입력은 별도로 2025-03-12입니다.


문자 경계와 DateTimeFormatter

ISO_LOCAL_DATE 같은 표준 formatter를 우선하고, 업무 형식이 필요할 때 패턴을 만듭니다.

MM은 월이고 mm은 분이며, HH는 24시간제 시간입니다.

src/BoardDateFormatting.java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public final class BoardDateFormatting {
    public static void main(String[] args) {
        DateTimeFormatter input = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH:mm", Locale.KOREAN);
        DateTimeFormatter output = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

        LocalDateTime parsed = LocalDateTime.parse("2025년 03월 10일 14:25", input);
        String formatted = parsed.format(output);

        System.out.println("parsed=" + parsed);
        System.out.println("formatted=" + formatted);
    }
}
parsed=2025-03-10T14:25
formatted=2025-03-10 14:25

입력의 구분자가 맞는 것과 달력 날짜가 유효한 것은 두 검사입니다. DateTimeFormatter의 기본 해석 정책은 SMART이며, ResolverStyle은 달력 필드 조합의 해석을 정합니다. 존재하지 않는 날짜를 거부하려는 입력 경계는 STRICT를 명시합니다.

예시

src/StrictDateInput.java
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;

public final class StrictDateInput {
    public static void main(String[] args) {
        DateTimeFormatter smart = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter strict = DateTimeFormatter.ofPattern("uuuu-MM-dd")
                .withResolverStyle(ResolverStyle.STRICT);
        System.out.println("smart=" + LocalDate.parse("2025-02-30", smart));
        try {
            LocalDate.parse("2025-02-30", strict);
        } catch (DateTimeParseException error) {
            System.out.println("strict=rejected");
        }
        System.out.println("leap=" + LocalDate.parse("2024-02-29", strict));
    }
}
smart=2025-02-28
strict=rejected
leap=2024-02-29

SMART는 이 입력의 30일을 2월 마지막 날로 조정합니다. STRICT는 조합을 거부합니다. u는 연도, y는 연호 내 연도이므로 여기서는 연호 없이 연·월·일을 엄격히 읽도록 uuuu를 사용합니다. 원 예제의 yyyy 출력 패턴을 무조건 잘못된 패턴이라고 볼 필요는 없으며 입력 해석의 목적에 맞춰 선택합니다.

답안을 바꾸기 전에 STRICT에 2025-02-29를 넣으면 어떻게 되는지 예상해 보세요. 2025년은 평년이므로 거부되고, 위 2024년 입력은 윤년이라 허용됩니다. 출력 모양이 같아도 날짜 규칙을 별도로 확인해야 하는 이유입니다.

formatter는 불변이며 공유해도 안전합니다.

패턴 문자열을 매 호출마다 만들 필요 없이 상수로 재사용할 수 있습니다.

외부 프로토콜은 사람이 읽는 지역 형식보다 ISO 표준 형식을 우선합니다.

파싱의 입력 계약과 출력 형식을 분리한다

문자열 parse→LocalDateTime→format의 실제 변환과 별도 SMART/STRICT 입력 판정을 보여 줍니다. 정상 왕복 단계와 별도 거부 지점을 분리해 타입과 출력 변화를 따라갈 수 있습니다.

파싱의 입력 계약과 출력 형식을 분리한다

패턴 구분자 불일치 실패와 정상 한국어 날짜 파싱, 출력 포맷을 서로 다른 단계의 계약으로 비교한다.

parse는 입력을 읽고, format은 출력 형식을 정합니다

parse는 문자열을 값으로 읽고, format은 값을 문자열로 표현합니다.

parse는 입력을 읽고, format은 출력 형식을 정합니다 원 입력2025년03월10일14:25→LocalDateTime→출력2025-03-10 14:25. 별도02-30 입력은 SMART02-28/STRICT예외로 형식과 달력 유효성 구분. 입력 문자열 2025년 03월 10일 14:25 parse의 값 LocalDateTime 2025-03-10 / 14:25 format의 문자열 2025-03-10 14:25 별도 STRICT 입력 · 패턴 일치와 달력 유효성은 별도 검사 2025-02-30 SMART: 2025-02-28 같은 입력 STRICT: 예외 DateTimeParseException 화살표는 parse/format 실행 · 아래 결과는 별도 입력 해석
parse는 입력을 읽고, format은 출력 형식을 정합니다 원 입력2025년03월10일14:25→LocalDateTime→출력2025-03-10 14:25. 별도02-30 입력은 SMART02-28/STRICT예외로 형식과 달력 유효성 구분. 입력 문자열 2025년 03월 10일 14:25 parse의 값 LocalDateTime 2025-03-10 / 14:25 format의 문자열 2025-03-10 14:25 입력 패턴 출력 패턴 별도 STRICT 입력 2025-02-30 SMART: 02-28 STRICT: 예외 DateTimeParseException 구분자 일치와 달력 검사는 별도 각 입력 예제는 독립
파싱의 입력 계약과 출력 형식을 분리한다
경계·원본입력과 형식관찰 결과
실패 입력 · DatePatternMismatchFailure문자열 2025.03.10

패턴 yyyy-MM-dd

점과 하이픈이 달라 · DateTimeParseException
정상 입력 · BoardDateFormatting2025년 03월 10일 14:25

yyyy년 MM월 dd일 HH:mm

한국어 로케일

2025-03-10T14:25
출력 표현 · 같은 정상 프로그램파싱한 값에 적용

yyyy-MM-dd HH:mm

2025-03-10 14:25
실패 입력 · DatePatternMismatchFailure
입력과 형식: 문자열 2025.03.10 · 패턴 yyyy-MM-dd
관찰 결과: 점과 하이픈이 달라 · DateTimeParseException
정상 입력 · BoardDateFormatting
입력: 2025년 03월 10일 14:25
패턴: yyyy년 MM월 dd일 HH:mm
로케일: 한국어
관찰 결과: 2025-03-10T14:25
출력 표현 · 같은 정상 프로그램
입력과 형식: 파싱한 값에 · yyyy-MM-dd HH:mm 적용
관찰 결과: 2025-03-10 14:25

MM은 월, mm은 분입니다. ofPattern의 기본 날짜 해석은 SMART이므로, 구분자 실패 한 사례가 모든 잘못된 달력 날짜의 엄격한 거부를 증명하지는 않습니다.

이 예의 조건

  • 02-30은 정상 왕복과 독립 입력이다.
관계도의 보충 설명
원 도표의 전체 관계·라벨·조건
구분내용
2원래 입력 2025년 03월 10일 14:25→LocalDateTime→출력 2025-03-10 14:25. 별도 02-30 입력은 SMART의 02-28/STRICT의 예외로 형식과 달력 유효성을 구분.
82025-03-10
원 도표의 설명과 주석
구분내용
설명원래 입력 2025년 03월 10일 14:25→LocalDateTime→출력 2025-03-10 14:25. 별도 02-30 입력은 SMART의 02-28/STRICT의 예외로 형식과 달력 유효성을 구분.
전체 읽기 정보파싱의 입력 계약과 출력 형식을 분리한다 파싱의 입력 계약과 출력 형식을 분리한다 패턴 구분자 불일치 실패와 정상 한국어 날짜 파싱, 출력 포맷을 서로 다른 단계의 계약으로 비교한다. parse는 입력을 읽고, format은 출력 형식을 정합니다 parse 는 문자열을 값으로 읽고, format 은 값을 문자열로 표현합니다. 파싱의 입력 계약과 출력 형식을 분리한다 경계·원본 입력과 형식 관찰 결과 실패 입력 · DatePatternMismatchFailure 문자열 2025.03.10 패턴 yyyy-MM-dd 점과 하이픈이 달라 · DateTimeParseException 정상 입력 · BoardDateFormatting 2025년 03월 10일 14:25 yyyy년 MM월 dd일 HH:mm 한국어 로케일 2025-03-10T14:25 출력 표현 · 같은 정상 프로그램 파싱한 값에 적용 yyyy-MM-dd HH:mm 2025-03-10 14:25 실패 입력 · DatePatternMismatchFailure 입력과 형식: 문자열 2025.03.10 · 패턴 yyyy-MM-dd 관찰 결과: 점과 하이픈이 달라 · DateTimeParseException 정상 입력 · BoardDateFormatting 입력: 2025년 03월 10일 14:25 패턴: yyyy년 MM월 dd일 HH:mm 로케일: 한국어 관찰 결과: 2025-03-10T14:25 출력 표현 · 같은 정상 프로그램 입력과 형식: 파싱한 값에 · yyyy-MM-dd HH:mm 적용 관찰 결과: 2025-03-10 14:25 MM 은 월, mm 은 분입니다. ofPattern 의 기본 날짜 해석은 SMART 이므로, 구분자 실패 한 사례가 모든 잘못된 달력 날짜의 엄격한 거부를 증명하지는 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
문자 입력

원 형식의 날짜/시각을 받습니다.

  • 문자열: 2025년 03월 10일 14:25
  • 입력 formatter가 문자열 형식을 해석한다.
parse 값

LocalDateTime을 만들며 별도 달력 오류도 판정합니다.

  • 정상 값: 2025-03-10 14:25
  • 02-30 SMART: 2025-02-28
  • 02-30 STRICT: DateTimeParseException
  • 형식 일치만으로 달력 유효성을 보장하지 않는다.
format 출력

정상 값을 다른 형식의 문자열로 만듭니다.

  • 출력: 2025-03-10 14:25
  • 값 타입: LocalDateTime 유지
  • format은 저장 값이 아닌 외부 표현을 정한다.

파싱의 입력 계약과 출력 형식을 분리한다

문자열 parse→LocalDateTime→format의 실제 변환과 별도 SMART/STRICT 입력 판정을 보여 줍니다. 정상 왕복 단계와 별도 거부 지점을 분리해 타입과 출력 변화를 따라갈 수 있습니다.

파싱의 입력 계약과 출력 형식을 분리한다

패턴 구분자 불일치 실패와 정상 한국어 날짜 파싱, 출력 포맷을 서로 다른 단계의 계약으로 비교한다.

parse는 입력을 읽고, format은 출력 형식을 정합니다

parse는 문자열을 값으로 읽고, format은 값을 문자열로 표현합니다.

parse는 입력을 읽고, format은 출력 형식을 정합니다 원 입력2025년03월10일14:25→LocalDateTime→출력2025-03-10 14:25. 별도02-30 입력은 SMART02-28/STRICT예외로 형식과 달력 유효성 구분. 입력 문자열 2025년 03월 10일 14:25 parse의 값 LocalDateTime 2025-03-10 / 14:25 format의 문자열 2025-03-10 14:25 별도 STRICT 입력 · 패턴 일치와 달력 유효성은 별도 검사 2025-02-30 SMART: 2025-02-28 같은 입력 STRICT: 예외 DateTimeParseException 화살표는 parse/format 실행 · 아래 결과는 별도 입력 해석
parse는 입력을 읽고, format은 출력 형식을 정합니다 원 입력2025년03월10일14:25→LocalDateTime→출력2025-03-10 14:25. 별도02-30 입력은 SMART02-28/STRICT예외로 형식과 달력 유효성 구분. 입력 문자열 2025년 03월 10일 14:25 parse의 값 LocalDateTime 2025-03-10 / 14:25 format의 문자열 2025-03-10 14:25 입력 패턴 출력 패턴 별도 STRICT 입력 2025-02-30 SMART: 02-28 STRICT: 예외 DateTimeParseException 구분자 일치와 달력 검사는 별도 각 입력 예제는 독립
파싱의 입력 계약과 출력 형식을 분리한다
경계·원본입력과 형식관찰 결과
실패 입력 · DatePatternMismatchFailure문자열 2025.03.10

패턴 yyyy-MM-dd

점과 하이픈이 달라 · DateTimeParseException
정상 입력 · BoardDateFormatting2025년 03월 10일 14:25

yyyy년 MM월 dd일 HH:mm

한국어 로케일

2025-03-10T14:25
출력 표현 · 같은 정상 프로그램파싱한 값에 적용

yyyy-MM-dd HH:mm

2025-03-10 14:25
실패 입력 · DatePatternMismatchFailure
입력과 형식: 문자열 2025.03.10 · 패턴 yyyy-MM-dd
관찰 결과: 점과 하이픈이 달라 · DateTimeParseException
정상 입력 · BoardDateFormatting
입력: 2025년 03월 10일 14:25
패턴: yyyy년 MM월 dd일 HH:mm
로케일: 한국어
관찰 결과: 2025-03-10T14:25
출력 표현 · 같은 정상 프로그램
입력과 형식: 파싱한 값에 · yyyy-MM-dd HH:mm 적용
관찰 결과: 2025-03-10 14:25

MM은 월, mm은 분입니다. ofPattern의 기본 날짜 해석은 SMART이므로, 구분자 실패 한 사례가 모든 잘못된 달력 날짜의 엄격한 거부를 증명하지는 않습니다.

이 예의 조건

  • 02-30은 정상 왕복과 독립 입력이다.

관계도의 보충 설명

원 도표의 전체 관계·라벨·조건
구분내용
2원래 입력 2025년 03월 10일 14:25→LocalDateTime→출력 2025-03-10 14:25. 별도 02-30 입력은 SMART의 02-28/STRICT의 예외로 형식과 달력 유효성을 구분.
82025-03-10
원 도표의 설명과 주석
구분내용
설명원래 입력 2025년 03월 10일 14:25→LocalDateTime→출력 2025-03-10 14:25. 별도 02-30 입력은 SMART의 02-28/STRICT의 예외로 형식과 달력 유효성을 구분.
전체 읽기 정보파싱의 입력 계약과 출력 형식을 분리한다 파싱의 입력 계약과 출력 형식을 분리한다 패턴 구분자 불일치 실패와 정상 한국어 날짜 파싱, 출력 포맷을 서로 다른 단계의 계약으로 비교한다. parse는 입력을 읽고, format은 출력 형식을 정합니다 parse 는 문자열을 값으로 읽고, format 은 값을 문자열로 표현합니다. 파싱의 입력 계약과 출력 형식을 분리한다 경계·원본 입력과 형식 관찰 결과 실패 입력 · DatePatternMismatchFailure 문자열 2025.03.10 패턴 yyyy-MM-dd 점과 하이픈이 달라 · DateTimeParseException 정상 입력 · BoardDateFormatting 2025년 03월 10일 14:25 yyyy년 MM월 dd일 HH:mm 한국어 로케일 2025-03-10T14:25 출력 표현 · 같은 정상 프로그램 파싱한 값에 적용 yyyy-MM-dd HH:mm 2025-03-10 14:25 실패 입력 · DatePatternMismatchFailure 입력과 형식: 문자열 2025.03.10 · 패턴 yyyy-MM-dd 관찰 결과: 점과 하이픈이 달라 · DateTimeParseException 정상 입력 · BoardDateFormatting 입력: 2025년 03월 10일 14:25 패턴: yyyy년 MM월 dd일 HH:mm 로케일: 한국어 관찰 결과: 2025-03-10T14:25 출력 표현 · 같은 정상 프로그램 입력과 형식: 파싱한 값에 · yyyy-MM-dd HH:mm 적용 관찰 결과: 2025-03-10 14:25 MM 은 월, mm 은 분입니다. ofPattern 의 기본 날짜 해석은 SMART 이므로, 구분자 실패 한 사례가 모든 잘못된 달력 날짜의 엄격한 거부를 증명하지는 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
문자 입력

원 형식의 날짜/시각을 받습니다.

  • 문자열: 2025년 03월 10일 14:25
  • 입력 formatter가 문자열 형식을 해석한다.
parse 값

LocalDateTime을 만들며 별도 달력 오류도 판정합니다.

  • 정상 값: 2025-03-10 14:25
  • 02-30 SMART: 2025-02-28
  • 02-30 STRICT: DateTimeParseException
  • 형식 일치만으로 달력 유효성을 보장하지 않는다.
format 출력

정상 값을 다른 형식의 문자열로 만듭니다.

  • 출력: 2025-03-10 14:25
  • 값 타입: LocalDateTime 유지
  • format은 저장 값이 아닌 외부 표현을 정한다.

날짜 API 선택 기준

  • 고정 필드 조회는 편의 getter
  • 동적 필드 도구는 TemporalField와 isSupported
  • 상대적 이동은 plus/minus
  • 특정 필드 교체는 withX
  • 달력 규칙은 TemporalAdjusters
  • 문자열→시간은 parse
  • 시간→문자열은 format

불변 반환값을 변수에 받지 않는 실수를 조심하고, 월말과 DST 같은 경계 입력을 반드시 예제에 포함합니다.


연습 문제

입력 날짜가 월요일이면 그대로, 아니면 다음 월요일을 선택하고 yyyy/MM/dd E 형식으로 출력하세요.

해설 보기
src/NextBoardMondayExercise.java
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;

public final class NextBoardMondayExercise {
    public static void main(String[] args) {
        LocalDate input = LocalDate.of(2025, 3, 12);
        LocalDate monday = input.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd E", Locale.ENGLISH);
        System.out.println(monday.format(formatter));
    }
}
2025/03/17 Mon

nextOrSame이 “이미 월요일이면 유지”라는 요구를 직접 표현합니다.

직접 요일 숫자를 빼는 계산보다 월 경계를 안전하게 넘습니다.