日付時刻関連で使われる書式の調査

以下も併せてお読みください。

- [日付時刻関連のクラスを活用しよう](http://qiita.com/mpyw/items/e42fa173e5a26220a785)
- ["厳密に" 日付時刻のフォーマットをチェックする](http://qiita.com/mpyw/items/d8c12ced65a1cd87c630)

# 日付時刻の入力書式

以下の関数やメソッドで使われます。

- [strtotime]
- [DateTime::__construct]
- [DateTimeImmutable::__construct]
- [DateTime::modify]
- [DateTimeImmutable::modify]
- [DateInterval::createFromDateString]

書式について詳しく知りたい場合は[サポートする日付と時刻の書式]を参照してください。特に**[相対的な書式]**の注意事項には目を通しておくべき</ins>です。

ここではマニュアルを見ても分かりづらいであろう「変更される部分」「変更されない部分」を明らかにし、また具体例を中心にピックアップしていきたいと思います。

- タイムゾーンは`php.ini`で既定された値に従います。ここでは現在のタイムゾーンが`UTC`または`+00:00`に既定されていると仮定します。
- 書式中にタイムゾーンが含まれていた場合、それに従います。
- [DateTimeZone]オブジェクトによりタイムゾーンが指定された場合、それに従います。
- 書式中のタイムゾーンと[DateTimeZone]オブジェクトが同時に競合した場合、**書式中のタイムゾーンが優先**されます。
- 表中の`*`は現在値が使用されることを意味します。

#### 絶対指定

<table>
<tr>
<th>指定対象</th>
<th>年</th>
<th>月</th>
<th>日</th>
<th>時</th>
<th>分</th>
<th>秒</th>
<th>タイムゾーン</th>
<th>書式</th>
<th>注記</th>
</tr>
<tr>
<td rowspan="9">時刻</td>
<td rowspan="9"><strong>*</strong></td>
<td rowspan="9"><strong>*</strong></td>
<td rowspan="9"><strong>*</strong></td>
<td rowspan="9">23</td>
<td rowspan="9">00</td>
<td rowspan="9">00</td>
<td rowspan="9"><strong>*</strong></td>
<td><code>23:00:00</code></td><td></td>
</tr>
<tr><td><code>23.00.00</code></td><td></td></tr>
<td><code>23:00:00.0000000</code></td>
<td rowspan="2">
<ul>
<li>PHP5.3以降限定</li><li>マイクロ秒7桁以下</li>
</ul>
</td>
<tr><td><code>23.00.00.0000000</code></td></tr>
<tr><td><code>T23:00:00</code></td><td></td></tr>
<tr><td><code>230000</code></td><td>6桁</td></tr>
<tr><td><code>11 pm</code></td><td></td></tr>
<tr><td><code>11:00 PM</code></td><td></td></tr>
<tr><td><code>11.00 P.M.</code></td><td></td></tr>
<tr>
<td rowspan="17">年月日</td>
<td rowspan="17">2015</td>
<td rowspan="17">01</td>
<td rowspan="17">02</td>
<td rowspan="17"><strong>00</strong></td>
<td rowspan="17"><strong>00</strong></td>
<td rowspan="17"><strong>00</strong></td>
<td rowspan="17"><strong>*</strong></td>
<td><code>2015-01-02</code></td><td>年は4桁</td>
</tr>
<tr><td><code>15-1-2</code></td><td>非推奨</td></tr>
<tr><td><code>2-1-2015</code></td><td>年は4桁</td></tr>
<tr><td><code>2.1.2015</code></td><td>年は4桁</td></tr>
<tr><td><code>2.1.15</code></td><td>非推奨</td></tr>
<tr><td><code>2015/01/02</code></td><td>年は4桁</td></tr>
<tr><td><code>2015-1-2</code></td><td>年は4桁</td></tr>
<tr><td><code>1/2/2015</code></td><td>年は4桁</td></tr>
<tr><td><code>1/2/15</code></td><td>非推奨</td></tr>
<tr><td><code>2 January 2015</code></td><td></td></tr>
<tr><td><code>2 January 15</code></td><td>非推奨</td></tr>
<tr><td><code>January 2 2015</code></td><td></td></tr>
<tr><td><code>January 2 15</code></td><td>非推奨</td></tr>
<tr><td><code>2nd January 2015</code></td><td></td></tr>
<tr><td><code>2 Jan 2015</code></td><td></td></tr>
<tr><td><code>2-Jan 2015</code></td><td></td></tr>
<tr><td><code>2-Jan-2015</code></td><td></td></tr>
<tr>
<td rowspan="5">年月</td>
<td rowspan="5">2015</td>
<td rowspan="5">01</td>
<td rowspan="5"><strong>01</strong></td>
<td rowspan="5"><strong>00</strong></td>
<td rowspan="5"><strong>00</strong></td>
<td rowspan="5"><strong>00</strong></td>
<td rowspan="5"><strong>*</strong></td>
<td><code>2015-01</code></td><td rowspan="5">年は4桁</td>
</tr>
<tr><td><code>January 2015</code></td></tr>
<tr><td><code>2015 January</code></td></tr>
<tr><td><code>Jan 2015</code></td></tr>
<tr><td><code>Jan-2015</code></td></tr>
<tr>
<td rowspan="8">月日</td>
<td rowspan="8"><strong>*</strong></td>
<td rowspan="8">01</td>
<td rowspan="8">02</td>
<td rowspan="8"><strong>00</strong></td>
<td rowspan="8"><strong>00</strong></td>
<td rowspan="8"><strong>00</strong></td>
<td rowspan="8"><strong>*</strong></td>
<td><code>01/02</code></td><td></td>
</tr>
<tr><td><code>2 January</code></td><td></td></tr>
<tr><td><code>January 2</code></td><td></td></tr>
<tr><td><code>2nd January</code></td><td></td></tr>
<tr><td><code>2 Jan</code></td><td></td></tr>
<tr><td><code>2-Jan</code></td><td></td></tr>
<tr><td><code>Jan 2</code></td><td></td></tr>
<tr><td><code>Jan-2</code></td><td></td></tr>
<tr>
<td rowspan="1">年</td>
<td rowspan="1">2015</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>2015</code></td><td>4桁</td>
</tr>
<tr>
<td rowspan="2">月</td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2">01</td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>January</code></td><td></td>
</tr>
<tr><td><code>Jan</code></td><td></td></tr>
<tr>
<td rowspan="2">年月日, 時刻</td>
<td rowspan="2">2015</td>
<td rowspan="2">02</td>
<td rowspan="2">03</td>
<td rowspan="2">23</td>
<td rowspan="2">03</td>
<td rowspan="2">04</td>
<td rowspan="2"><strong>*</strong></td>
<td><code>2015-02-03 23:03:04</code></td><td rowspan="2">年は4桁</td>
</tr>
<tr><td><code>2015-02-03T23:03:04</code></td></tr>
<tr>
<td rowspan="2">年, 週番号, 曜日番号</td>
<td rowspan="2">2015</td>
<td rowspan="2">02</td>
<td rowspan="2">03</td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>2015-W06-2</code></td><td rowspan="4">
<ul>
<li>年は4桁, 週は2桁, 曜日は1桁</li>
<li><strong>月曜日を<code>1</code></strong>～<strong>日曜日を<code>7</code></strong>として番号付けをし, <ins>月曜日を週の開始とする</ins></li>
<li><code>0</code>は<code>1</code>の<strong>前日</strong>として動作する</li>
</ul>
</td>
</tr>
<tr><td><code>2015W062</code></td></tr>
<tr>
<td rowspan="2">年, 週番号</td>
<td rowspan="2">2015</td>
<td rowspan="2">02</td>
<td rowspan="2">01</td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>2015-W06</code></td>
</tr>
<tr><td><code>2015W06</code></td></tr>
<tr>
<td rowspan="2">年, 通算日</td>
<td rowspan="2">2015</td>
<td rowspan="2">02</td>
<td rowspan="2">03</td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>2015.034</code></td><td rowspan="2">年は4桁, 通算日は3桁</td>
</tr>
<tr><td><code>2015034</code></td></tr>
<tr>
<td rowspan="1">タイムスタンプ</td>
<td rowspan="1">2015</td>
<td rowspan="1">02</td>
<td rowspan="1">03</td>
<td rowspan="1">23</td>
<td rowspan="1">03</td>
<td rowspan="1">04</td>
<td rowspan="1"><strong><ins>UTC</ins></strong></td>
<td><code>@1422972184</code></td><td>
<ul>
<li><code>DateTime::modify</code>で使うとタイムゾーン再設定が行われない<strong>バグ</strong>がある</li>
<li><code>DateTime::setTimestamp</code>は整数を扱うため32ビット環境では<strong>2038年問題</strong>が発生するが、こちらは文字列で渡すためそれを回避することが出来る</li>
</ul>
</td>
</tr>
<tr>
<td rowspan="3">タイムゾーン<br /><strong>(数値)</strong></td>
<td rowspan="3"><strong>*</strong></td>
<td rowspan="3"><strong>*</strong></td>
<td rowspan="3"><strong>*</strong></td>
<td rowspan="3"><strong>*</strong></td>
<td rowspan="3"><strong>*</strong></td>
<td rowspan="3"><strong>*</strong></td>
<td rowspan="3">+09:00</td>
<td><code>+0900</code></td><td rowspan="3">
<ul>
<li>時刻に影響を<ins>及ぼさない</ins></li>
<li><code>DateTime::modify</code>では無視される</li>
<li><code>GMT</code>は大文字
</ul>
</td>
</tr>
<tr><td><code>+09:00</code></td></tr>
<tr><td><code>GMT+09:00</code></td></tr>
</table>

#### 相対指定

<table>
<tr>
<th>意味</th>
<th>年</th>
<th>月</th>
<th>日</th>
<th>時</th>
<th>分</th>
<th>秒</th>
<th>タイムゾーン</th>
<th>書式</th>
<th>注記</th>
</tr>
<tr>
<td rowspan="1">現在</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>now</code></td>
<td>コンストラクタの規定値</td>
</td>
</tr>
<tr>
<td rowspan="2">時刻を<code>00:00:00</code>に</td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>today</code></td><td></td>
</tr>
<tr><td><code>midnight</code></td><td></td></tr>
<tr>
<td rowspan="1">時刻を<code>12:00:00</code>に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>12</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>today</code></td><td></td>
</tr>
<tr>
<td rowspan="1">時刻を前日の<code>00:00:00</code>に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">-1</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>yesterday</code></td><td></td>
</tr>
<tr>
<td rowspan="1">時刻を翌日の<code>00:00:00</code>に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">+1</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>tomorrow</code></td><td></td>
</tr>
<tr>
<td rowspan="2">時刻を18時15分前に</td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2">17</td>
<td rowspan="2"><strong>45</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>front of 18</code></td><td rowspan="4">タイムゾーン記述と併用すると12時間ずれる<strong>バグ</strong>がある</td>
</tr>
<tr><td><code>front of 6 pm</code></td></tr>
<tr>
<td rowspan="2">時刻を18時15分後に</td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td rowspan="2">18</td>
<td rowspan="2"><strong>15</strong></td>
<td rowspan="2"><strong>00</strong></td>
<td rowspan="2"><strong>*</strong></td>
<td><code>back of 18</code></td>
</tr>
<tr><td><code>back of 6 pm</code></td></tr>
<tr>
<td rowspan="6">時刻を1分前に</td>
<td rowspan="6"><strong>*</strong></td>
<td rowspan="6"><strong>*</strong></td>
<td rowspan="6"><strong>*</strong></td>
<td rowspan="6"><strong>*</strong></td>
<td rowspan="6">-1</td>
<td rowspan="6"><strong>*</strong></td>
<td rowspan="6"><strong>*</strong></td>
<td><code>-1 min</code></td><td></td>
</tr>
<tr><td><code>-1 mins</code></td><td></td></tr>
<tr><td><code>-1 minutes</code></td><td></td></tr>
<tr><td><code>1 min ago</code></td><td></td></tr>
<tr><td><code>previous mins</code></td><td></td></tr>
<tr><td><code>first min ago</code></td><td></td></tr>
<tr>
<td rowspan="5">日付を2週間後に</td>
<td rowspan="5"><strong>*</strong></td>
<td rowspan="5"><strong>*</strong></td>
<td rowspan="5">+14</td>
<td rowspan="5"><strong>*</strong></td>
<td rowspan="5"><strong>*</strong></td>
<td rowspan="5"><strong>*</strong></td>
<td rowspan="5"><strong>*</strong></td>
<td><code>+2 week</code></td><td></td>
</tr>
<tr><td><code>+2 weeks</code></td><td></td></tr>
<tr><td><code>2 weeks</code></td><td></td></tr>
<tr><td><code>next weeks next week</code></td><td></td></tr>
<tr><td><code>second weeks</code></td><td><ins><code>weeks</code>は必ず複数形</ins><br />(序数詞+週単位のケースが特例)</td></tr>
<tr>
<td rowspan="1">日付を月の初日に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">1</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>first day of</code></td><td rowspan="4"><code>of</code>に月を続けると自然言語風らしくなる</td>
</tr>
<tr>
<td rowspan="1">日付を月の末日に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>last day of</code></td>
</tr>
<tr>
<td rowspan="1">日付を月の2番目の木曜日に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>second Thursday of</code></td>
</tr>
<tr>
<td rowspan="1">日付を月の最後の水曜日に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>last Wednesday of</code></td>
</tr>
<tr>
<td rowspan="1"><strong>(今日を含む)</strong><br />次の木曜日</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>Thursday</code></td><td></td>
</tr>
<tr>
<td rowspan="1"><strong>(今日を含まない)</strong><br />次の火曜日</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>next Tuesday</code></td><td></td>
</tr>
<tr>
<td rowspan="1"><strong>(月曜を始まりとする)</strong><br />来週の月曜日</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>Monday next week</code></td><td></td>
</tr>
<tr>
<td rowspan="1"><strong>(月曜を始まりとする)</strong><br />先週の日曜日</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">特殊</td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>00</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td><code>Sunday previous week</code></td><td></td>
</tr>
<tr>
<td rowspan="1">タイムゾーン<strong>(名称)</strong>を<code>Asia/Tokyo</code>に</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">+9</td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1"><strong>*</strong></td>
<td rowspan="1">Asia/Tokyo</td>
<td><code>Asia/Tokyo</code></td><td>
<ul>
<li>時刻に影響を<ins>及ぼす</ins></li>
<li><code>DateTime::modify</code>では無視される</li>
</ul>
</td>
</tr>
</table>

# 日付時刻の入力書式定義のための書式

以下のメソッドで使われます。

- [DateTime::createFromFormat]
- [DateTimeImmutable::createFromFormat]

コンストラクタをそのまま使う際に問題であった解釈の曖昧性を、自分で書式定義することにより解消出来る有用なメソッドです。マニュアルに十分な説明があるので、具体例の紹介は割愛します。

# 日付時刻の出力書式

以下の関数やメソッドで使われます。

- [date]
- [idate]
- [DateTimeInterface::format]

マニュアルに十分な説明があるので、具体例の紹介は割愛します。

# 時間の入力書式

以下のメソッドで使われます。この書式は他とは大きく見た目が異なりますが、非常にシンプルなので理解に困ることは無いでしょう。

- [DateInterval::__construct]

マニュアルに十分な説明があるので、`P <*Y><*M><*D|*W> T <*H><*I><*S>`の形に従う通常の書式に関する具体例の紹介は簡便に済ませます。但し、このオブジェクトには**マニュアルに記載されていない**隠しプロパティが存在しており、それは[DateInterval::createFromDateString]を利用して生成することによってのみ設定することが可能なものです。

|PHPバージョン|隠しプロパティを直接書き換えようとしたときの挙動|
|:----:|:-----:|
|HHVM|`__set()`メソッドがコールされ、`Undefined property` として例外が投げられる |
|5.3.27以降<br />5.4.17以降<br />5.5.0以降| 全て `0` が設定される |
|5.3.26以前<br />5.4.16以前 | 意図通りの値が設定される |

上記のとおりPHP言語の開発者サイドから「触るんじゃねーぞ！」みたいな圧力を受けている気がするので、もし可能なバージョンであったとしても自分で書き換えるのはやめておいたほうが良さそうです。 

## マニュアルに記載されているプロパティ

### `y` `m` `d` `h` `i` `s`

| 書式 | `y` | `m` | `d` | `h` | `i` | `s` |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|`P2D`| 0 | 0 | 2 | 0 | 0 | 0 |
|`P2W`| 0 | 0 | 14 | 0 | 0 | 0 |
|`PT2S`| 0 | 0 | 0 | 0 | 0 | 2 | 
|`P6YT5M`| 6 | 0 | 0 | 0 | 5 | 0 | 

この方法で直接**負の値**を設定することは出来ません。設定するには以下の何れかを採用してください。

```php:オブジェクト生成後に個別にプロパティを設定する
$interval = new \DateInterval('P0D');
$interval->i = -1;
$interval->s = -30; 
```

```php:DateInterval&#58;&#58;createFromDateStringを利用してオブジェクトを生成する
$interval = \DateInterval::createFromDateString('-1 min -30 sec');
```

### `invert`

`0`または`1`の値を取る、正負を逆転させるフラグです。以下のメソッドで結果が負になる演算を行った時、<ins>負の値をプロパティに代入する代わりに</ins>このフラグがセットされた上で**正の値**が使われます。

- [DateTimeInterface::diff]

これを手動で有効にすれば負の値を設定したときと同じ効果が得られます。

```php:DateTimeImmutable&#58;&#58;addでの利用
// 正を負に反転させたものを加算する
$interval = new \DateInterval('P1D');
$interval->invert = 1;
$date = (new \DateTimeImmutable('2015-03-05'))->add($interval);
echo $date->format('Y-m-d') . PHP_EOL; // 2015-03-04

// 負を正に反転させたものを加算する
$interval = \DateInterval::createFromDateString('-1 day');
$interval->invert = 1;
$date = (new \DateTimeImmutable('2015-03-05'))->add($interval);
echo $date->format('Y-m-d') . PHP_EOL; // 2015-03-06
```

但し、このフラグを正しく扱えるのは以下の加減算に直接関わるメソッドに限られます。

- [DateTime::add]
- [DateTimeImmutable::add]
- [DateTimeInterface::diff]

```php:DateInterval&#58;&#58;createFromDateString経由では単に負の値が設定されるだけで、このフラグには影響を及ぼさない
$interval = \DateInterval::createFromDateString('-1 day');
var_dump($interval->d); // int(-1)
var_dump($interval->invert); // int(0)
```

```php:DatePeriodは負の間隔・invertフラグともに正しく取り扱えない
$a = new \DateTime('2015-02-15');
$b = new \DateTime('2015-02-20');
$negative_interval = \DateInterval::createFromDateString('-1 day');
$inverted_interval = new \DateInterval('P1D');
$inverted_interval->invert = 1;
$inverted_negative_interval = \DateInterval::createFromDateString('-1 day');
$inverted_negative_interval->invert = 1;

// 全て array(0) { }
var_dump(iterator_to_array(new \DatePeriod($b, $negative_interval, $a)));
var_dump(iterator_to_array(new \DatePeriod($b, $inverted_interval, $a))); 
var_dump(iterator_to_array(new \DatePeriod($b, $inverted_negative_interval, $a)));

// 全てメモリーリーク
var_dump(iterator_to_array(new \DatePeriod($a, $negative_interval, $b)));
var_dump(iterator_to_array(new \DatePeriod($a, $inverted_interval, $b)));
var_dump(iterator_to_array(new \DatePeriod($a, $inverted_negative_interval, $b)));
```

一貫性ガン無視な実装ですが、これもまたPHPならではの趣と言えるでしょう。

### `days`

総日数を表します。以下のメソッドで演算を行った時にのみ設定される特別なプロパティとされます。

- [DateTimeInterface::diff]

```php:DateTime&#58;&#58;diffで差を求める
$a = new \DateTime('2015-02-15');
$b = new \DateTime('2014-02-15');
$interval = $a->diff($b);
var_dump($interval->invert); // int(1)
var_dump($interval->y); // int(1)
var_dump($interval->m); // int(0)
var_dump($interval->d); // int(0)
var_dump($interval->days); // int(365)
```

## マニュアルに記載されていないプロパティ

### `first_last_day_of`

日数が月ごとに再計算されるかどうかを示すフラグです。
[do_adjust_special_early](https://github.com/php/php-src/blob/fc33f52d8c25997dd0711de3e07d0dc260a18c11/ext/date/lib/tm2unixtime.c#L283-L300)関数によって処理されます。

|`first_last_day_of`|意味|書式例|
|:--:|:--:|:---:|
|`1`|今月の初日|`first day of`|
|`2`|翌月の末日|`last day of next month`|

### `weekday` `weekday_behavior` `have_weekday_relative`

曜日を扱うフラグです。
[do_adjust_relative](https://github.com/php/php-src/blob/fc33f52d8c25997dd0711de3e07d0dc260a18c11/ext/date/lib/tm2unixtime.c#L192-L218)関数によって処理されます。

|`weekday`|`weekday_behavior`|`have_weekday_relative`|意味|書式|
|:--:|:--:|:---:|:---:|:---:|
|`3`|`1`|`1`|**(今日を含む)<br />**次の木曜日|`Wednesday`|
|`2`|`1`|`1`|**(今日を含まない)<br />**次の火曜日|`next Tuesday`|
|`1`|`2`|`1`|<strong>(月曜を始まりとする)</strong><br />今週の月曜日|`Monday this week`|
|`0`|`2`|`1`|<strong>(月曜を始まりとする)</strong><br />先週の日曜日|`Sunday previous week`|

### `special_type` `special_amount` `have_special_relative`

特別な処理を行うフラグです。`have_weekday_relative` と複合しているものもあります。
[do_adjust_special](https://github.com/php/php-src/blob/fc33f52d8c25997dd0711de3e07d0dc260a18c11/ext/date/lib/tm2unixtime.c#L270-L281)関数によって処理されます。

|`special_type`|`special_amount`|`have_special_relative`|意味|書式|
|:--:|:--:|:---:|:---:|:---:|
|`1`|`2`|`1`|2日間隔の平日|`second weekday`|
|`2`|`0`|`1`|今月の2番目の木曜日|`second Thursday of`|
|`3`|`0`|`1`|今月の最後の水曜日|`last Wednesday of `|

# 時間の出力書式

以下のメソッドで使われます。この書式は他とはやや見た目が異なりますが、非常にシンプルなので理解に困ることは無いでしょう。PHPを含むさまざまな言語で利用されている`printf`の書式に類似した形式です。

- [DateInterval::format]

マニュアルに十分な説明があるので、具体例の紹介は割愛します。但し1点だけ注意があります。

```php:自分でプロパティを設定すると、書式「%R%d」「%r%d」に従う出力が、マイナス2つが連なるものになり得る
$date = new \DateInterval('P0D');
$date->d = -1;
$date->invert = 1;
echo $date->format('%R%d') . PHP_EOL; // --1
echo $date->format('%r%d') . PHP_EOL; // --1
```

# 期間の入力書式

以下のメソッドで使われます。

- [DatePeriod::__construct]

3種類の引数の渡し方がありますが、このうち3番目の、以下に示される`$isostr`として利用されます。

```php
public DatePeriod::__construct ( string $isostr [, int $options ] )
```

ISO8601に従うとありますが、実際には更に以下の制約が付きます。

### 日付日時に対する制約

- 日付と時刻を両方含む必要があり、デリミタとして`T`が必要である。
- タイムゾーンは`UTC`固定であり、それを意味する`Z`がサフィックスとして必要である。

### 反復回数に対する制約

- 回数にはプレフィックス `R` が必要である。

### 全体としての制約

以下の何れかのフォーマットに従う必要がある。

- `開始日時/反復間隔/終了日時`
- `開始日時/終了日時/反復間隔`
- `開始日時/反復間隔/反復回数`
- `反復回数/開始日時/反復間隔`

```php:有効な例&nbsp;(終了日時を採用)
$period = new \DatePeriod('2015-01-01T00:00:00Z/P1D/2016-01-01T00:00:00Z');
var_dump(count(iterator_to_array($period))); // int(365)
```

```php:有効な例&nbsp;(反復回数を採用)
$period = new \DatePeriod('2015-01-01T00:00:00Z/P1D/R364');
var_dump(count(iterator_to_array($period))); // int(365)
```

実際、3つの引数に分けて渡すこともできるので、無理に厳しい制約に従いつつ引数を1つにしなくても特に問題はないでしょう。

[DatePeriod]: http://www.php.net/manual/ja/class.dateperiod.php
[DateTime]: http://www.php.net/manual/ja/class.datetime.php
[DateTimeImmutable]: http://www.php.net/manual/ja/class.datetimeimmutable.php
[DateInterval]: http://www.php.net/manual/ja/class.dateinterval.php
[DateTimeZone]: http://www.php.net/manual/ja/class.datetimezone.php
[DateTimeInterface::getTimestamp]: http://www.php.net/manual/ja/datetime.gettimestamp.php
[DateTime::createFromFormat]: http://www.php.net/manual/ja/datetime.createfromformat.php
[DateTimeImmutable::createFromFormat]: http://www.php.net/manual/ja/datetimeimmutable.createfromformat.php
[DateTime::__construct]: http://www.php.net/manual/ja/datetime.construct.php
[DatePeriod::__construct]: http://www.php.net/manual/ja/dateperiod.construct.php
[DateTimeImmutable::__construct]: http://www.php.net/manual/ja/datetimeimmutable.construct.php
[DateInterval::__construct]: http://www.php.net/manual/ja/dateinterval.construct.php
[DateTime::modify]: http://www.php.net/manual/ja/datetime.modify.php
[DateTimeImmutable::modify]: http://www.php.net/manual/ja/datetimeimmutable.modify.php
[DateTime::add]: http://www.php.net/manual/ja/datetime.add.php
[DateTimeImmutable::add]: http://www.php.net/manual/ja/datetimeimmutable.add.php
[DateTimeInterface::diff]: http://www.php.net/manual/ja/datetime.diff.php
[DateTimeInterface::format]: http://www.php.net/manual/ja/datetime.format.php
[DateInterval::format]: http://www.php.net/manual/ja/dateinterval.format.php
[DateInterval::createFromDateString]: http://www.php.net/manual/ja/dateinterval.createfromdatestring.php
[DateTimeInterface::diff]: http://www.php.net/manual/ja/datetime.diff.php
[strtotime]: http://www.php.net/manual/ja/function.strtotime.php
[date]: http://www.php.net/manual/ja/function.date.php
[idate]: http://www.php.net/manual/ja/function.idate.php
[サポートする日付と時刻の書式]: http://www.php.net/manual/ja/datetime.formats.php
[相対的な書式]: http://www.php.net/manual/ja/datetime.formats.relative.php