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でも月が異なる2つの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は要求されたすべてのフィールドを1回のパスで抽出します — これは各フィールドに個別にCalendar.componentを呼び出すよりも大幅に高速です。

DateComponentsの作成

DateComponentsの初期化 — 最も簡単な方法:空の構造体を作成し、必要なフィールドを埋めます。指定されていないすべてのフィールドは自動的にnilになります。部分的なコンポーネントから作成された日付は初期化時に検証されません — エラーはCalendarを介してDateに変換するときにのみ発生する可能性があります。

イニシャライザDateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…)を使用すると、1回の呼び出しですべてのフィールドを設定できます。このイニシャライザは準備された値から完全な日付を作成するのに便利ですが、可読性の理由から5〜6個以上の引数で使用されることはほとんどありません。

Calendar.dateComponents(_:from:) — 既存のDateからDateComponentsを取得する主要な方法です。2番目の引数は抽出するコンポーネントのセットです。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のもう1つの使用例。Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate)は2つの日付間の年、月、日の差を返します。これはTimeIntervalを1年の秒数で割る代わりに年齢を計算する正しい方法です。Calendarが閏年を考慮するためです。

CalendarとDateComponents

Calendar — DateComponentsを扱う中心的なクラスです。日付の抽出、組み立て、比較のすべての操作はCalendarを通過します。Calendarなしでは、DateComponentsは時間的な意味を持たない単なる数値の集合です。Calendarはコンポーネントに解釈を与えます:月2が2月であること、weekday 2が月曜日であることを決定します。

Calendar.nextDateCalendar.enumerateDates — DateComponentsに基づく2つのメソッド。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は真と見なされます)、2つの構造体は等しくなります。フィールドの一部のみを比較するには — 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アプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

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

こちらもお読みください