DateComponents — 캘린더 구성 요소 및 NSCalendar란

저자: IT Sectr 게시일: 2026-07-12 읽는 시간: 7 분

DateComponents는 캘린더 날짜 구성 요소를 별도의 필드(년, 월, 일, 시, 분, 초 등)로 저장하는 Foundation 구조체입니다. 절대적인 시간 지점을 나타내는 Date와 달리 DateComponents는 캘린더 및 시간대에 의존하는 사람이 읽을 수 있는 값을 포함합니다. Apple Developer Documentation(2025)에 따르면 DateComponents는 Date와 Calendar 사이의 중간 연결고리로 사용되며, 이를 통해 수동 연산 없이 캘린더 날짜를 추출 및 구성하고 계산과 날짜 이동을 수행합니다.

핵심 사항

  • DateComponents — 날짜 구성 요소(년, 월, 일)를 옵셔널 정수 필드로 저장하는 구조체.
  • Calendar.dateComponents — 시간대를 고려하여 Date에서 지정된 구성 요소를 추출하는 메서드.
  • Calendar.date(from:) — 누락된 필드를 자동 완성하여 DateComponents를 Date로 역변환.
  • 옵셔널 필드 — DateComponents의 각 필드는 nil이 될 수 있어 부분적 날짜 지정 가능.
  • Range 및 구성 요소 — DateComponents는 Calendar에서 날짜 간 차이 계산 및 범위 내 날짜 검색에 사용.

DateComponents란?

DateComponents는 캘린더 시간 구성 요소를 저장하도록 설계된 Foundation 값 유형입니다. 각 구성 요소는 옵셔널 Int 필드로 표현됩니다: year, month, day, hour, minute, second, nanosecond, weekday, weekOfMonth, weekOfYear, quarter, yearForWeekOfYear 등.

Date와의 주요 차이점은 캘린더 바인딩입니다. Date는 절대 시간(기준 날짜로부터의 초 수)을 저장하는 반면, DateComponents는 특정 Calendar의 컨텍스트에서만 의미가 있는 사람이 읽을 수 있는 표현입니다. 동일한 Date가 다른 캘린더 및 시간대에서 다른 DateComponents로 표현될 수 있습니다.

DateComponents는 독립적인 시간 유형이 아니라 데이터 컨테이너입니다. DateComponents를 날짜로 해석하려면 구성 요소가 캘린더 시스템과 어떻게 관련되는지 이해하는 Calendar가 필요합니다. Calendar.dateComponents(from: Date)는 구성 요소 추출을 수행하고, Calendar.date(from: DateComponents)는 역조립을 수행합니다.

필드의 옵셔널성

DateComponents의 각 필드는 옵셔널(Int?)이며, 이는 부분적 날짜 작업에 기본적입니다. 년과 월만 지정된 경우 Calendar는 누락된 필드를 기본값으로 채웁니다: 일 = 1, 시 = 0, 분 = 0. 이는 기간 시작 날짜 생성에 편리합니다 — 관련 구성 요소만 지정하면 됩니다.

== 연산자로 DateComponents를 비교할 때는 지정된(nil이 아닌) 필드만 비교됩니다. 둘 다 연도가 2026이지만 월이 다른 두 DateComponents 구조체는 다른 것으로 간주됩니다. NSObjectProtocol의 isEqual은 DateComponents에 적용되지 않습니다 — DateComponents는 NSObject를 상속하지 않습니다.

날짜 구성 요소: 년, 월, 일

주요 필드 DateComponents에는 year, month, day, hour, minute, second, nanosecond가 포함됩니다. 각 필드는 해당 단위의 숫자 값을 저장합니다: 년 — 2026, 월 — 1..12, 일 — 1..31, 시 — 0..23, 분 — 0..59, 초 — 0..59. 나노초는 0에서 999999999까지의 범위입니다.

주 필드 — weekday(1..7, 그레고리력에서 1 = 일요일), weekOfMonth, weekOfYear. 이러한 필드는 Calendar에 의존하며 해당 컨텍스트 외부에서는 의미가 없습니다. weekday는 캘린더의 firstWeekday 설정에 따라 달라집니다: 러시아 로케일에서는 주가 월요일에 시작되고(그레고리력 시스템에서 weekday = 2), 미국 로케일에서는 일요일에 시작됩니다(weekday = 1).

특수 필드 — quarter(1..4), yearForWeekOfYear(주가 속한 연도), isLeapMonth(히브리어 또는 중국어 캘린더의 윤월에 대한 부울 플래그). calendar 및 timeZone 필드는 구조체가 생성된 해당 객체에 대한 참조를 저장합니다.

카테고리필드범위
캘린더year, month, day1..∞, 1..12, 1..31
시간hour, minute, second, nanosecond0..23, 0..59, 0..59, 0..999999999
weekday, weekOfMonth, weekOfYear1..7, 1..5, 1..53
특수quarter, yearForWeekOfYear1..4, 종속적

Calendar.dateComponents를 통해 구성 요소를 추출할 때는 성능을 위해 필요한 필드만 요청하는 것이 중요합니다. Calendar는 요청된 모든 필드를 단일 패스로 추출합니다 — 이는 각 필드에 대해 개별적으로 Calendar.component를 호출하는 것보다 훨씬 빠릅니다.

DateComponents 생성

DateComponents 초기화 — 가장 간단한 방법: 빈 구조체를 만들고 필요한 필드를 채웁니다. 지정되지 않은 모든 필드는 자동으로 nil을 받습니다. 부분적 구성 요소로 생성된 날짜는 초기화 시 검증되지 않습니다 — 오류는 Calendar를 통해 Date로 변환할 때만 발생할 수 있습니다.

초기화자 DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…)는 단일 호출로 모든 필드를 설정할 수 있습니다. 이 초기화자는 준비된 값으로 완전한 날짜를 생성하는 데 편리하지만, 가독성 때문에 5-6개 이상의 인수와 함께 사용되는 경우는 드뭅니다.

Calendar.dateComponents(_:from:) — 기존 Date에서 DateComponents를 얻는 기본 방법입니다. 두 번째 인수는 추출할 구성 요소의 집합입니다. Calendar는 시간대를 고려하여 캘린더 계산을 수행하고 요청된 필드만 있는 구조체를 반환하며, 나머지 필드는 nil로 남습니다.

swift
import Foundation

// 필드 초기화자로 생성
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21

// Date에서 추출
let now = Date()
let extracted = Calendar.current.dateComponents(
    [.year, .month, .day],
    from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")

// 확장 초기화자로 생성
let birthday = DateComponents(
    calendar: Calendar.current,
    year: 1990, month: 5, day: 15
)

필드를 수동으로 설정하여 DateComponents를 생성할 때는 Date로 변환하기 전에 항상 Calendar를 확인하세요. date(from:) 변환 시 구성 요소가 존재하지 않는 날짜(예: 2월 31일, 윤년이 아닌 해의 2월 30일)를 형성하는 경우 Calendar가 nil을 반환할 수 있습니다. 날짜 검증은 Calendar의 책임이며 DateComponents의 책임이 아닙니다.

DateComponents를 Date로 변환

Calendar.date(from:) — DateComponents를 Date로 변환하는 기본 메서드입니다. Calendar는 자체 캘린더와 시간대에 따라 구성 요소를 해석합니다. 일부 필드가 설정되지 않은(nil) 경우 Calendar는 기본값을 사용합니다: 일 = 1, 시 = 0, 분 = 0, 초 = 0.

메서드는 옵셔널 Date를 반환합니다 — 구성 요소가 서로 모순되거나 유효하지 않은 날짜를 형성하는 경우 nil이 발생합니다. nil의 일반적인 원인: 존재하지 않는 날짜(1월 32일, 2023년 2월 29일), 모순되는 필드(동일 세트의 weekday=1, day=5), 주어진 캘린더에 불가능한 연도(그레고리력의 연도 0).

timeZone이 있는 DateComponents — DateComponents에 timeZone이 포함된 경우 Calendar는 변환 시 이를 사용합니다. timeZone이 지정되지 않은 경우 Calendar는 자체 현재 timeZone을 사용합니다. Calendar.timeZone이 날짜의 예상 시간대와 일치하지 않으면 결과가 몇 시간 차이가 날 수 있습니다 — timeZone이 객체 중 하나에 명시적으로 설정되어 있는지 확인하세요.

swift
let calendar = Calendar(identifier: .gregorian)

// DateComponents에서 Date 생성
var comps = DateComponents()
comps.year = 2026
comps.month = 12
comps.day = 25
comps.hour = 10

if let date = calendar.date(from: comps) {
    print("Christmas: \(date)")
}

// timeZone 지정하여 생성
calendar.timeZone = TimeZone(identifier: "UTC")!
let utcComps = DateComponents(
    calendar: calendar, year: 2026, month: 7, day: 21,
    hour: 12
)
let utcDate = calendar.date(from: utcComps)!

Calendar.dateComponents를 사용한 날짜 차이 — DateComponents의 또 다른 사용 사례입니다. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate)는 두 날짜 사이의 년, 월, 일 차이를 반환합니다. 이는 TimeInterval을 1년의 초 수로 나누는 대신 나이를 계산하는 올바른 방법입니다. Calendar가 윤년을 고려하기 때문입니다.

Calendar와 DateComponents

Calendar — DateComponents와 함께 작동하는 중앙 클래스입니다. 날짜 추출, 조립 및 비교의 모든 작업은 Calendar를 통해 이루어집니다. Calendar 없이 DateComponents는 시간적 의미가 없는 단순한 숫자 집합입니다. Calendar는 구성 요소에 해석을 부여합니다: 월 2가 2월이고 weekday 2가 월요일임을 결정합니다.

Calendar.nextDateCalendar.enumerateDates — DateComponents에 기반한 두 메서드입니다. nextDate(after: Date(), matching: DateComponents)는 지정된 구성 요소와 일치하는 다음 날짜를 찾습니다 — 예: 오늘 이후의 다음 월요일. enumerateDates(startingAfter:matching:matchingPolicy:using:)는 지정된 한도까지 패턴과 일치하는 모든 날짜를 반복합니다.

Calendar.dateInterval — 지정된 구성 요소에 대한 DateInterval을 반환하는 메서드입니다. dateInterval(of: .month, for: Date())는 현재 월의 시작과 끝을 반환합니다. 내부적으로 이 메서드는 DateComponents를 사용하여 기간 경계를 찾습니다: 월의 첫째 날과 마지막 날로 DateComponents를 생성하고 Calendar를 통해 Date로 변환합니다.

swift
let calendar = Calendar.current

// 다음 월요일
let nextMonday = calendar.nextDate(
    after: Date(),
    matching: DateComponents(weekday: 2),
    matchingPolicy: .nextTime
)!

// 일 단위 날짜 차이
let diff = calendar.dateComponents(
    [.day], from: Date(), to: nextMonday
)

// 월 범위
let monthInterval = calendar.dateInterval(
    of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end

MatchingPolicy — DateComponents 작업 시 Calendar 메서드의 중요한 매개변수입니다. strictPolicy는 모든 구성 요소의 정확한 일치를 요구하고, nextTimePolicy는 다음 시간적 일치를 선택하며, nextTimePreservingSmallerComponents는 원본 날짜의 더 작은 구성 요소(분, 초)를 보존합니다. 정책 선택은 날짜 검색 결과에 영향을 미치며, 특히 일광 절약 시간 전환을 통한 이동 시 영향을 미칩니다.

DateComponents 예제

애플리케이션에서 DateComponents의 실제 사용 사례를 살펴보겠습니다. 각 예제는 iOS 개발자가 캘린더 날짜 작업 시 직면하는 일반적인 작업을 보여줍니다.

매월 1일 알림

Calendar.nextDate와 DateComponents(day: 1)로 다음 달의 첫 날을 찾습니다. Calendar는 자동으로 현재 월의 일 수를 결정하고 다음 달로 이동합니다. 반복 알림의 경우 Calendar 키와 함께 enumerateDates 또는 Combine.Timer를 사용하세요.

swift
func firstDayOfNextMonth(from date: Date) -> Date {
    let calendar = Calendar.current
    let comps = DateComponents(day: 1)
    return calendar.nextDate(
        after: date,
        matching: comps,
        matchingPolicy: .nextTime
    )!
}

// 년 단위 나이 계산
func ageInYears(from birthDate: Date) -> Int {
    let calendar = Calendar.current
    let ageComponents = calendar.dateComponents(
        [.year], from: birthDate, to: Date()
    )
    return ageComponents.year ?? 0
}

// 년과 월별 이벤트 그룹화
func groupEventsByMonth(_ events: [Event]) -> [String: [Event]] {
    let calendar = Calendar.current
    return Dictionary(grouping: events) { event in
        let comps = calendar.dateComponents(
            [.year, .month], from: event.date
        )
        return "\(comps.year!)-\(comps.month!)"
    }
}

나이 계산 — Calendar.dateComponents([.year], from:to:)를 통한 방법이 윤년을 고려하는 유일한 올바른 방법입니다. TimeInterval 기반 계산(초 / 31536000)은 2월 29일에 태어난 사람들에게 오류를 발생시킵니다. Calendar는 현재 연도에 생일이 있었는지 여부를 올바르게 판단하고 정확한 나이를 반환합니다.

년과 월별 그룹화 — 기록 또는 캘린더 화면의 일반적인 작업입니다. DateComponents가 그룹화 키 역할을 합니다: 이벤트 날짜에서 년과 월을 추출하고, 문자열 키를 형성하고, Dictionary(grouping:)을 통해 그룹화합니다. 표시를 위해 지역화된 월 이름에는 "LLLL yyyy" 템플릿과 함께 DateFormatter를 사용하세요.

작업Calendar 메서드DateComponents 역할
월의 첫 날nextDate(after:matching:)day: 1
나이 계산dateComponents(from:to:)차이에서 [.year]
날짜 그룹화dateComponents(_:from:)year + month 키
요일 검색nextDate(after:matching:)weekday: N

자주 묻는 질문

Calendar.date(from:)이 DateComponents에 대해 nil을 반환하는 이유는 무엇인가요?

원인: 존재하지 않는 날짜(4월 31일), 모순되는 필드(weekday=1과 day=5), 선택한 캘린더에 유효하지 않은 필드 조합. Calendar는 자체 시스템에서 구성 요소를 해석하려고 시도합니다 — 조합이 불가능한 경우 결과는 nil입니다. 변환 시 항상 guard let 또는 if let을 사용하세요.

DateComponents를 서로 비교할 수 있나요?

, == 연산자를 통해 가능합니다. DateComponents는 Equatable을 구현하며 모든 필드를 비교합니다. 모든 필드가 같으면(nil == nil은 참으로 간주) 두 구조체는 같습니다. 필드의 일부만 비교하려면 — Calendar.dateComponents를 통해 동일한 집합을 추출하세요.

DateComponents와 Date의 차이는 무엇인가요?

Date는 캘린더 바인딩이 없는 절대적인 시간 지점입니다. DateComponents는 Calendar의 컨텍스트에서만 의미가 있는 사람이 읽을 수 있는 숫자(년, 월, 일)의 집합입니다. Date는 비교, 빼기, ISO 8601으로 직렬화가 가능합니다. DateComponents는 캘린더와 상호작용하기 위한 중간 표현입니다.

DateComponents에서 년과 월만 지정하는 방법은?

year와 month 필드만 설정하고 나머지는 nil로 둡니다. Calendar.date(from:)을 통해 Date로 변환할 때 Calendar가 자동으로 일 = 1, 시 = 0, 분 = 0으로 설정합니다. 결과는 지정된 월의 첫 날 자정에 해당하는 Date입니다.

DateComponents는 시간대를 어떻게 처리하나요?

DateComponents는 필드에 시간대 정보를 저장하지 않습니다 — 필드 값(년, 월, 일) 자체가 추출된 timeZone에 따라 달라집니다. "2026년 7월 21일 14:00 MSK" 및 "2026년 7월 21일 10:00 UTC"의 구성 요소는 동일한 Date를 나타내지만 DateComponents 필드는 다릅니다.

요약

  • DateComponents — 캘린더 구성 요소(년, 월, 일, 시)를 옵셔널 Int? 필드로 저장하는 Foundation 구조체.
  • Calendar.dateComponents는 시간대와 캘린더 시스템을 고려하여 Date에서 구성 요소를 추출.
  • Calendar.date(from:)은 누락된 필드에 기본값을 사용하여 DateComponents에서 Date를 조립.
  • 옵셔널 필드를 통해 부분적 날짜 지정 가능 — Calendar가 누락된 값을 채움.
  • Calendar.nextDate는 DateComponents와 일치하는 다음 날짜를 찾음 — 알림 및 반복 이벤트용.
  • 나이 계산은 Calendar.dateComponents([.year], from:to:)를 통해 — 윤년을 고려하는 유일한 올바른 방법.
  • MatchingPolicy는 모든 구성 요소가 일치하지 않을 때 Calendar 동작을 제어 — 날짜 검색의 중요한 매개변수.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기