Algorithm For Scheduling Around Weekends And Holidays Scott Auge scott_auge@amduus.com sauge@amduus.com Introduction We have all heard the phrase x business days for something to be delivered or done. It could be sales goods to a buyer, inventory moving between warehouses for manufacture or distribution, among other uses. We recognize that business days doesn't include the weekends or holidays. But how do we quickly calculate that? That will be the point of this article. First off are holidays. Some holidays move around on the calendar, so one cannot simply mark days off programmatically. Some companies will use federal holidays while others are more discerning. Also, if your application is meant to be used internationally one cannot mark days off we need a database table to hold the dates of holidays. The second is the weekends. Luckily Progress ABL has a function called WeekDay() that will return 1 through 7 for Sunday through Saturday. It returns a number because it can be used with many languages internationally hence it is up to you for the preferred language. We also want a routine that can be used is WWW, GUI, and character programs. I use Progress Advanced Business Language because it is a pretty straight forward language and should be easily translated into others. Don't get caught up in the use of a character interface, the routine can work in GUI and WWW too. (Progress runs in all three environments.) It is just easier to test in character when I am on a UNIX computer. More information about Progress can be found at http://www.progress.com. A Database Table Needed First we need to create a database table to hold dates that will effect the scheduling of a job regarding holidays, days off, and the like. This will provide the general elements and types of the table if one wishes to put it in your own Progress application or translate it to another database: 06/17/15 10:08:51 PROGRESS Report Database: test (PROGRESS) ========================================================================= ============================= Table: Holiday ============================ Table Flags: "f" = frozen, "s" = a SQL table Table Table Field Index Table Name Flags Count Count Label -------------------------------- ----- ----- ----- ------------------------ Holiday 2 1 Holiday Dump Name: holiday Description: Dates for schedule effecting holidays Storage Area: Schema Area ============================= FIELD SUMMARY ============================= ============================= Table: Holiday ============================ Flags: <c>ase sensitive, <i>ndex component, <m>andatory, <v>iew component Order Field Name Data Type
Flags ----- -------------------------------- ----------- ----- 10 Name char 20 MDY date i Field Name Format -------------------------------- ----------------------------- Name x(8) MDY 99/99/99 Field Name Initial -------------------------------- ----------------------------- Name MDY? Field Name Label Column Label ------------------------------ ---------------------- ---------------------- Name Name Name MDY Date Date ============================= INDEX SUMMARY ============================= ============================= Table: Holiday ============================ Flags: <p>rimary, <u>nique, <w>ord, <a>bbreviated, <i>nactive, + asc, - desc Flags Index Name Cnt Field Name ----- -------------------------------- --- --------------------------------- pu pu 1 + MDY ** Index Name: pu Storage Area: Schema Area ============================= FIELD DETAILS ============================= ============================= Table: Holiday ============================ ** Field Name: Name Description: Name of the holiday ** Field Name: MDY Description: Date of the holiday Creating Sample Data Next, we need to create dates in the table... here is a quick Progress ABL program to create records in GUI and Character interfaces: define variable TheName as character no-undo. define variable TheDate as date noundo. update TheName TheDate. create Holiday. assign Holiday.Name = TheName Holiday.MDY = TheDate. The records we created: Name Date new year 01/01/15 Co Hol 01/02/15 We are going to use 12/31/14 as the start date for a sample set of data to use, so lets look at our days used: define variable increment as integer no-undo. /* Convert a date into it's week day designation */
function WeekDayName returns character (input Thedate as date): return entry(weekday(thedate), "Sun,Mon,Tues,Wed,Thur,Fri,Sat"). end. repeat: display 12/31/14 + increment WeekDayName(12/31/14 + increment). increment = increment + 1. if increment > 10 then leave. end. /* repeat */ Which returns us: 12/31/14 Wed 01/01/15 Thur 01/02/15 Fri 01/03/15 Sat 01/04/15 Sun 01/05/15 Mon 01/06/15 Tues 01/07/15 Wed 01/08/15 Thur 01/09/15 Fri We see our two holiday entries, which are Thursday and Friday. We see also that they border on the weekend. So now we have an idea of the data set now the program! The comments should be able to clue in the reader what is going on. Remember this in your programming! /* MoveScheduleDate.p Compute date in business days from a start date and number of days. Scott Auge 06/17/2015 Init */ define input parameter NumberOfDays as integer no-undo. define input parameter StartDate as date no-undo. define output parameter NewDate as date no-undo. define variable BusinessDays as integer no-undo. /* Start our date holder and business days holder */ NewDate = StartDate. BusinessDays = 0. repeat: /* These a means of catching extremes - if zero days, we are happy */ /* with what we have! */ if NumberOfDays = 0 then leave.
/* Using ABLs date tools, is the NewDate a weekend? We do this first */ /* because if even a Holiday is on a weekend, we want all weekends to */ /* to be overlooked. */ if weekday(newdate) = 1 or weekday (NewDate) = 7 then do: NewDate = NewDate + 1. next. end. /* if weekday(newdate) = 1 or weekday (NewDate) = 7 */ /* Is the current date a holiday? We don't actually pull back */ /* the record, just if we can find it. */ if can-find(holiday where Holiday.MDY = NewDate) then do: NewDate = NewDate + 1. next. end. /* if can-find(holiday where Holiday.MDY = NewDate) */ /* We got through weekends and holidays, so add in a business day */ BusinessDays = BusinessDays + 1. NewDate = NewDate + 1. /* Determine if we are ready to bail? */ if BusinessDays = NumberOfDays then leave. end. /* repeat */ Testing Over Our Data Set Now it is time to test the routine. The first screen shot is a quick test program that will be used to generate the results.
Running the program we get
We see that the routine recognizes that the first and the second are holidays from the database table. Then it uses Progresses built in function to recognize the days after are weekends. It then counts the number of days and calculates a new date for delivery or scheduling. Conclusion Having a business days scheduler is one of the most basic date routines available in applications that hope to have a real user base. So many applications I have seen that do not have this simple algorithm! What should be interesting to those who manage is not just the creation of the program that originally started all this, but the creation of a program to add holidays, a program to see what data we need to contend with, and the creation of a test program. So to make this one routine, four programs all together were written. User-wise, the database table Holidays will need to be kept up. So there is a combination of user and programmer involvement in the creation of this routine and it's usefulness. Thanks for reading this and I hope you keep an eye out on www.amduus.com for new articles! Addendum As a viewer (Tex Texin a very good resource) reviewed this paper, he mentioned some international
companies have different weekends that the US. I would suppose this is Friday for muslim countries, Saturday for Jewish countries... and some may have none. (I'm not going into detail on that one.) So this is good for the reader to know. I would also point out, that weekends can be overlooked for truck and train transport. So, the algorithm should be improved based on that it can become very complicated based on what the customer is doing and where. About the author: Scott Augé is the founder and president of Amduus Information Works, Inc. He has been programming in the Progress environment since 1994. His works have included e-business initiatives and focuses on web applications on UNIX platforms. His contact information is at linkedin.com at https://www.linkedin.com/in/scottauge/en.