Reputation:
This function here basically gets the start and end date and loops through all the monthly dates in between these dates. The problem i am facing is the accuracy if user gives the date as 2018-01-30 (Y-m-d format) and it will skip Feb and instead give a date 2018-03-02 (Y-m-d format) which is not desired. Instead i want the date to be set as 2018-02-28. Also, for dates like 2017-01-31 months which does not have 31 should instead set the date to 30th of their month.
Thanks !
/**
* @param $startDate
* @param $endDate
* @return array
*/
public function calculateDaysOfMonth($startDate, $endDate){
$begin = new \DateTime($startDate);
$end = new \DateTime($endDate);
//For including last date in DatePeriod
$end = $end->modify('+1 day');
$days = array();
$interval = new \DateInterval('P1M');
$dateRange = new \DatePeriod($begin , $interval, $end);
foreach ($dateRange as $date) {
$days[] = $date;
}
return $days;
}
Upvotes: 2
Views: 124
Reputation:
Hello everyone I solved this issue and now I am providing the solution for such cases where we need consistency from calendar POV.
<?php
/**
* @param $startDate
* @param $endDate
* @return array
*/
function calculateDaysOfMonth($startDate, $endDate){
$begin = new DateTime($startDate);
$end = new DateTime($endDate);
$days = array();
//Special case of February
if($begin->format('d')== 29){
$end = $end->modify('+1 month');
$interval = new DateInterval('P1M');
$dateRange = new DatePeriod($begin, $interval, $end);
foreach($dateRange as $date){
if($date->format('m')<02){
$days[] = $date;
}
elseif($date->format('m')==3){
$days[] = $date->modify('-1 day');
}
elseif($date->format('m')%2==0){
$days[] = $date->modify('-3 day');
}
elseif ($date->format('m')%2!=0 && $date->format('m')>3){
$days[] = $date->modify('-2 day');
}
}
//var_dump($days);die();
return $days;
}
//Months that dont have dates 30 and 31
else if($begin->format('d')==30 || $begin->format('d')==31){
$time1 = strtotime($begin->format('Y-m-d'));
$time2 = strtotime($end->format('Y-m-d'));
$my = date('mY', $time2);
$months = array(date('Y-m-t', $time1));
$f = '';
while($time1 < $time2) {
$time1 = strtotime((date('Y-m-d', $time1).' +15days'));
if(date('F', $time1) != $f) {
$f = date('F', $time1);
if(date('mY', $time1) != $my && ($time1 < $time2))
$months[] = date('Y-m-t', $time1);
}
}
$months[] = date('Y-m-d', $time2);
return $months;
}
}
$a = calculateDaysOfMonth('2018-01-29', '2018-06-30');
foreach($a as $b){
var_dump($b);
}
The output is this
object(DateTime)#6 (3) {
["date"]=>
string(26) "2018-01-29 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
object(DateTime)#7 (3) {
["date"]=>
string(26) "2018-02-28 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
object(DateTime)#8 (3) {
["date"]=>
string(26) "2018-03-29 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
object(DateTime)#9 (3) {
["date"]=>
string(26) "2018-04-29 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
object(DateTime)#10 (3) {
["date"]=>
string(26) "2018-05-29 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
object(DateTime)#11 (3) {
["date"]=>
string(26) "2018-06-29 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(10) "US/Pacific"
}
Upvotes: 1
Reputation: 2958
Just use IntlCalendar.
$startDate = '2018-03-06';
$endDate = '2019-01-30';
$aField = array(
IntlCalendar::FIELD_YEAR,
IntlCalendar::FIELD_MONTH,
IntlCalendar::FIELD_DATE,
);
// start date
$oStartDate = IntlCalendar::createInstance();
$oStartDate->setLenient(FALSE); // strict interpretation of dates
foreach(explode('-', $startDate) as $i => $v) {
$v = (int) $v;
if ($i == 1) $v--; // months are off by one
$sField = $aField[$i];
$iMin = $oStartDate->getActualMinimum($sField);
$iMax = $oStartDate->getActualMaximum($sField);
if ($v < $iMin) {$v = $iMin;}
if ($v > $iMax) {$v = $iMax;}
$oStartDate->set($sField, $v);
}
// end date
$oEndDate = IntlCalendar::createInstance();
$oEndDate->setLenient(FALSE); // strict interpretation of dates
foreach(explode('-', $endDate) as $i => $v) {
$v = (int) $v;
if ($i == 1) $v--; // months are off by one
$sField = $aField[$i];
$iMin = $oEndDate->getActualMinimum($sField);
$iMax = $oEndDate->getActualMaximum($sField);
if ($v < $iMin) {$v = $iMin; }
if ($v > $iMax) {$v = $iMax; }
$oEndDate->set($sField, $v);
}
// date formatting
$oFmt = new IntlDateFormatter(
Locale::getDefault(),
IntlDateFormatter::SHORT, // datetype
IntlDateFormatter::NONE // timetype
);
$oFmt->setPattern('yyyy-MM-dd');
// calculate difference, sets values in object..
$oCalcDate = clone $oStartDate;
$iDays = $oCalcDate->fieldDifference($oEndDate->getTime(), IntlCalendar::FIELD_DATE);
// output date range
for ($i=0; $i<$iDays; $i++) {
echo $oFmt->format($oStartDate) . PHP_EOL;
$oStartDate->add(IntlCalendar::FIELD_DATE, 1);
}
Upvotes: 0