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自体にはこの情報が含まれていません。

カレンダーは3つのパラメータグループを考慮します:カレンダーシステム(グレゴリオ暦、仏暦、和暦)、タイムゾーン、およびロケールです。Calendar.currentは、ユーザーのシステム設定からこれら3つを組み合わせます。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は1回の呼び出しで複数のコンポーネントを抽出します — Calendarが各コンポーネントごとに個別に計算するのではなく、一度だけカレンダー計算を行うため、より効率的です。3つ以上のコンポーネントのリストには常にdateComponentsを使用してください。

Calendar.compareは、指定された精度で2つのDateを比較します。toGranularityパラメータはコンポーネントの精度を決定します:.yearは年のみ、.monthは年と月、.dayは年、月、日を比較します。時間を無視して2つの日付が同じ日であるかを確認するのに便利です。

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.isDateInTodayisDateInTomorrowisDateInYesterday — 相対チェックのためのメソッドです。Calendar.isDate(_:inSameDayAs:)は、カレンダーのタイムゾーンを考慮して2つの日付が同じ暦日にあるかをチェックします。これらのメソッドは内部でCalendar.compareを使用し、頻繁な呼び出しに最適化されています。

Calendarの計算

Calendar.dateIntervalは、分析やUIにとって最も有用なメソッドの1つです。指定されたコンポーネント(日、週、月、年)の開始と終了を表すDateIntervalを返します。DateIntervalにはstart(Date)とend(Date) — 期間の境界が含まれます。例えば、dateInterval(of: .weekOfYear, for: Date())は現在の週の月曜日の開始と日曜日の終了を返します。

Calendar.dateと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を使用すべきでない理由の1つです。

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アプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください