3
\$\begingroup\$

The University Library where I work is in the process of redesigned our home page. One of the features we wish to include is a widget to display the current hours. Unfortunately for a variety of reasons this must be done using client side scripting instead of a PHP script which pulls from Google Calendar. After much wailing and gnashing of teeth I have created such a script. It uses functions to see check if key dates fall within certain ranges and then assigns hours based on those results. After error checked it by manually setting the date assigned to the variable current everything seems fine. But its given me enough trouble that I want more experienced eyes to look at it and see if there any lingering issues I missed.

The script works as follows. After getting the current date and day of the week, the library's hours are assigned to a set of arrays. The set of variables which follows are the dates at which our schedule changes. Making each one a date object (as opposed to a number) seems to be the only way to compare apples to apples and not get an erroneous result. Also it makes it easier for non-programmers to update the script. In the functions which follows the current date is compared against these an if it falls within that range, the function returns true. The exception is the short_hours function which must exist because there are a handful of days out of the year when we are open from 7:30 AM to 5PM. Lastly the functions are called using a set of if statements and the one which is true sets the current hours from one of the arrays.

Flame away!

http://jsfiddle.net/mjcodelib/XZF59/

//Code written by Michael Paulmeno

var current = new Date();                           //creates new date object
var weekDay = current.getDay();                     //gets day of week (0-6 in Javascript)
var hours = document.getElementById("hours");       //stores code to simplify if statements

//The variables below set the library's operating hours 

var hours_regular = ["Sunday's Hours: 2:00PM to 10:00PM", "Monday's Hours: 7:30AM to 10:00PM", "Tuesday's Hours: 7:30AM to 10:00PM", "Wednesday's Hours: 7:30AM to 10:00PM","Thursday's Hours: 7:30AM to 10:00PM", "Friday's Hours: 7:30AM to 4:00PM", "Closed Today"];  
var hours_exam = ["Sunday's Hours: 2:00PM to 12:00PM", "Monday's Hours: 7:30AM to 12:00PM", "Tuesday's Hours: 7:30AM to 12:00PM", "Wednesday's Hours: 7:30AM to 12:00PM", "Thursday's Hours: 7:30AM to 12:00PM", "Friday's Hours: 7:30AM to 4:00PM", "Closed Today"];
var hours_intercession = ["Closed Today", "Monday's Hours: 8:00AM to 4:30PM", "Tuesday's Hours: 8:00AM to 4:30PM", "Wednesday's Hours: 8:00AM to 4:30PM", "Thursday's Hours: 8:00AM to 4:30PM", "Friday's Hours: 8:00AM to 4:00PM", "Closed Today", "Today's Hours: 8am - 5pm"];
var hours_august_intercession = ["Closed Today", "Monday's Hours: 8:00AM to 5:00PM", "Tuesday's Hours: 8:00AM to 5:00PM", "Wednesday's Hours: 8:00AM to 5:00PM", "Thursday's Hours: 8:00AM to 5:00PM", "Friday's Hours: 8:00AM to 4:00PM", "Closed Today"];
var hours_shortened = "Today's Hours: 7:30AM to 5:00PM";


//These are the 'important dates' for the year.  At each of these dates our operating hours change.  The format is year + month + date. 
//Change the values in parenthesis every year to reflect the important dates for the next academic year.
//Note that in Javascript counting begins at 0, so January is month 0 and December is month 11.

//Fall Dates
var fall_semester_begin = new Date (2014,7,18);
var fall_break_begin = new Date (2014,9,15);
var fall_break_end = new Date (2014,9,20);
var fall_exams = new Date (2014,11,8);
var thanksgiving_break = new Date (2014,10,24);
var thanksgiving_break_end = new Date (2014,11,1);
var winter_intercession = new Date (2014,11,14);
var winter_holidays = new Date (2015,11,23);

//Spring Dates
var winter_intercession2 = new Date (2015,0,1);
var spring_semester_begin = new Date (2015,0,12);
var spring_break_begin = new Date (2015,2,9);
var spring_break_end = new Date (2015,2,16);
var spring_exams = new Date (2015,4,4);

//Summer Dates
var may_intercession = new Date (2014,4,11);
var summer_hours_regular = new Date(2014,5,2);
var august_intercession = new Date (2014,6,31);


//Do not touch anything below this line.
//The functions below compare the current date as formatted above to the important dates to determine at what point in the year the current date falls in.

function regular_hours_spring (today) {
    if (today >= spring_break_end   && today < spring_exams ||      
        today >= spring_semester_begin && today < spring_break_begin ) 
        {       
        return true; }
    else {
        return false;
    }
}

function regular_hours_summer (today){
    if (today >= summer_hours_regular &&
        today < august_intercession) 
        {
        return true;}
    else {return false;}
}

function regular_hours_fall (today) {
    if (today >= thanksgiving_break_end && today < fall_exams ||
        today >= fall_break_end && today < thanksgiving_break ||
        today >= fall_semester_begin && today < fall_break_begin) 
        {       
        return true; }
    else {
        return false;
    }
}

function exam_hours (today) {
    if (today >= fall_exams && today <= winter_intercession ||
        today >= spring_exams && today <= may_intercession) 
        {       
        return true; }
    else {
        return false;
    }
}

function intercession_hours (today) {
    if (today >= winter_intercession && today < winter_holidays         ||
        today >= thanksgiving_break && today < thanksgiving_break_end   ||
        today > fall_break_begin && today < fall_break_end              ||
        today >= august_intercession && today < fall_semester_begin     ||
        today >= may_intercession   && today < summer_hours_regular     ||
        today > spring_break_begin && today < spring_break_end          ||
        today > winter_intercession2 && today < spring_semester_begin) 
        {       
        return true; }
    else {
        return false;
    }
}

function short_hours (today) {
    if (fall_break_begin && today > fall_semester_begin) 
        {       
        return true; }
    else {
        return false; }
}

function closed (today) {
    if (today >= winter_holidays) 
        {
        return true;
        }
    else {
        return false; }
}

//The value returned by the functions above is logged to the error console for troubleshooting

console.log (regular_hours_spring(current));
console.log (regular_hours_summer(current));
console.log (regular_hours_fall(current));
console.log (exam_hours(current));
console.log (intercession_hours(current));
console.log (short_hours(current));
console.log (closed(current));


//The if statements below call the functions above and determine whether the hours displayed should be regular, exam, or intercession.

if (closed (current) && current >= winter_holidays) {
    hours.innerHTML = "Closed Today";
} 
else if(intercession_hours(current) && current >= august_intercession && current < fall_semester_begin) {
        hours.innerHTML = hours_august_intercession[weekDay];
}
else if(intercession_hours(current)) {
    hours.innerHTML = hours_intercession[weekDay];
}
else if (exam_hours (current)) {
    hours.innerHTML = hours_exam[weekDay];
} 
else if(regular_hours_spring(current)){
    hours.innerHTML = hours_intercession[weekDay]; 
} 
else if (regular_hours_summer (current)){
    hours.innerHTML = hours_regular[weekDay];
} 
else if (regular_hours_fall (current)){
    hours.innerHTML = hours_regular[weekDay];
} 
else if (short_hours ()) {
    hours.innerHTML = hours_shortened;
} 
else {hours.innerHTML = "<a href='http://www.deltastate.edu/academics/libraries/libraries-hours-of-operation/' target='_blank'>Click here for Library hours</a>"};
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

I agree with Marc-Andre, for a situation like this, you don't want abstract out the data. Don't tie your logic so specific values. In the code below, I pulled out your date information into a list of terms that describe themselves. You can add or remove any term without any need to change the logic of the code.

Code

//The variables below set the library's operating hours 
var determineLibraryhours = (function () {

    var hours = {
        regular: ["Sunday's Hours: 2:00PM to 10:00PM", "Monday's Hours: 7:30AM to 10:00PM", "Tuesday's Hours: 7:30AM to 10:00PM", "Wednesday's Hours: 7:30AM to 10:00PM", "Thursday's Hours: 7:30AM to 10:00PM", "Friday's Hours: 7:30AM to 4:00PM", "Closed Today"],
        exams: ["Sunday's Hours: 2:00PM to 12:00PM", "Monday's Hours: 7:30AM to 12:00PM", "Tuesday's Hours: 7:30AM to 12:00PM", "Wednesday's Hours: 7:30AM to 12:00PM", "Thursday's Hours: 7:30AM to 12:00PM", "Friday's Hours: 7:30AM to 4:00PM", "Closed Today"],
        intercession: ["Closed Today", "Monday's Hours: 8:00AM to 4:30PM", "Tuesday's Hours: 8:00AM to 4:30PM", "Wednesday's Hours: 8:00AM to 4:30PM", "Thursday's Hours: 8:00AM to 4:30PM", "Friday's Hours: 8:00AM to 4:00PM", "Closed Today", "Today's Hours: 8am - 5pm"],
        august_intercession: ["Closed Today", "Monday's Hours: 8:00AM to 5:00PM", "Tuesday's Hours: 8:00AM to 5:00PM", "Wednesday's Hours: 8:00AM to 5:00PM", "Thursday's Hours: 8:00AM to 5:00PM", "Friday's Hours: 8:00AM to 4:00PM", "Closed Today"],
        shortened: "Today's Hours: 7:30AM to 5:00PM",
        closed: "Closed Today",
        unspecified: "<a href='http://www.deltastate.edu/academics/libraries/libraries-hours-of-operation/' target='_blank'>Click here for Library hours</a>"
    };


    //These are the 'important dates' for the year.  At each of these dates our operating hours change.  The format is year + month + date. 
    //Change the values in parenthesis every year to reflect the important dates for the next academic year.
    //Note that in Javascript counting begins at 0, so January is month 0 and December is month 11.

    var terms = [{
        id: "fallsemester2014",
        type: "regular",
        begin: new Date(2014, 7, 18),
        exams_start: new Date(2014, 11, 8),
        end: new Date(2014, 11, 13),
        breaks: [{
            id: "fall",
            begin: new Date(2014, 9, 15),
            end: new Date(2014, 9, 20)
        }, {
            id: "thanksgiving",
            begin: new Date(2014, 10, 24),
            end: new Date(2014, 1, 1)
        }

        ]
    },

    {
        id: "winterintercession2014",
        type: "intercession",
        begin: new Date(2014, 11, 14),
        end: new Date(2014, 11, 22)
    },

    {
        id: "winterbreak2014",
        type: "closed",
        begin: new Date(2014, 11, 23),
        end: new Date(2014, 11, 31)
    },

    {
        id: "winterintercession22014",
        type: "intercession",
        begin: new Date(2015, 0, 1),
        end: new Date(2015, 0, 11)
    },

    {
        id: "springsemester2015",
        type: "regular",
        begin: new Date(2015, 0, 12),
        exams_start: new Date(2015, 4, 4),
        end: new Date(2015, 4, 10),
        breaks: [{
            id: "spring",
            begin: new Date(2015, 2, 9),
            end: new Date(2015, 2, 16)
        }]
    },

    {
        id: "mayintercession2014",
        type: "intercession",
        begin: new Date(2015, 4, 11),
        end: new Date(2015, 5, 1)
    }, {
        id: "summer2015",
        type: "regular",
        begin: new Date(2015, 5, 2),
        end: new Date(2015, 6, 31)
    }, {
        id: "augustintercession2014",
        type: "august_intercession",
        begin: new Date(2015, 6, 31),
        end: new Date(2015, 7, 16)
    }];

    function lookupDayType(date) {
        var currentTerm = terms.filter(function (term) {
            return term.begin <= date && date <= term.end;
        })[0];
        if (!currentTerm) {
            return "unspecified";
        }

        //checks for breaks
        if (currentTerm.breaks) {
            var isBreak = currentTerm.breaks.filter(function (b) {
                return b.begin <= date && date <= b.end;
            }).length > 0;
            if (isBreak) {
                return "intercession";
            }
        }

        //checks for exams
        if (currentTerm.exams_start && date >= currentTerm.exams_start) {
            return "exams";
        }

        //default to term type
        return currentTerm.type;
    }

    return function determineLibraryhours(date) {
        var dayType = lookupDayType(date),
            hoursLookup = hours[dayType];
        if ((!hoursLookup) || ((typeof hoursLookup) === "string")) {
            return hoursLookup;
        } else {
            return hoursLookup[date.getDay()];
        }
    };
}());


function runTest(explanation, date, expectedHours) {
    var hours = determineLibraryhours(date);
    if (hours === expectedHours) {
        console.log("[SUCCESS] " + explanation + ":" + date);
    } else {
        console.error("[FAIL] " + explanation + ":" + date + ":" + hours + "!=" + expectedHours);
    }
}


runTest("fall semester", new Date(2014, 7, 20), "Wednesday's Hours: 7:30AM to 10:00PM");
runTest("fall break", new Date(2014, 9, 17), "Friday's Hours: 8:00AM to 4:00PM");
runTest("fall exam", new Date(2014, 11, 10), "Wednesday's Hours: 7:30AM to 12:00PM");
runTest("winter intercession", new Date(2014, 11, 18), "Thursday's Hours: 8:00AM to 4:30PM");
runTest("winter break", new Date(2014, 11, 24), "Closed Today");
runTest("spring semester", new Date(2015, 0, 20), "Tuesday's Hours: 7:30AM to 10:00PM");
runTest("spring break", new Date(2015, 2, 16), "Monday's Hours: 8:00AM to 4:30PM");
runTest("spring exam", new Date(2015, 4, 5), "Tuesday's Hours: 7:30AM to 12:00PM");
runTest("may intercession", new Date(2015, 4, 15), "Friday's Hours: 8:00AM to 4:00PM");
runTest("summer", new Date(2015, 5, 5), "Friday's Hours: 7:30AM to 4:00PM");
runTest("august intercession", new Date(2015, 7, 3), "Monday's Hours: 8:00AM to 5:00PM");


hours.innerHTML = determineLibraryhours(new Date());

JSFIDDLE

http://jsfiddle.net/XZF59/2/

\$\endgroup\$
3
  • \$\begingroup\$ Creating an object to hold each term makes more sense from a programming point of view. A while back the thought had occurred to me to use the Javascript equivalent of a multidimensional array in PHP, but I wasn't sure what that was. Your approach is more flexible too so if say a federal holiday falls within a term that can be accounted for. Thank you very much for your help. Tomorrow I will play around with setting different dates and commenting the code. \$\endgroup\$
    – Mike P
    Jul 21, 2014 at 22:47
  • \$\begingroup\$ Depending on how you end up using this, my data structure may or may not work, this is just one I settled with. It might be easier to pull out holidays and breaks into a separate list and process them before moving on to the terms, or something totally different. \$\endgroup\$
    – pgreen2
    Jul 21, 2014 at 23:10
  • \$\begingroup\$ Well I already have a function which handles holidays separately and will see if that works. The script is ultimately used to put one line of text on our new home page with the day's opening hours so your structure should work fine. \$\endgroup\$
    – Mike P
    Jul 22, 2014 at 13:37
2
\$\begingroup\$

I'm not an expert in JavaScript, but there is some things I can say about your code.

Naming

Unfortunately for a variety of reasons this must be done using client side scripting instead of a PHP script which pulls from Google Calendar

Even if it's not your first option and not the one you desire, you need to have the same standard as your regular. Have you read some style convention ? Almost all your variables names and functions names are not following any "standard" of JavaScript.

Jsfiddle

You provided a jsfiddle (which is nice), but do know that there is a TidyUp and JSHint buttons that will clear most of the commons (syntax and indentation) mistakes. I was surprised that there was still some errors left (only two but there should have been none).

Bad Idea

I don't know if you really have another option (as I don't if you have some database or what all your system looks like), but this right here is something you should evade at all cost!

//Fall Dates
var fall_semester_begin = new Date (2014,7,18);
var fall_break_begin = new Date (2014,9,15);
var fall_break_end = new Date (2014,9,20);
var fall_exams = new Date (2014,11,8);
var thanksgiving_break = new Date (2014,10,24);
var thanksgiving_break_end = new Date (2014,11,1);
var winter_intercession = new Date (2014,11,14);
var winter_holidays = new Date (2015,11,23);

//Spring Dates
var winter_intercession2 = new Date (2015,0,1);
var spring_semester_begin = new Date (2015,0,12);
var spring_break_begin = new Date (2015,2,9);
var spring_break_end = new Date (2015,2,16);
var spring_exams = new Date (2015,4,4);

//Summer Dates
var may_intercession = new Date (2014,4,11);
var summer_hours_regular = new Date(2014,5,2);
var august_intercession = new Date (2014,6,31);

As a maintainer, I don't want to open a script, change dates, repackage everything, tests everything to make sure I didn't broke something just for some dates. Worse, I will need to do it every year, since it's will never be the same thing. There is also a risk that nobody will think to change the values in time for the new session, which would be easily avoidable with an external source of date (since it would not need a code change).

You should be able to grab this data from an external source which will feed you the good values. A JSON object containing the data could be an option, querying a service which return you all you need could work too. If "this must be done using client side" means : I can't use an external source of data and I need to hard-code the values, then disregards this.

\$\endgroup\$
3
  • \$\begingroup\$ The campus web presence is managed by the marketing department who WordPress as a CMS. It will not accept the PHP file I'd orginially planned to use to integrate with Google Calendar. Since I am not the admin and have no access to the webserver the Javascript file above seems to be the best way. I did consider JSON, but do not know much about it. As for the long list of variables, my feeling is that a non-programmer would have an easier time with that than if they were confronted with an array. If there is a way to do this without having to change values once a year I am all ears. \$\endgroup\$
    – Mike P
    Jul 21, 2014 at 20:19
  • \$\begingroup\$ @MikeP The thing I'm more worried about is not a long list of variables, but really the hard-coding problem. But you must read my last phrase ` If "this must be done using client side" means : I can't use an external source of data and I need to hard-code the values, then disregards this`. If there was no way at the start to do it cleanly, I guess there not much to do. But for a non-programmer editing anything in a script is what I could call a bad idea. The real problem is you do not have the tools to do your job. \$\endgroup\$
    – Marc-Andre
    Jul 21, 2014 at 21:19
  • \$\begingroup\$ That is a problem of which I am well aware. I am wondering if the script is as good as it can get which seems to be the case. \$\endgroup\$
    – Mike P
    Jul 21, 2014 at 21:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.