Search code examples
phpdatetimefinance

Get financial registry every 15 days


I'm working on a system that controls the finances of the company. One of the features is to schedule fixed income/expenses that happens every 15 days.

I already have a function that schedules incomes/expenses on a weekly basis, and I tried to copy that to make it work every 15 days. This is the code:

$registry_list = array();

foreach ($list as $item) {
  /**
   * Day of month 01-31 of the income/expense.
   * This is the day the user created the first registered item.
   */
  $firstDay = $item->day;

  // How many days there is on the month
  $daysOnMonth = cal_days_in_month(CAL_GREGORIAN, $params['month'], $params['year']);

  // We check if the current month/year is greater than the first registry
  // expire_date is always day 01. The day itself is on the property 'day'
  if ($item->expire_date < $params['year'].$params['month'].'-01') {
    $newDate = new \DateTime($item->expire_date);
    $newYear = $newDate->format('Y');
    $newMonth = $newDate->format('m');
  
    $newDate = $newDate->setDate($newYear, $newMonth, $firstDay);
    $newDate = $newDate->format('l');
  
    $firstDay = strtotime("first ".$newDate." ".$params['year']."-".$params['month']);
    $firstDay = date('d', $firstDay);
  }
  
  // First registry
  $newItem = clone($item);
  $newItem->expire_date = $params['year'].'-'.$params['month'].'-'.$firstDay;
  $newItem = new Registry($newItem); // Just to format some data
  array_push($registry_list, $newItem);
  
  while ($firstDay < $daysOnMonth) {
    $firstDay = $firstDay + 14; // 14 because the day itself count as +1
  
    if ($firstDay <= $daysOnMonth) {
      $newItem=clone($item);
      $newItem->expire_date = $params['year'].'-'.$params['month'].'-'.$firstDay;
      $newItem = new Registry($newItem);
  
      array_push($registry_list, $newItem);
    }
  }
}

return $registry_list;

This code runs just fine when the month/year is the same as when the registry was created. But as it goes to the next month, sometimes it's not correct. For example, if I start it on 02/08/2022 (d/m/Y) the schedules for that month are:

  • 02/08/2022
  • 16/08/2022
  • 30/08/2022

Which is correct. However, when September starts it messes with the list. Since I'm starting on the first weekday (based on the first registry) it's not always correct. So September list is as follow:

  • 06/09/2022
  • 20/09/2022

Which is incorrect. This should be the correct dates:

  • 13/09/2022
  • 27/09/2022

Because the last one from August was 30/08/2022.

I just don't know how to identify when I need to skip the first week of the month based on the last month. I also don't want to always start counting from the date the registry was created, otherwise I would go a long way to find the correct date when we pass a year or 2.

Since it's business financial control, they tend to look further on the calendar.

Is there a way to fix this issue?


Solution

  • This is the kind of problem that DateInterval can solve pretty easily:

    /*
        This function returns all dates between two dates at the given interval
    
        Interval syntax: https://www.php.net/manual/en/dateinterval.construct.php
        Date format syntax: https://www.php.net/manual/en/datetime.format.php
    */
    function get_schedule_dates($start_date, $end_date, $interval, $format = 'Y-m-d')
    {
        $date = new DateTime($start_date);
        $end = new DateTime($end_date);
    
        $schedule = [$date->format($format)];
    
        while ($date->add(new DateInterval($interval)) <= $end)
            $schedule[] = $date->format($format);
    
        return $schedule;
    }
    
    var_dump(get_schedule_dates('2022-08-02', '2022-10-03', 'P14D', 'd/m/Y'));
    

    Yields

    array(5) {
      [0]=>
      string(10) "02/08/2022"
      [1]=>
      string(10) "16/08/2022"
      [2]=>
      string(10) "30/08/2022"
      [3]=>
      string(10) "13/09/2022"
      [4]=>
      string(10) "27/09/2022"
    }
    

    What you could do is get the expiry_date of the first item as the start date, then calculate a date in advance, say 2 years, based on that, and use that as the end date.

    You could then iterate through the scheduled dates and clone and create new Registry objects, pushing to $registry_list