DateFormatterは、Dateオブジェクトとその文字列表現の間の双方向変換のために設計されたFoundationクラスです。このクラスはユーザーのロケール、タイムゾーン、カレンダーを考慮し、世界のどの地域でも日付を正しく表示します。Apple Developer Documentation(2025)によると、DateFormatterは4つのプリセット日付/時刻スタイルと、テンプレート文字列による完全カスタム形式をサポートしています。DateFormatterなしでは、国際化されたアプリケーションでユーザーに日付を正しく表示することは不可能です。
主なポイント
DateFormatterは、Foundationフレームワークのクラスで、Dateと文字列の間の双方向変換を実装します。これはOpenStepでNSDateFormatterとして初めて登場し、それ以来すべてのAppleプラットフォームで日付書式設定の主要ツールであり続けています。このクラスはFormatterから継承し、ローカライズされた日付表示のための便利なAPIを提供します。
DateFormatterはUnicode LDMLパターンに基づいて動作します。これはICU(International Components for Unicode)で使用されるものと同じです。パターンはdateFormatプロパティを介して設定され、記号y、M、d、H、m、sは年、月、日、時、分、秒に対応します。記号の繰り返しが形式を決定します:「y」— 2桁の年、「yyyy」— 4桁の年。
DateFormatterの作成は高コストな操作です。初期化中にロケールとカレンダーのデータがロードされるためです。Appleは書式設定タイプごとにフォーマッターを一度作成し、再利用することを推奨しています。SwiftUIやUIKitでは、フォーマッターは静的なプロパティにキャッシュされたり、最初のアクセス時に遅延作成されたりすることがよくあります。
DateFormatterは、多くのiOSシステムコンポーネントで使用されています。UIDatePickerはcountDownTimerモードで日付を表示するために内部でDateFormatterを使用します。フォーマッター付きのTextFieldは、ユーザーが入力した日付を自動的に検証できます。Core DataはDate型の属性をサポートしますが、その文字列表現は常にDateFormatterを介して処理されます。
スレッドセーフティ — DateFormatterはスレッドセーフではありません。異なるスレッドからフォーマッターのプロパティを変更すると、未定義の動作を引き起こします。マルチスレッド環境では、スレッドごとに個別のフォーマッターインスタンスを作成するか、NSLockまたはシリアルキューを使用した同期を行います。
dateStyleとtimeStyleは、日付表示を設定する最も簡単な方法です。各スタイルには4つのバリエーションがあります:.short、.medium、.long、.full。dateStyleとtimeStyleの組み合わせにより、日付と時刻の形式を独立して設定でき、.noneプロパティは対応する部分を無効にします。
米国ロケールの場合、.shortは日付を「7/21/26」として書式設定し、ロシアルケールの場合は「21.07.2026」とします。ロシアルケールの.longスタイルは「2026年7月21日」を出力し、.fullは「2026年7月21日火曜日」を曜日付きで出力します。4つのスタイルはすべて、コンポーネントの順序や区切り文字を含め、地域の標準に自動的に適応します。
iOS 15+のSFDateFormatterは、RelativeDateFormatterとDateIntervalFormatterを介した代替アプローチを提供します。RelativeDateFormatterはコンテキスト表示用に「今日」「昨日」「3日後」を出力します。DateIntervalFormatterは日付範囲を表示します:「2026年7月21日~25日」— 予約や計画用です。
| スタイル | 例(ru_RU) | 例(en_US) |
|---|---|---|
| .short | 21.07.2026 | 7/21/26 |
| .medium | 2026年7月21日 | Jul 21, 2026 |
| .long | 2026年7月21日 | July 21, 2026 |
| .full | 2026年7月21日火曜日 | Tuesday, July 21, 2026 |
スタイルを組み合わせる場合、DateFormatterは自動的に区切り文字を選択します:.short.date + .short.timeの場合、結果は「2026/07/21 14:30」になります。.full.date + .full.timeの場合 — 「2026年7月21日火曜日 14:30:00 MSK」。区切り文字は開発者ではなくロケールによって管理されます。これにより、ユーザーの地域的な期待に準拠することが保証されます。
dateFormatを使用すると、Unicode LDML仕様記号を使用して任意の書式パターンを設定できます。これにより表示を完全に制御できます:年と月のみ、または日付なしの曜日、秒なしの時刻を表示できます。カスタム形式は特定のデザイン要件に不可欠です。
主な記号 — yyyy(年:2026)、MM(月:07)、dd(日:21)、HH(時:14)、mm(分:30)、ss(秒:00)。月の完全な名前にはMMMM(7月)、短縮形にはMMM(7月)を使用します。曜日 — EEEE(火曜日)、短縮形 — E(火)。
dateFormatを使用する場合、フォーマッターのロケールを設定することが重要です。ロケールが設定されていない場合、フォーマッターはシステムロケールを使用します。これはAPIで固定形式を使用する場合に望ましくない可能性があります。Appleは、特にサーバー応答からの日付解析時に、固定の地域間形式としてlocale = Locale(identifier: "en_US_POSIX")を設定することを推奨しています。
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.dateFormat = "d MMMM yyyy"
let customString = formatter.string(from: Date())
// "21 July 2026"
// カスタム文字列の解析
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: "2026-07-21 14:30:00")!
dateFormatのエラーは、アプリケーションクラッシュの一般的な原因の1つです。形式が文字列と一致しない場合、date(from:)メソッドはnilを返します。安全なオプショナルアンラッピングにはguard letまたは??を使用します。形式を検証するには、サポートされているすべての言語でテストしてください。一部のLDML記号はロケールによって動作が異なります。
Localeは、月名、曜日名、および区切り文字の表示方法を決定します。DateFormatterはデフォルトでLocale.currentを使用しますが、一部のシナリオでは特定のロケールを指定する必要があります:ログの固定形式にはen_US_POSIX、サーバーの日付にはサーバーに一致するロケールを使用します。
TimeZoneプロパティは、表示のタイムゾーンを決定します。デフォルトではシステムのタイムゾーンが使用されますが、国際的なユーザーを持つアプリケーションでは、多くの場合ユーザーのタイムゾーンまたはUTCで日付を表示する必要があります。timeZoneの変更は表示にのみ影響します — Date値は変更されません。
重要な機能:DateFormatterが文字列の解析に使用され、文字列にタイムゾーンの指定が含まれている場合(たとえば、UTCを示すZ付きの「2026-07-21T14:30:00Z」)、timeZoneプロパティは無視され、フォーマッターは文字列からタイムゾーンを使用します。文字列にタイムゾーンがない場合は、フォーマッターのtimeZoneが適用されます。
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
formatter.dateStyle = .long
formatter.timeStyle = .short
let moscowTime = formatter.string(from: Date())
// "21 July 2026, 14:30"
// 文字列内のタイムゾーンなしで解析
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let utcDate = formatter.date(from: "2026-07-21 10:30")!
AutoupdatingCurrentLocale — ユーザーのシステム設定が変更されたときに自動的に更新される特別なロケールタイプです。DateFormatterはデフォルトでこれをサポートしています。アプリがバックグラウンドで実行中にユーザーがシステム言語を変更した場合、変更前に作成されたフォーマッターは古いロケールを引き続き使用します — 更新するには新しいインスタンスを作成する必要があります。
ISO8601DateFormatterは、ISO 8601形式の日付を扱うための専門フォーマッターです。この形式は、REST API、JSON、データ交換の事実上の標準です。ISO8601DateFormatterはロケールに依存せず、固定の解析文法を使用するため、DateFormatterよりも大幅に高速に動作します。
主なフォーマッターオプション — .withInternetDateTime(2026-07-21T14:30:00Z)、.withFractionalSeconds(ミリ秒を追加)、.withTimeZone(タイムゾーンオフセットを含む)。オプションを組み合わせることで、任意のISO 8601バリアントを取得できます:ミリ秒付き、タイムゾーン付き、日付のみなど。
JSONEncoder.DateEncodingStrategyを使用すると、すべてのCodableモデルの日付エンコーディングをグローバルに設定できます。オプション — .iso8601(ISO8601DateFormatterを使用)、.formatted(DateFormatter)、.millisecondsSince1970、.secondsSince1970。戦略の選択はシリアライゼーションのライフサイクル全体に影響し、すべてのAPIエンドポイントで一貫している必要があります。
// ISO8601DateFormatter
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let isoString = isoFormatter.string(from: Date())
// "2026-07-21T14:30:00.000Z"
// ISO8601を使用したJSONEncoder
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
// 代替:カスタムフォーマッターを使用したJSONEncoder
let customEncoder = JSONEncoder()
customEncoder.dateEncodingStrategy = .formatted(myFormatter)
DateFormatter vs ISO8601DateFormatter — APIでの日付のシリアライズと解析にはISO8601DateFormatterを選択してください。DateFormatterより5〜10倍高速で、ローカライゼーションエラーの影響を受けません。DateFormatterは、ユーザーの母国語での月名や曜日名を含むローカライズされた表示が必要なユーザーインターフェース用に残します。
iOSアプリでのDateFormatterの実際の使用シナリオを見てみましょう:ニュースフィードでの日付表示、生年月日の入力、異なるタイムゾーンの日付を含むレポートのエクスポート。
RelativeDateFormatterはニュースフィードに最適です。新しいニュースには「たった今」「5分前」「昨日」と表示し、古いニュースには完全な日付に切り替えます。切り替えのしきい値はカレンダーで設定します:ニュースの場合は24時間、メッセンジャーの場合は1週間です。
func formatRelativeDate(_ date: Date) -> String {
let relative = RelativeDateFormatter()
relative.unitsStyle = .full
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
let daysDiff = Calendar.current.dateComponents(
[.day], from: date, to: Date()
).day ?? 0
return daysDiff < 1
? relative.localizedString(for: date, relativeTo: Date())
: formatter.string(from: date)
}
生年月日の入力 — もう1つの一般的なシナリオです。DateFormatterは特定のdateFormat「dd.MM.yyyy」とlocale「ru_RU」で設定されます。入力された文字列を解析する際は、エラーを適切に処理することが重要です:フォーマッターは無効な文字列に対してnilを返します。解析成功後、日付が許容範囲内(1900年より前ではなく、今日より後ではない)であることを確認します。
日付を含むレポートのエクスポートには、ユーザーのロケールに依存しない固定形式が必要です。locale en_US_POSIXとタイムゾーンUTCでdateFormat「yyyy-MM-dd HH:mm:ss」を使用します。このアプローチにより、システムの地域設定に関係なく、ファイルがどの国でも正しく開かれることが保証されます。
| シナリオ | フォーマッター | 主要設定 |
|---|---|---|
| ニュースフィード | RelativeDateFormatter | unitsStyle = .full |
| 日付入力 | DateFormatter | dateFormat + フォールバック |
| APIシリアライズ | ISO8601DateFormatter | withInternetDateTime |
| レポートエクスポート | DateFormatter | en_US_POSIX + UTC |
よくある質問
最も一般的な理由 — dateFormatと文字列形式の不一致です。たとえば、形式「dd.MM.yyyy」は文字列「2026-07-21」を解析しません。2番目の理由 — ロケールの不一致:文字列「July 21, 2026」はru_RUロケールでは解析されません。3番目 — LDML記号のタイプミス:yyyyを使用してください。YYYではありません(意味が異なります)。
いいえ。DateFormatterは重いオブジェクトで、初期化にはロケールデータの読み込みが含まれます。書式設定タイプごとに1つのインスタンスを作成し、再利用してください。マルチスレッド環境では、スレッドローカルストレージまたは同期用のシリアルキューを持つフォーマッタープールを使用します。
DateFormatterは絶対日付(2026年7月21日)を表示し、RelativeDateFormatterは相対日付(今日、昨日、3日後)を表示します。RelativeDateFormatterはiOS 15+で導入され、同じLDMLテンプレートを使用しますが、自動的に相対表示を選択します。
解析前にフォーマッターのtimeZoneをUTCに設定します。サーバーがタイムゾーン指定なしで現地時間の日付を返す場合、API仕様を確認してください — おそらくUTCが暗示されています。末尾にZが付いたISO 8601の場合、timeZoneは不要です — フォーマッターが文字列からオフセットを解析します。
同期なしで異なるスレッドから単一のインスタンスを使用しないでください。各スレッドで新しいインスタンスを作成するか、ストレージにThread.current.threadDictionaryを使用します。代替手段として、string(from:)とdate(from:)の間ロックするNSLockがあります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。