Calendar란 무엇인가, Date 캘린더 및 Swift에서의 계산

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

Calendar는 캘린더 시스템을 정의하고 날짜 구성 요소 추출, 날짜 간 차이 계산, 기간 경계 찾기 및 날짜 이동을 위한 메서드를 제공하는 Foundation 클래스입니다. 캘린더는 절대 시간(Date)을 사람이 읽을 수 있는 구성 요소와 연결하고 주의 시작, 시간대, 서머타임과 같은 지역별 특성을 고려합니다. Apple Developer Documentation(2025)에 따르면 Foundation은 그레고리력부터 불교력, 일본력까지 17개의 캘린더 시스템을 지원하여 Calendar를 국제화된 애플리케이션을 위한 범용 도구로 만듭니다.

주요 내용

  • Calendar — 캘린더 계산을 위한 Foundation 클래스: 구성 요소 추출, 비교 및 날짜 이동.
  • Calendar.current — 사용자의 시스템 캘린더로, 지역 설정을 자동으로 반영합니다.
  • 17개의 캘린더 시스템 — Foundation은 그레고리력, 불교력, 일본력, 히브리력, 이슬람력 등을 지원합니다.
  • Calendar.dateComponents — 시간대를 고려하여 Date에서 구성 요소(년, 월, 일)를 추출합니다.
  • Calendar.dateInterval — 지정된 기간(일, 주, 월)의 시작일과 종료일을 반환합니다.

Foundation에서 Calendar란?

Calendar는 ICU(International Components for Unicode)를 기반으로 캘린더 계산을 구현하는 Foundation 클래스입니다. 캘린더는 절대 시간(Date)이 년, 월, 일, 시, 분, 초와 같은 캘린더 구성 요소에 매핑되는 방식을 정의합니다. Calendar 없이는 현재 연도, 월, 일을 알 수 없습니다 — Date 자체에는 이 정보가 포함되어 있지 않습니다.

캘린더는 세 가지 매개변수 그룹을 고려합니다: 캘린더 시스템(그레고리력, 불교력, 일본력), 시간대로케일입니다. Calendar.current는 사용자의 시스템 설정에서 이 세 가지를 결합합니다. Calendar.autoupdatingCurrent는 NotificationCenter를 통해 애플리케이션을 다시 시작하지 않고 설정 변경 시 자동으로 업데이트되는 특별한 버전입니다.

Calendar는 Foundation에서 값 타입(value type)입니다. Calendar(identifier:)는 고정 매개변수로 새 인스턴스를 생성합니다. Calendar는 복사 가능하고 ==로 비교 가능하며 딕셔너리 키로 사용할 수 있습니다. 이를 통해 테스트를 위해 특정 timeZone 및 locale 설정으로 캘린더를 만들 수 있습니다.

Calendar와 NSCalendar

Calendar는 Objective-C NSCalendar의 Swift 버전으로, as Calendar / as NSCalendar를 통해 브리지됩니다. 현대 Swift에서는 Calendar가 모든 곳에서 사용됩니다. NSCalendar는 Objective-C API와의 역호환성을 위해 유지됩니다. Calendar는 NS 접두사 없이 완전한 메서드 세트를 갖추고 있으며 type-safe 인수와 Swift 옵셔널을 지원합니다.

스레드 안전성 — Calendar는 읽기에 대해 스레드 안전합니다. 생성된 인스턴스는 여러 스레드에서 안전하게 읽을 수 있습니다. 속성(timeZone, locale) 수정은 스레드 안전하지 않습니다 — 다른 구성에 대해 별도의 Calendar 인스턴스를 만드세요.

Foundation의 캘린더 유형

Foundation은 Calendar.Identifier 열거형을 통해 17개의 캘린더 시스템을 지원합니다. 각 시스템에는 윤년, 월 수 및 시대의 시작에 대한 고유한 규칙이 있습니다. 캘린더 선택은 dateComponents, dateInterval, nextDate 등 모든 계산에 영향을 미칩니다.

주요 캘린더 시스템:

  • .gregorian — 국제 표준, 12개월, 365/366일, 예수 탄생부터 시작하는 시대.
  • .buddhist — 불교력, 태국, 캄보디아, 라오스에서 사용, 시대가 그레고리력보다 543년 앞서 있습니다.
  • .japanese — 일본력, 천황 통치 시대 기준, 그레고리력과 같이 12개월.
  • .hebrew — 히브리력, 12/13개월, 윤월 아다르 II.
  • .islamic — 이슬람력, 12개의 음력 월, 354/355일.
  • .indian — 인도 국정력(사카력), 12개월.

Calendar(identifier: .gregorian) — 가장 일반적으로 사용됩니다. 국제 표준 ISO 8601을 준수하며 대부분의 국가에서 기본 캘린더입니다. 국제 사용자를 대상으로 하는 애플리케이션의 경우 Calendar.current를 사용하세요 — 사용자의 시스템 캘린더와 자동으로 일치합니다.

식별자유형사용 지역
.gregorian태양력국제
.buddhist태양력태국, 캄보디아
.japanese태양력일본
.hebrew태음태양력이스라엘
.islamic태음력이슬람 국가
.chinese태음태양력중국

Calendar와 DateComponents

DateComponentsCalendar는 떼려야 뗄 수 없는 관계입니다. Calendar.dateComponents(_:from:)는 캘린더의 시간대를 고려하여 Date에서 구성 요소를 추출합니다. Calendar.date(from:)는 DateComponents에서 Date를 조합하며 누락된 필드를 기본값(일=1, 시=0, 분=0, 초=0)으로 채웁니다.

Calendar.component 메서드는 단일 구성 요소를 추출하여 빠른 확인에 편리합니다. Calendar.dateComponents는 한 번의 호출로 여러 구성 요소를 추출합니다 — Calendar가 각 구성 요소에 대해 개별적으로 계산하는 대신 한 번만 캘린더 계산을 수행하므로 더 효율적입니다. 3개 이상의 구성 요소 목록에는 항상 dateComponents를 사용하세요.

Calendar.compare는 지정된 정밀도로 두 Date를 비교합니다. toGranularity 매개변수는 구성 요소 정밀도를 결정합니다: .year는 연도만, .month는 연도와 월, .day는 연도, 월, 일을 비교합니다. 시간을 무시하고 두 날짜가 같은 날인지 확인하는 데 유용합니다.

swift
let calendar = Calendar.current
let now = Date()

// 단일 구성 요소 추출
let year = calendar.component(.year, from: now)

// 구성 요소 집합 추출
let comps = calendar.dateComponents(
    [.year, .month, .day], from: now
)

// 일 단위로 비교
let isSameDay = calendar.compare(date1, to: date2,
                                  toGranularity: .day) == .orderedSame

// 날짜가 오늘인지 확인
let isToday = calendar.isDateInToday(someDate)

Calendar.isDateInToday, isDateInTomorrow, isDateInYesterday — 상대적 확인을 위한 메서드입니다. Calendar.isDate(_:inSameDayAs:)는 캘린더의 시간대를 고려하여 두 날짜가 같은 캘린더 일에 속하는지 확인합니다. 이 메서드들은 내부적으로 Calendar.compare를 사용하며 빈번한 호출에 최적화되어 있습니다.

Calendar 계산

Calendar.dateInterval은 분석 및 UI에 가장 유용한 메서드 중 하나입니다. 지정된 구성 요소(일, 주, 월, 년)에 대한 DateInterval을 반환합니다. DateInterval에는 start(Date)와 end(Date) — 기간의 경계가 포함됩니다. 예를 들어, dateInterval(of: .weekOfYear, for: Date())는 현재 주의 월요일 시작과 일요일 종료를 반환합니다.

Calendar.date with byAdding — 날짜를 이동하는 메서드입니다. Calendar.date(byAdding: .day, value: 7, to: Date())는 1주일 후의 날짜를 반환합니다. Calendar.date(byAdding: DateComponents)는 더 유연한 버전으로 여러 구성 요소를 한 번에 이동할 수 있습니다: +1개월 +3일. Calendar는 자동으로 다양한 월 길이와 윤년을 고려합니다.

Calendar.nextDate는 지정된 DateComponents와 일치하는 다음 날짜를 검색합니다. matchingPolicy 매개변수는 불일치 시 동작을 정의합니다: .nextTime — 다음 시간 일치, .nextTimePreservingSmallerComponents — 원래 날짜의 분과 초 유지, .strict — 정확한 일치 필요.

swift
let calendar = Calendar.current
let today = Date()

// 주의 시작과 끝
let weekInterval = calendar.dateInterval(
    of: .weekOfYear, for: today
)!

// 1개월 이동
let nextMonth = calendar.date(
    byAdding: .month, value: 1, to: today
)!

// DateComponents를 통한 이동
var delta = DateComponents()
delta.month = 1
delta.day = 3
let shifted = calendar.date(byAdding: delta, to: today)!

// 다음 13일의 금요일
let friday13Components = DateComponents(
    weekday: 6, day: 13
)
let nextFriday13 = calendar.nextDate(
    after: today, matching: friday13Components,
    matchingPolicy: .nextTime
)

EnumerateDates — 패턴에 따라 날짜를 반복하는 강력한 메서드입니다. Calendar.enumerateDates(startingAfter:matching:matchingPolicy:using:)는 블록이 stop = true를 반환할 때까지 각 일치에 대해 블록을 호출합니다. 캘린더 및 일정에서 반복 이벤트를 생성하는 데 사용됩니다. 이 메서드는 ICU에 의해 최적화되어 nextDate를 사용한 수동 루프보다 효율적입니다.

TimeZone과 Locale

TimeZone은 Calendar의 필수적인 부분입니다. 시간대는 절대 Date가 어느 캘린더 시간에 해당하는지 결정합니다. UTC와 모스크바에서 동일한 Date는 다른 구성 요소를 생성합니다: UTC의 Date()는 10:00을 표시하는 반면 MSK에서는 13:00을 표시합니다. Calendar.timeZone의 기본값은 TimeZone.current입니다.

Locale은 주의 첫 번째 요일, 연도의 첫 번째 주 최소 일수(minDaysInFirstWeek) 및 월/요일 이름(DateFormatter를 통한 변환 시)에 영향을 미칩니다. Calendar.locale의 기본값은 Locale.current입니다. 러시아 로케일에서는 주가 월요일에 시작되고 미국 로케일에서는 일요일에 시작됩니다.

Calendar.availableIdentifiers는 지원되는 모든 캘린더 식별자 목록을 반환합니다. 정적 속성 Calendar.availableCalendarIdentifiers는 동일한 식별자를 가진 문자열 배열입니다. 캘린더 선택 UI를 구축하고 장치에서 특정 캘린더 시스템의 가용성을 확인하는 데 사용됩니다.

swift
// 특정 시간대의 Calendar
var utcCalendar = Calendar(identifier: .gregorian)
utcCalendar.timeZone = TimeZone(identifier: "UTC")!

// 러시아 로케일의 Calendar
var russianCalendar = Calendar(identifier: .gregorian)
russianCalendar.locale = Locale(identifier: "ru_RU")

// 첫 번째 요일은 로케일에 따라 다름
let firstWeekday = russianCalendar.firstWeekday
// 2 = 월요일(ru_RU에서)

// 사용 가능한 캘린더 목록
for identifier in Calendar.availableIdentifiers {
    print(identifier)
}

firstWeekday — 주의 첫 번째 요일을 결정하는 Calendar 속성입니다. 러시아 로케일에서는 Sunday = 2(월요일이 첫 번째)입니다. 미국 로케일에서는 Sunday = 1입니다. 이는 weekOfMonth와 weekOfYear에 영향을 미칩니다: 동일한 날짜가 다른 로케일에서 다른 주 번호에 속할 수 있습니다. 날짜를 다루는 애플리케이션의 경우 Calendar.current를 사용하거나 firstWeekday를 명시적으로 설정하세요.

Calendar 사용 예제

Calendar의 기능을 보여주는 실용적인 시나리오를 살펴보겠습니다. 각 예제는 특정 iOS 개발 작업을 해결하고 캘린더 계산의 올바른 사용법을 보여줍니다.

확인: 날짜가 이번 달인가요?

Calendar.dateInterval(of: .month, for:)는 현재 달의 경계를 반환합니다. Date가 이 간격 내에 있는지 확인하는 것이 날짜가 현재 달에 속하는지 판단하는 가장 빠른 방법입니다. 다른 방법은 .month 정밀도로 Calendar.compare를 사용하는 것입니다: 결과가 .orderedSame이면 월이 일치합니다.

swift
func isInCurrentMonth(_ date: Date) -> Bool {
    let calendar = Calendar.current
    let monthInterval = calendar.dateInterval(
        of: .month, for: Date()
    )!
    return monthInterval.contains(date)
}

// 한 달의 일 수
func daysInMonth(for date: Date) -> Int {
    let calendar = Calendar.current
    return calendar.range(
        of: .day, in: .month, for: date
    )?.count ?? 0
}

// 올바른 래핑으로 월 추가
func addMonths(_ months: Int, to date: Date) -> Date {
    let calendar = Calendar.current
    return calendar.date(
        byAdding: .month, value: months, to: date
    )!
}

Calendar.range(of:in:for:)는 다른 구성 요소의 컨텍스트에서 지정된 구성 요소의 유효한 값 범위를 반환합니다. 예를 들어, range(of: .day, in: .month, for: date)는 31일이 있는 달의 경우 1..<32를, 윤년이 아닌 해의 2월의 경우 1..<29를 반환합니다. 하드코딩된 값 대신 한 달의 일 수를 알아내는 올바른 방법입니다.

월 추가는 Calendar.date(byAdding:value:to:)를 통해 경계 날짜를 올바르게 처리합니다. 1월 31일에 1개월을 추가하면 Calendar는 2월 28일(또는 윤년의 경우 29일)을 반환합니다. TimeInterval을 통해 단순히 30일을 추가했을 때의 3월 3일이 아닙니다. 이것이 캘린더 계산에 TimeInterval을 사용하지 말아야 하는 또 다른 이유입니다.

Calendar 메서드목적예제
dateInterval기간 경계월의 시작과 끝
range(of:in:for:)구성 요소 범위현재 달의 일 수
date(byAdding:)날짜 이동오늘부터 +1개월
isDateInToday오늘 확인날짜가 오늘인가요?
compare(toGranularity:)정밀도 비교시간 무시하고 같은 날

자주 묻는 질문

Calendar.current와 Calendar(identifier: .gregorian)의 차이점은 무엇인가요?

Calendar.current는 사용자의 시스템 설정에서 캘린더를 반환합니다 — 그레고리력이 아닐 수 있습니다(예: 태국의 불교력). Calendar(identifier: .gregorian)는 설정에 관계없이 항상 그레고리력 캘린더를 생성합니다. 날짜 표시에는 Calendar.current를, 비즈니스 로직에는 명시적으로 선택한 식별자를 사용하세요.

Calendar.date(byAdding: .month, value: 1)가 때때로 같은 날짜를 반환하는 이유는 무엇인가요?

이는 월 길이가 다르기 때문입니다. 현재 날짜가 1월 31일인 경우 1개월을 추가하면 2월 28일이 됩니다. 2월에는 31일이 없기 때문입니다. Calendar는 자동으로 날짜를 해당 월의 마지막 유효일로 조정합니다. 정확한 제어를 위해 월의 첫 번째 날로 이동하려면 day: 1이 있는 DateComponents를 사용하세요.

DateFormatter는 기본적으로 어떤 캘린더를 사용하나요?

DateFormatter는 Calendar.current — 사용자의 시스템 캘린더를 사용합니다. 애플리케이션이 설정에 관계없이 항상 그레고리력으로 날짜를 표시해야 하는 경우 formatter.calendar = Calendar(identifier: .gregorian)을 설정하세요. 이는 모든 사용자에게 통일된 표시를 보장합니다.

Calendar를 통해 연도가 윤년인지 확인하는 방법은 무엇인가요?

Calendar.range(of: .day, in: .year, for: date)는 365 또는 366일을 반환합니다. 더 간단한 방법: Calendar.date(from: DateComponents(year: year, month: 2, day: 29)) != nil — 2월 29일이 존재하면 윤년입니다. Calendar는 특정 캘린더 시스템의 규칙을 자동으로 처리합니다.

Calendar 생성 후 firstWeekday를 변경할 수 있나요?

, firstWeekday 속성은 쓰기 가능합니다. 변경은 weekOfMonth, weekOfYear 및 주 번호와 관련된 모든 계산에 영향을 미칩니다. locale = Locale(identifier: “ru_RU”)를 설정하면 firstWeekday가 자동으로 2(월요일)가 됩니다. 수동 설정은 로케일의 값을 재정의합니다.

요약

  • Calendar — DateComponents를 통해 Date를 사람이 읽을 수 있는 구성 요소와 연결하는 캘린더 계산용 Foundation 클래스입니다.
  • 17개의 캘린더 시스템이 Foundation에서 지원됩니다 — 그레고리력부터 불교력, 일본력까지, 자동 지역 규칙 처리 포함.
  • Calendar.current는 사용자의 시스템 캘린더와 일치합니다 — UI와 DateFormatter에 사용하세요.
  • Calendar.dateIntervalCalendar.range는 기간 경계 및 구성 요소 범위 작업을 위한 주요 메서드입니다.
  • Calendar.date(byAdding:)는 다양한 월 길이와 윤년을 올바르게 처리합니다 — TimeInterval 산술로 대체하지 마세요.
  • TimeZone과 Locale은 Calendar 속성으로서 모든 캘린더 계산에 영향을 미칩니다 — 예측 가능한 동작을 위해 명시적으로 설정하세요.
  • firstWeekday는 로케일에 의해 결정되며 주 번호에 영향을 미칩니다 — 글로벌 애플리케이션의 경우 명시적으로 설정하거나 Calendar.current를 사용하세요.

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

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

프로젝트 논의

더 읽어보기