Search code examples
phplaraveldatepickerphp-carbon

Laravel Carbon - how to restrict weekends and evenings with current code?


I am currently working with an existing php site utilizing laravel's carbon for date picking. The below code seems to limit only how far out a person can pick a date, but I would like to add restrictions such as specific holiday dates and also weekends. What would be the best way to place these limits within this current setup?

        $callBackDateTime = Input::get("datetime");

        try {
            $dateTime = Carbon::createFromFormat("F d Y - h:iA", $callBackDateTime);
        } catch (InvalidArgumentException $err) {
            $dateTime = null;
        }

        if (!$dateTime) {
            return Redirect::back()->with("error-modal", "Invalid date or time, please try again.");
        }

        $now = new Carbon;
        $now->second = 0;
        $limit = new Carbon;
        $limit->addDays(60);
        $limit->hour = 23;
        $limit->minute = 59;
        $limit->second = 59;

        if ($dateTime->lt($now)) {
            return Redirect::back()->with("error-modal", "The date and time you entered is in the past. Please enter a date and time after now.");
        }

        if ($dateTime->gt($limit)) {
            return Redirect::back()->with("error-modal", "The date and time you entered is more than 60 days away. Please enter a date and time within the next 60 days.");
        }

        $this->orderInProgress = Input::get("submit");
        $this->orderInProgress = isset($this->orderInProgress) ? $this->orderInProgress : false;

Thank you for your suggestions in advance.

I have currently tried looking at the isWeekend functions and $holiday array, but I am unsure how these would work for this current code as it seems they are used for calculating difference in date.


Solution

  • I think we can do a few things here.

    1. The $limit code can entirely be removed since Carbon already has methods for doing what this check appears to be doing.
    2. You're right that isWeekend() is what you're looking for. If you're experiencing an issue with that during certain hours of the day, it's likely due to timezone issues. You'll need to provide the timezone to Carbon when you create the objects. Eg: Carbon::createFromFormat("F d Y - h:iA", $callBackDateTime, $timezone);
    3. You can make a new method for detecting holidays. Ideally you'll want to use a regional database or library such as azuyalabs/yasumi, and since some holidays move around from year to year this can get complicated, but I'll provide a rough example.
    // Our new holiday-checking function
    function isHoliday($date) {
        $holidays = [
            '2023-01-01', // New Year's Day
            '2023-12-25', // Christmas Day
            // Add more holidays here
        ];
    
        // Format the date as a string for easy comparison
        $formattedDate = $date->format('Y-m-d');
    
        // Check if the given date is a holiday
        return in_array($formattedDate, $holidays);
    }
    
    // Refactored existing code
    
    $callBackDateTime = Input::get("datetime");
    try {
        $dateTime = Carbon::createFromFormat("F d Y - h:iA", $callBackDateTime);
    } catch (InvalidArgumentException $err) {
        return Redirect::back()->with("error-modal", "Invalid date or time, please try again.");
    }
    
    // Normalize "now" to the current minute, rather than this specific second.
    $now = Carbon::now()->second(0);
    
    // Check if the date and time is in the past
    if ($dateTime->lt($now)) {
        return Redirect::back()->with("error-modal", "The date and time you entered is in the past. Please enter a date and time after now.");
    }
    
    // Check if the date and time is more than 60 days in the future.
    // We use "copy" here to ensure "now" isn't changed, in case we ever use it again.
    if ($dateTime->gt($now->copy()->addDays(60)->endOfDay())) {
        return Redirect::back()->with("error-modal", "The date and time you entered is more than 60 days away. Please enter a date and time within the next 60 days.");
    }
    
    // Check for weekends
    if ($dateTime->isWeekend()) {
        return Redirect::back()->with("error-modal", "The selected date is a weekend. Please choose a working day.");
    }
    
    // Check for holidays
    if (isHoliday($dateTime)) {
        return Redirect::back()->with("error-modal", "The selected date is a holiday. Please choose a working day.");
    }
    
    // Simplify the last two lines
    $this->orderInProgress = Input::get("submit") ?? false;