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:
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:
Which is incorrect. This should be the correct dates:
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?
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