0

I am looking for a way to determine the next occurrence of a certain day of a month. This would refer to a numbered day (e.g. the next 30th). Every month should always have one eligible date, so in case a particular month doesn't have the specified day, we wouldn't overflow to the next one, getting the last day of that month instead.

  • Carbon provides the nthOfMonth function, but it refers to weekdays.
  • I found a couple of answers, but they deal with getting that day next month instead of the next fitting occurrence of a day
    • This answer only provides a start date, while in this case we might be many months or years in the future and we want to "catch up" with a subscription from that point on
    • This answer seems closer, but it seems like it could be made more readable or less verbose with Carbon

Is there any built in function in Carbon that fits this use case? It seems odd to have the nthOfMonth function and other functions with "No Overflow" without getting this case covered in between.

Aridez
  • 452
  • 5
  • 17

2 Answers2

1

This function finds the next future occurrence of "nth" day of a month (not weekday). This function won't overflow if there aren't enough days in the month, but will jump to the next one if the "nth" day already passed since the closest future date where we can find would be the next month:

public function nextNthNoOverflow(int $nth, Carbon $from): Carbon
{
    // Get the fitting day on the $from month as a starting point
    // We do so without overflowing to take into account shorter months
    $day_in_month = $from->copy()->setUnitNoOverflow('day', $nth, 'month');

    // If the date found is greater than the $from starting date we already found the next day
    // Otherwise, jump to the next month without overflowing
    return $day_in_month->gt($from) ? $day_in_month : $day_in_month->addMonthNoOverflow();
}

Since we are using the $from date in the last comparison, we want to make sure to copy() it previously so it doesn't mess the date. Also, depending on your needs, you might consider including equal dates with gte() instead.

Aridez
  • 452
  • 5
  • 17
  • Be careful, if today is 31st and $nth is 31 too, then gt() returns false and the addMonthNoOverflow() gives the 30th of the next month (if it's a 30-days month) – KyleK Jan 06 '23 at 14:43
  • @KyleK that's the idea behind having one eligible date per month. Thinking of it as, for example, a subscription, if it is billed the 31st of March, the next month it should come around the 30th of April. Just persisting the "$nth" variable and last date is enough to calculate the next date correctly after that (31st of May). But following your comment, I guess that a more correct name for the function would be "nextNthOfMonthNoOverflow" following the carbon convention, I'll edit it :) – Aridez Jan 06 '23 at 16:01
0

To get the next occurrence of a certain day of a month using Carbon, you can use the next() method and pass it a closure that checks if the current date is the day you are looking for.

use Carbon\Carbon;

$date = Carbon::now(); $day = 30; // the 30th of the month

$nextOccurrence = $date->next(function (Carbon $date) use ($day) {
    return $date->day == $day; });

echo $nextOccurrence;

This will output the next occurrence of the 30th day of the month, taking into account the current date. If the current date is already the 30th of the month, it will return the current date.