日付に関連するクラス
macOS 10.15.7 / Xcode 11.3.1 / Swift 5.0
日付に関連する Dateクラス、Calendarクラス、DateFormatterクラス、DateComponentsクラスの機能、相互の関連について整理してみた。

Dateクラス
日付時刻の基本情報(年、月、日、時、分、秒)を保持する。
Dateオブジェクトは日付時刻のみ保持し、カレンダーの種類(グレゴリオ暦、和暦、イスラム暦など)、曜日、タイムゾーン、ロケールには関与しない。これらは以下に説明するクラスが担当する。
主要なメソッドはこれだけである。日付時刻の操作は以降で説明する日付関連クラスと連携して行われる。Dateオブジェクトは日付時刻を表す単一の値(おそらく time interval)を持っているだけで、年月日時分秒と分けて表示するときは Calendarオブジェクトと内部的に連携しているのだと思う。
Calendarクラス
Calendarオブジェクトは、年・月・日・曜日などカレンダーに関わる情報を保持する。システム規定のカレンダーは「グレゴリオ歴」である。(これはシステム環境設定の「言語と地域」で変更することができる。イスラム暦、ヘブライ歴を使う国や地域があるのだろう。)
日本でもグレゴリオ歴以外を使用することは一般的にはありそうもないが、例外的に元号年の表示などで「和暦」を使用する場合がある。和暦を使う場合はシステム規定とは別に Calendarオブジェクトを作成して使用する。また、カレンダーの情報を必要とする処理をメソッドとして提供する。
余談だが、エチオピア暦は月が 13月まであるそうだ。一意の日付に対して、異なるカレンダーにより日付が様々に表現されることを理解する上で参考になる例でしょう。
DateFormatterクラス
日付オブジェクトを文字列として表示する。日付の文字列から日付オブジェクトを作成する。タイムゾーン、言語・地域(ロケール)を管理する。
タイムゾーン
まず、Dateオブジェクトのタイムゾーンはグリニッジ標準時(GMT)である。しかしながら、Dateオブジェクトを DateFormatterオブジェクトを利用して文字列に変換すると、タイムゾーンはその地域のものに自動的に変換される。
DateFormatterオブジェクトは、自身のプロパティにタームゾーンに保持する。デフォルトではホストPCに設定されているタイムゾーン(システム環境設定の「日付と時刻」)になるので、日本であれば日本標準時(JST)になっている。従って DateFormatterオブジェクトを介して日付・時刻を表現する場合、タイムゾーンを意識する必要は全くない。
タイムゾーンは timeZoneプロパティの設定により変更できる。変更した後は、DateFormatterオブジェクトを利用して取得する日時はそのタイムゾーンを基準としたものとなる。
言語・地域(ロケール)
日付を DateFormatterオブジェクトで文字列に変換するときの表示形式を指定する。暦の種類(グレゴリ暦等)、日付・時刻の表現形式(日時の位置や区切り文字)、曜日の表現などは地域・言語ごとに異なる。
ソフトウェアの世界では、これらの要素を「ロケール」という単位で定義する。ロケールは国際的に標準化された識別子で、言語コードと国コードをハイフンでつなげた文字列で表現する。それぞでのコードは ISO 639と ISO 3166で規定されている。
いくつかの例をあげると
- ja_JP (日本語+日本)
- en_US (英語+米国)
- en_GB (英語+英国)
DateFormatterオブジェクトはプロパティにロケールを持ち、それに応じた日付・時刻の編集を行う。ロケールの設定方法、ロケールの違いでどのような表示形式になるかを示す。
表示形式は、言語(en, ja)に対応していて、地域(JP, US)には影響を受けないようだ。なおロケールはタイムゾーンとは無関係である。
書式指定子による日付・時刻の編集
日付専用の書式指定子を使用して Dateオブジェクトを文字列に変換する。Stringクラスの書式指定子と同じような使い方になる。書式指定子は、年、月、日、時、分、秒、曜日など日付の要素ごとに細かく定義されている。これは、Unicode Technical Standard (UTS)の仕様である Date Format Patternsに対応する。リンクはこちら
手順
書式指定子を埋め込んだ文字列を作成しDateFormatterオブジェクトの dateFormatプロパティにセットする。stringメソッドは、Dateオブジェクトから日付の要素を抜き出し文字列に展開する。
yyyy: 西暦年、M: 月、d: 日、h: 時、m: 分、s: 秒
曜日は Dateオブジェクトの内部では、日曜から始まる整数値(1〜7)で保持されている。書式子の"E" は、ロケールに応じた曜日名に変換する。"e" は整数値をそのまま出力する。
定型的な日付・時刻の出力
4種類の定型的な日付・時刻を出力することができる。ロケールを指定しなければシステムで選択されているロケール(ja_JP)で表示される。dateStyleは日付の表示スタイル、timeStyleは時刻の表示スタイルで、それぞれ別に指定することができる。
日付の文字列から Dateオブジェクトを作成する
date( from: )メソッドは日付文字列から Dateオブジェクトを作成する。文字列に埋め込む値として年、月、日、時、分、秒を個別に指定する。
dateFormatプロパティには、書式指定子を指定する。これは、入力文字列に埋め込まれた日付の要素と対応していて、その要素を抜き出すために使われる。入力日付の形式は自由で、日付・時刻の並び順、セパレータの有無、セパレータの文字や個数は任意である。例題では、たまたま標準的な形式の日付にしただけである。
また、日付要素は省略することができ、省略値はデフォルトの値が使用される。
時刻を省略すると 0時0分0秒になる。
和暦を扱う
和暦年(年号+年)を表示する
calendarプロパティはカレンダーの種類を指定する重要なプロパティであるが、デフォルトはグレゴリオ暦(gregorian)となっているので、通常はそのまま使用すればよい。和暦年を表示するには、カレンダーを和暦(japanese)にし、年号を表示する書式指定子("G")と年を組み合わせる。なお、年号は、改元日に変わるので、2019/4/30までは平成、5/1以降に令和と表示される。
(和暦といっても年の表示形式が異なるだけで、暦が旧暦になるわけではない)
和暦の文字列から Dateオブジェクトを作成する
calendarプロパティを和暦にする。
和暦の日付文字列を入力にして、date( from: ) メソッドを実行し Dateオブジェクトを作成する。年の書式指定子には元号を指定し和暦として扱うこと。
DateComponentsクラス

DateComponentsオブジェクトと Dateオブジェクトは、1対1の関係にある。両者は互換性があり、前者から後者を作成、または後者から前者を作成することができる。
DateComponentsオブジェクトは Dateオブジェクトが保持する日付データを要素(年、月、日、時、分、秒、曜日)に分解し、それぞれプロパティとして保持する。プロパティの値は整数(オプショナル Int型)である。これらの値を利用して日付の計算(加減算や期間算出)や大小比較、要素からの文字列を作成するといった日付に関する操作を行う。
DateComponentsオブジェクトを作成する
方法1
イニシャライザで空のオブジェクトを作成し、各要素のプロパティに値をセットする。プロパティの型は Optional Int である。
要素を省略することもできる。
方法2
Calendarオブジェクトの dateComponents( _: from: ) メソッドにより、Dateオブジェクトから指定した日付の要素を抽出し、DateComponentsオブジェクトを作成する。抽出する要素は、Calendar.Component列挙型を要素とした Setオブジェクトとし引数に指定する。
要素は dateComponentsメソッドの引数に直接指定すると自動的に型変換されるので下記のように簡潔に書くことができる(こちらの方がお勧め)。また要素は省略することができる。
DateComponentsオブジェクトからDateオブジェクトを作成する
日付の要素(年、月、日、時、分、秒)を指定して DateComponentsオブジェクトを作成し、そこから Dateオブジェクトを作成する。
指定する日付は、システムに設定されているタイムゾーンを基準とする。ここでは日本標準時(JST)になる。Dateオブジェクトの日付は自動的にグリニッジ標準時(GMT)に変換される。
日付の計算
月末日を求める
現在日の月末日を求める。月末日は、月の大小または2月の場合は閏年によって異なるので、取得するのにひと工夫いる。
Calendarオブジェクトの rangeメソッドで現在日の月の日数(=月末日)を得る。例題では現在日が 2025/6/10 なので月末日は 30日になる。月末日の Dataオブジェクトを求める場合は現在日の年月日から DateComponentsオブジェクトを作成し、日(day)要素を月末日に置き換え Dateオブジェクトを作成する。
rangeメソッドの説明は難しいが言葉にすれば
「指定の日付の月の日の範囲(日数)を求め Rangeオブジェクトとして返す」
ということになる。引数の "for" は指定の日付(現在日)、"in" は月、"of" は日の範囲という対応になる。戻り値の Rangeオブジェクトは添字の範囲なので、件数は最大添字 -1 になる。
次のコードは現在日の年の日数を求めるものである。
日付の足し算/引き算
日付の演算は、Calendarオブジェクトの date( byAdding: to: ) メソッドを使う。演算対象の Dateオブジェクトに、日付要素に値をセットした DateComponentsオブジェクトを加算するという形をとる。
2000年12月20日 の 10日後(足し算)
2000年12月20日 の 10日前(引き算)
2000年12月20日 の 1000日後(年月日の繰り上がりを伴う足し算)
2000年12月20日 の 2ヶ月と 20日後(要素の混合)
このような場合は計算の順序が重要である。まず対象日付の 2ヶ月を求め、次にその 20日後を求めている。
これを逆に、20日後を求めた後に 2ヶ月を求めると結果が異なる。月ごとに日数が違うのでこのようなことが起こるので、使用の際は注意すること。
2000年12月20日 の 20日後を求めた後に 2ヶ月後を求める
以上二つの結果が違うのは、2月の日数(28)と 12月の日数(31)の違いから来ている。
月末日を求める
月末日を求める方法は既述だが、日付の加減算よっても求めることができる。
対象日の月の初日の日付を求め、1ヶ月加算して1日戻る。
2000/12/20 → 2000/12/1 → 2001/1/1 → 2000/12/31
期間を求める(二つの日付の差)
二つの日付(Dataオブジェクト)の差を Calendarオブジェクトの dateComponents( _: from: to: ) メソッドを利用して求める。from引数に指定した Dateオブジェクトと to引数に指定した Dateオブジェクトの差を DateComponentsオブジェクトにして返す。
春分の日から秋分の日までの日数を求める。第1引数の [ .day ] により結果を日数で返す。
誕生日から現在日までの期間(年齢)を求める。第1引数の [ .year, .month, .day ] は結果を年月日で返すように指定する。
[ 注意事項 ]
日付の差の取得に関しては時刻の取り扱いに注意すること。次の例では、1月1から1月2までの日付の差が0日となる。時刻を含めて計算するとその差は23時間となり、1日未満という判定になるからである。かなりレアなケースだと思うが、現在日を使用するような場合は留意すべき点である。
従って、日付の差を計算するときは、比較する日付の時刻を合わせておくことを強くお勧めする。次の例は現在日の Dateオブジェクトから時刻を除外するコードである。
期間を求める(二つの日付の差)別解
日付の通算日数の差から期間を求める。システムが持つカレンダーはグレゴリオ暦の場合、西暦1年1月1日0時が始点となっている。Calendarオブジェクトの ordinality( of: in: for: )メソッドを使えば、カレンダーの始点の日から指定した日付までの日数(あるいは年数、月数、経過時間など)を求めることができる。これを利用して2つの日付の日数の差を求める。
引数の説明
- of: 単位を指定する。日数(day)、年(year)、月(month)、時間(hour)など DateComponentクラスに定義された要素を指定することができる。
- in: 期間の始点を指定する。システムカレンダの初日を始点にするには時代(era)とする。他の使い方はよくわからない。
- for: 対象とする日付の Dateオブジェクトを指定する。
ordinalityメソッドは引数の指定の仕方によって様々なデータを得ることができる。次のコードは、現在時刻が0時より何秒経過したかがわかる。
時刻の操作
時刻の操作も日付の操作と変わるところは特に何もない。「年」「月」「日」に対するいかなる操作も、「時」「分」「秒」に対して同様に行うことができる。
曜日の操作
DateComponentsオブジェクトの曜日 weekDay は、日曜から始まる整数値 1〜7 を保持する。これは年月日とカレンダーの種類から決まる。
次に要素の値をロケールに合わせた曜日名に変換する手順を示す。DateFormatterオブジェクトに曜日名のテーブルを持つ。添字は要素の値マイナス1とする。
日付操作ライブラリ
日付の操作をしようとすると、単純なコードを繰り返し書かなければならないのが煩わしい。
ライブラリ化して、そういうコードを書かずに済むようにするのが使い方としては望ましいと思うので作成してみた。
DateUtilクラス(工事中)