web dev & more!

Restricting Dates of jQuery UI Datepicker with custom WordPress plugin

Published: November 21, 2016

Recently, I was tasked with adding a custom field to a WooCommerce order details section. This itself is not a major task and is well documented online, but this particular field had some special requirements. It had to

  1. Allow users to select a date for delivery/pickup
  2. Restrict possible order dates to days when the facility is open

Sounds simple enough, right? Just toss a calendar widget in there and limit the days to weekdays since weekends and holidays are the only days when this particular facility is closed. Weekends are easy, but holidays? That’s where it got a little bit weird. Take Labor Day for instance. It always falls on the first Monday in September. President’s Day falls on the third Monday of February each year. These wouldn’t be difficult to code for the current year, but we don’t want our calendar widget to only work for this year. It should work for every year, so we don’t have to remember to go update our code every December 31st.

This company also has special rules regarding holidays. For example, if the 4th of July falls on a Saturday, then they are closed the Friday before. If the 4th is on a Sunday, then employees get the following Monday off. All in all, there are 11 different holidays we need to prevent orders from happening on.

I wasn’t sure how this could all be done with jQuery, so I ended up doing it in PHP and then serving up an array of dates to be blocked off in the widget with an AJAX call.

PHP

In our PHP code, I went through the normal suggested steps of enabling AJAX calls in a plugin and ended up with the function `company_holidays_callback`. This function gets our current year, and pushes each holiday onto an array. It does this for the current year, and the following year. Figuring out the date for each holiday wouldn’t have been so simple without the PHP strtotime function. That was a life saver! And the  function `fri_or_mon` was the logic used to mark the previous Friday or the following Monday off of the calendar as per special rule of this particular company.

This script can also be found at https://gist.github.com/Dilden/e1fa597ee0b7ecf716a85956ab4a5e93

<?php

if( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly


// AJAX request to disable days on calendar in checkout
add_action( 'wp_ajax_company_holidays', 'company_holidays_callback' );
add_action( 'wp_ajax_nopriv_company_holidays', 'company_holidays_callback' );
function company_holidays_callback() {
    $holidays = [];
    $currentYear = $limitYear = (int) date('Y');

    do {
        // New Years
        $x = date('N', strtotime("first day of january $currentYear"));
        $day = date('d', strtotime("first day of january $currentYear"));
        array_push($holidays, [1, (int) ($day + fri_or_mon($x)), $currentYear]);

        // President's Day
        $day = date('d', strtotime("third monday of february $currentYear"));
        array_push($holidays, [2, (int) $day, $currentYear]);

        // Good Friday
        $month = date('m', strtotime("-2 day", easter_date($currentYear)));
        $day = date('d', strtotime("-2 day", easter_date($currentYear)));
        array_push($holidays, [(int) $month, (int) $day, $currentYear]);
        
        // Memorial Day
        $day = date('d', strtotime("last Monday of May $currentYear"));
        array_push($holidays, [5, (int) $day, $currentYear]);

        // July 4th
        $x = (int) date('N', strtotime("3 day", strtotime("July $currentYear")));
        $day = (int) date('d', strtotime("3 day", strtotime("July $currentYear")));
        array_push($holidays, [7, $day, $currentYear]);

        // Labor Day
        $day = date('d', strtotime("first Monday of September $currentYear"));
        array_push($holidays, [9, (int) $day, $currentYear]);

        // Veteran's Day
        $x = date('N', strtotime("10 day", strtotime("November $currentYear")));
        $day = date('d', strtotime("10 day", strtotime("November $currentYear")));
        array_push($holidays, [11, (int) ($day + fri_or_mon($x)), $currentYear]);

        // Thanksgiving + Black Friday
        $day = date('d', strtotime("fourth Thursday of November $currentYear"));
        array_push($holidays, [11, (int) $day, $currentYear]);
        array_push($holidays, [11, (int) $day + 1, $currentYear]);

        // Xmas eve
        $day = date('d', strtotime("last day of December $currentYear"));
        $day = ((int) $day) - 7;
        array_push($holidays, [12, $day, $currentYear]);

        // Xmas
        $twenty_fifth = strtotime("-6 day", strtotime("last day of December $currentYear"));
        $x = date('N', $twenty_fifth);
        $day = date('d', $twenty_fifth);
        array_push($holidays, [12, $day + fri_or_mon($x), $currentYear]);
        $currentYear++;
    } while ($currentYear < $limitYear + 2);

    wp_send_json($holidays);
}

// Accepts a numerical value for the day of the week. Returns # of days to be added
function fri_or_mon($dayofweek) {
    if($dayofweek == 6) {
        // we get the previous Friday off
        return -1;
    }
    elseif($dayofweek == 7) {
        // we get the following Monday off
        return 1;
    }
    else {
        return 0;
    }
}

jQuery

To break down the jQuery, we create the instance of our datepicker on the element `#order_fulfillment_date`. If that element exists on the page, we perform our AJAX call to get the list of dates that need to be restricted. The function `noWeekendsOrHolidays` checks to if the date is a weekend. If it is, it gets disabled. If it isn’t, it passes the day onto `nationalDays` where we check if the date is one of our holidays that needs to be disabled as well.


In all of this code there isn’t really anything special or groundbreaking, but I thought the logic that determined the holidays was useful and maybe someone else could save themselves a little bit of time and headaches. Thanks for reading!