Austin Nieset
Austin Nieset

Reputation: 61

Is there a way to skip specific days in Carbon?

I have a calculate shipping days method. I need to be able to add days onto a Carbon instance but i need to skip adding a day based on if its a specific day of the week, not necessarily Saturday/Sunday, it could be Monday or Tuesday as well. I cant seem to find anything in the Carbon Docs that specifies blacklisting certain days of the week when adding days.

Upvotes: 1

Views: 986

Answers (2)

Martin Tonev
Martin Tonev

Reputation: 775

Yes you can use CarbonPeriod and method filter

$month = CarbonPeriod::between($start, Carbon::now()->endOfMonth())
        ->filter(fn ($date) => !$date->isMonday() || !$date->isSunday() )
        ->toArray();

This way you can skip days from your array of days

Upvotes: 0

KyleK
KyleK

Reputation: 5121

A library for Carbon helps to set open/closed days in a week and so you can:

https://github.com/kylekatarnls/business-time

And you could do:

BusinessTime::enable(Carbon::class, [
  'monday'.   => [],
  'tuesday'   => ['00:00-24:00'],
  'wednesday' => ['00:00-24:00'],
  'thursday'  => ['00:00-24:00'],
  'friday'    => [],
  'saturday'  => ['00:00-24:00'],
  'sunday'    => ['00:00-24:00'],
]);

$date = Carbon::parse('2019-06-01');

echo $date->addOpenTime('4 days');

This add 4 days skipping Friday and Monday.

You could basically get the same result with a loop adding days one by one, but it will be as slow as the interval is big. To optimize the operation, you should first add complete weeks:

$daysToAdd = 36;
$skippedDays = ['Monday', 'Friday'];
$daysPerWeek = 7 - count($skippedDays);
$completeWeeks = floor($daysToAdd / $daysPerWeek);

function skip(CarbonInterface $date, array $skippedDays): CarbonInterface {
  $date = $date->copy(); // if not using CarbonImmutable

  while (in_array($date->format('l'), $skippedDays)) {
    $date = $date->addDay();
  }

  return $date;
}

$start = Carbon::now(); // Or whatever date
$end = $start->copy()->addWeeks($completeWeeks); // ->copy() not needed if you use CarbonImmutable
$end = skip($end, $skippedDays);

// For each remaining days
for ($i = $daysToAdd % $daysPerWeek; $i--; $i > 0) {
  $end = skip($end->addDay(), $skippedDays);
}

This is a vanilla version for your specific case, but if you think someday, you will have to deal with hour-intervals/hour-precision instead of days, or will have special days (like holidays exceptions), then you should definitely use cmixin/business-time (link above).

Upvotes: 0

Related Questions