開始日から終了日までの期間を月ごとに分割するアルゴリズム

<?php
	$samples=array();

	//サンプル日付
	$samples[]=array('2012/12/25','2015/5/12');
	$samples[]=array('2012/12/12','2013/5/12');
	$samples[]=array('2012/5/12','2012/9/13');
	$samples[]=array('2012/11/12','2012/11/13');
	$samples[]=array('2012/11/12','2012/11/12');
	$samples[]=array('2012/12/12','2012/11/12');


	$interval=3;

	foreach($samples as $sample){

		$start=$sample[0];
		$end=$sample[1];
		$rs=termMonthSplit($start,$end,$interval);

		//結果出力
		output($rs,$start,$end,$interval);

	}

	//結果出力
	function output($rs,$start,$end,$interval){
		echo "<hr>";
		echo "{$start} ~ {$end} 間隔:{$interval}月毎<br>";

		if(empty($rs)){
		echo "<div class='result'>Null</div>";
		}else{
		foreach($rs as $i=> $ent){
		echo "<div class='result'>{$i}:  {$ent[0]} ~ {$ent[1]}</div>";
		}


		}

	}

	/**
	 * 開始日から終了日までの期間を月ごとに分割する。
	 * 月間隔を指定するなら季節ごとの分割も再現できる。(1月を基準とし、月間隔値の倍数で分割)
	 * @param $start 開始日
	 * @param $end 終了日
	 * @param $interval 月間隔
	 * @return 期間分割リスト
	 */
	function termMonthSplit($start,$end,$interval){

		//Y-m-d形式にする。
		$start=date('Y-m-d',strtotime($start));
		$end=date('Y-m-d',strtotime($end));

		// 開始日を年月日に分割
		$start_y=date('Y', strtotime($start));
		$start_m=date('m', strtotime($start));


		// 終了日を年月日に分割
		$end_y=date('Y', strtotime($end));
		$end_m=date('m', strtotime($end));


		$m1=0;	// 月値1
		$m2=0;	// 月値2
		$list=array();
		// 開始年から終了年までの年ループ
		for($y=$start_y ; $y <= $end_y ; $y++){

			// 年ループは初周回であるか?
			if($y == $start_y){
				$m1 = $start_m; // 月ループの月値1に開始月をセット

			}else{
				$m1 = 1;	// 月ループの月値1に1をセット
			}

			// 年ループは最周回であるか?
			if($y == $end_y){
				$m2 = $end_m;// 月ループの月値2に終了月をセット

			}else{
				$m2 = 12; // 月ループの月値2に12
			}

			// 月ループ	月値1から月値2までループ
			for($m=$m1 ; $m <= $m2 ; $m++){


				// 年ループの初周回且つ、月ループの初周回であるか?
				if($y == $start_y && $m == $m1){
					$list[]=$start; // リストに開始日を追加
				}

				// 年ループの最周回且つ、月ループの最周回であるか?
				if($y == $end_y && $m == $m2 ){
					$list[]=$end;// リストに最終日を追加
				}

				// 月値は月間隔の倍数であるか?
				if(($m % $interval) == 0){

 					$str_ym1=$y.'-'.$m.'-1';

					// 月値の月末を取得し、リストに追加
					$yml = date('Y-m-t',strtotime($str_ym1));

 					$list[]=$yml;

					// 次月の月初を取得し、リストに追加
					if($m == 12){
						$str_ym2=($y + 1).'-1-1';
					}else{
						$str_ym2=$y.'-'.($m + 1).'-1';
					}
					$ym1=date('Y-m-d',strtotime($str_ym2));
					$list[]=$ym1;

				}

			}
		}

		$list2=array_chunk($list,2);//開始年と終了年のリストになるよう構造変換

		return $list2;
	}


?>
	

結果出力


2012/12/25 ~ 2015/5/12 間隔:3月毎
0:  2012-12-25 ~ 2012-12-31
1:  2013-01-01 ~ 2013-03-31
2:  2013-04-01 ~ 2013-06-30
3:  2013-07-01 ~ 2013-09-30
4:  2013-10-01 ~ 2013-12-31
5:  2014-01-01 ~ 2014-03-31
6:  2014-04-01 ~ 2014-06-30
7:  2014-07-01 ~ 2014-09-30
8:  2014-10-01 ~ 2014-12-31
9:  2015-01-01 ~ 2015-03-31
10:  2015-04-01 ~ 2015-05-12

2012/12/12 ~ 2013/5/12 間隔:3月毎
0:  2012-12-12 ~ 2012-12-31
1:  2013-01-01 ~ 2013-03-31
2:  2013-04-01 ~ 2013-05-12

2012/5/12 ~ 2012/9/13 間隔:3月毎
0:  2012-05-12 ~ 2012-06-30
1:  2012-07-01 ~ 2012-09-13
2:  2012-09-30 ~ 2012-10-01

2012/11/12 ~ 2012/11/13 間隔:3月毎
0:  2012-11-12 ~ 2012-11-13

2012/11/12 ~ 2012/11/12 間隔:3月毎
0:  2012-11-12 ~ 2012-11-12

2012/12/12 ~ 2012/11/12 間隔:3月毎
Null