user6679664
user6679664

Reputation:

Date accuracy from calendar POV PHP

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

Answers (2)

user6679664
user6679664

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

Code4R7
Code4R7

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

Related Questions