これは直感に反してイライラするというOPの意見に同意しますが、 +1 month
ますが、これが発生するシナリオでの意味をことにもなります。次の例を検討してください。
2015-01-31から始めて、月に6回追加して、メールニュースレターを送信するためのスケジュールサイクルを取得します。OPの最初の期待を念頭に置いて、これは戻ります:
- 2015-01-31
- 2015-02-28
- 2015-03-31
- 2015-04-30
- 2015-05-31
- 2015-06-30
すぐに、私たちが+1 month
意味することを期待していることに注意してくださいlast day of month
か、あるいは、反復ごとに1か月を追加ますが、常に開始点を参照してください。これを「月の最後の日」と解釈する代わりに、「翌月の31日またはその月の最後の利用可能日」と読むことができます。つまり、5月30日ではなく4月30日から5月31日にジャンプします。これは、「月の最後の日」ではなく、「開始月の日付に最も近い日付」を求めていることに注意してください。
したがって、あるユーザーが別のニュースレターを購読して2015-01-30に開始するとします。直感的な日付は+1 month
何ですか?1つの解釈は、「翌月の30日または利用可能な最も近い日」となり、次のように返されます。
- 2015-01-30
- 2015-02-28
- 2015-03-30
- 2015-04-30
- 2015-05-30
- 2015-06-30
これは、ユーザーが同じ日に両方のニュースレターを受け取る場合を除き、問題ありません。これが需要側ではなく供給側の問題であると仮定しましょう。ユーザーが同じ日に2通のニュースレターを受信することに煩わされることはありませんが、メールサーバーは2回送信するための帯域幅を利用できません。多くのニュースレター。このことを念頭に置いて、「各月の2日目から最後の日に送信する」という「+1か月」の別の解釈に戻ります。
- 2015-01-30
- 2015-02-27
- 2015-03-30
- 2015-04-29
- 2015-05-30
- 2015-06-29
これで、最初のセットとの重複を回避しましたが、4月と6月29日に終了します。これは、+1 month
単純に戻る必要がある元の直感m/$d/Y
またはm/30/Y
可能なすべての月について魅力的でシンプルなものと確かに一致します。それでは、+1 month
両方の日付を使用する3つ目の解釈を考えてみましょう。
1月31日
- 2015-01-31
- 2015-03-03
- 2015-03-31
- 2015-05-01
- 2015-05-31
- 2015-07-01
1月30日
- 2015-01-30
- 2015-03-02
- 2015-03-30
- 2015-04-30
- 2015-05-30
- 2015-06-30
上記にはいくつかの問題があります。2月はスキップされます。これは、サプライエンド(たとえば、月ごとの帯域幅割り当てがあり、2月が無駄になり、3月が2倍になる場合)とデマンドエンド(ユーザーが2月からだまされて、余分な3月を知覚する場合)の両方で問題になる可能性があります。間違いを修正する試みとして)。一方、2つの日付セットに注意してください。
- 決して重ならない
- その月に日付があるときは常に同じ日付にあります(したがって、1月30日のセットはかなりきれいに見えます)
- 「正しい」日付とみなされる可能性のあるものからすべて3日以内(ほとんどの場合1日)です。
- 後継者と前任者から少なくとも28日(太陰月)であるため、非常に均等に分散されます。
最後の2つのセットを考えると、日付の1つが実際の翌月から外れている場合(最初のセットで2月28日と4月30日にロールバックする)、いずれかの日付を単純にロールバックし、 「月の最後の日」対「月の2番目から最後の日」のパターンからの時折の重なりと相違。しかし、ライブラリが「最もきれい/自然」、「02/31の数学的解釈と他の月のオーバーフロー」、「月の最初または先月と比較して」のいずれかを選択すると、常に期待が満たされず、 「間違った」解釈がもたらす現実の問題を回避するために「間違った」日付を調整する必要があるいくつかのスケジュール。
繰り返しになり+1 month
ますが、実際には翌月の日付を返すことも期待しますが、直感ほど簡単ではなく、選択肢を考えると、Web開発者の期待を数学的に計算するのがおそらく安全な選択です。
以下は、他のソリューションと同じようにまだ不格好ですが、すばらしい結果が得られると思います。
foreach(range(0,5) as $count) {
$new_date = clone $date;
$new_date->modify("+$count month");
$expected_month = $count + 1;
$actual_month = $new_date->format("m");
if($expected_month != $actual_month) {
$new_date = clone $date;
$new_date->modify("+". ($count - 1) . " month");
$new_date->modify("+4 weeks");
}
echo "* " . nl2br($new_date->format("Y-m-d") . PHP_EOL);
}
それは最適ではありませんが、基本的なロジックは次のとおりです。1か月を追加すると、翌月の予定とは異なる日付になる場合は、その日付をスクラップして4週間を追加します。2つのテスト日の結果は次のとおりです。
1月31日
- 2015-01-31
- 2015-02-28
- 2015-03-31
- 2015-04-28
- 2015-05-31
- 2015-06-28
1月30日
- 2015-01-30
- 2015-02-27
- 2015-03-30
- 2015-04-30
- 2015-05-30
- 2015-06-30
(私のコードは混乱しており、複数年のシナリオでは機能しません。基礎となる前提がそのまま維持される限り、つまり、+ 1か月がファンキーな日付を返す場合は、誰でもソリューションをよりエレガントなコードで書き直すことを歓迎します。代わりに+4週間。)