label84/laravel-hours-helper
Generate Laravel Collections of time/date intervals between start and end with a given step. Customize output formatting, handle ranges crossing midnight or spanning days, and exclude specific intervals—ideal for meeting schedulers, calendars, and dropdown time selectors.
Installation:
composer require label84/laravel-hours-helper
No additional configuration or service provider registration is required.
First Use Case: Generate a collection of time slots between two times with a specified interval:
use Label84\HoursHelper\Facades\HoursHelper;
$slots = HoursHelper::create('09:00', '17:00', 30);
// Returns: ['09:00', '09:30', '10:00', ..., '17:00']
Where to Look First:
HoursHelper is the primary entry point.tests/HoursHelperTest.php demonstrates edge cases (e.g., midnight transitions, exclusions).// Generate 15-minute slots for a booking form
$timeSlots = HoursHelper::create('08:00', '18:00', 15, 'H:i');
return view('bookings.create', compact('timeSlots'));
<select name="time_slot">
@foreach($timeSlots as $slot)
<option>{{ $slot }}</option>
@endforeach
</select>
// Block lunch breaks (12:00–13:00) and a meeting (14:30–15:00)
$availableSlots = HoursHelper::create(
'09:00', '17:00', 30,
'H:i', [
['12:00', '12:59'],
['14:30', '15:00']
]
);
// Generate slots for a 2-day event (custom format for APIs)
$eventSlots = HoursHelper::create(
'2024-01-01 09:00', '2024-01-02 17:00', 60,
'Y-m-d H:i:s'
);
// Generate intervals for a range picker API endpoint
$request->validate(['start_time' => 'required', 'end_time' => 'required']);
$intervals = HoursHelper::create(
$request->start_time,
$request->end_time,
$request->interval_minutes ?? 30
);
return response()->json($intervals);
// Livewire component property
public function getTimeSlotsProperty() {
return HoursHelper::create('08:00', '18:00', 30, 'H:i');
}
Validation:
Combine with Laravel’s validation to ensure start < end and valid intervals:
use Illuminate\Validation\Rule;
$request->validate([
'start_time' => ['required', 'date_format:H:i'],
'end_time' => ['required', 'date_format:H:i', Rule::unique('events')->whereFn(function ($query) use ($request) {
return $query->where('start_time', '<=', $request->end_time)
->where('end_time', '>=', $request->start_time);
})],
'interval' => ['required', 'integer', 'min:1'],
]);
Caching Static Ranges: Cache frequently used ranges (e.g., business hours) to avoid recomputation:
$businessHours = Cache::remember('business_hours', now()->addHours(1), function () {
return HoursHelper::create('09:00', '17:00', 30);
});
Extending for Timezones:
Wrap calls with timezone-aware Carbon parsing:
$slots = HoursHelper::create(
Carbon::parse($request->start_time, config('app.timezone')),
Carbon::parse($request->end_time, config('app.timezone')),
$request->interval
);
Database Integration: Store generated slots as JSON in a database for complex scheduling:
$event->time_slots = HoursHelper::create(
$event->start_time,
$event->end_time,
$event->interval
)->toJson();
Testing: Mock the facade in unit tests:
HoursHelper::shouldReceive('create')
->once()
->with('09:00', '17:00', 30)
->andReturn(collect(['09:00', '09:30']));
Midnight Transitions:
23:00 to 01:00) may not behave as expected if not explicitly handled.$overnightSlots = HoursHelper::create('23:00', '01:00', 60);
// Returns: ['23:00', '00:00', '01:00']
Exclusion Overlaps:
['09:00', '10:00'] and ['09:30', '10:30']) may produce inconsistent results.$exclusions = [['09:00', '10:00'], ['09:30', '10:30']];
$mergedExclusions = $this->mergeOverlappingRanges($exclusions);
Timezone Assumptions:
$slots = HoursHelper::create(
Carbon::parse($startTime, config('app.timezone')),
Carbon::parse($endTime, config('app.timezone')),
$interval
);
Performance with Large Ranges:
00:00 to 23:59 with 1-minute intervals) may impact memory.$generator = function () use ($start, $end, $interval) {
$current = Carbon::parse($start);
while ($current->lte($end)) {
yield $current->format('H:i');
$current->addMinutes($interval);
}
};
Invalid Input Handling:
start < end or positive intervals.if ($start >= $end) {
throw new \InvalidArgumentException("Start time must be before end time.");
}
Inspect Collections:
Use dd() to debug generated collections:
$slots = HoursHelper::create('09:00', '17:00', 30);
dd($slots->all());
Check Exclusion Logic: Verify exclusions by logging intermediate steps:
$exclusions = [['12:00', '13:00']];
$slots = HoursHelper::create('09:00', '17:00', 30, 'H:i', $exclusions);
logger()->debug('Generated slots:', ['slots' => $slots->all()]);
Time Format Issues:
Ensure the format string matches your expectations (e.g., H:i vs. h:i A):
// 24-hour format
HoursHelper::create('09:00', '17:00', 30, 'H:i');
// 12-hour format with AM/PM
HoursHelper::create('09:00', '17:00', 30, '
How can I help you explore Laravel packages today?