#!/bin/sh # # This is a shell archive. To extract its contents, # execute this file with /bin/sh to create the file(s): # # README busdays.ec # # This shell archive created: Fri Jul 3 10:58:15 CDT 1998 # echo "Extracting file README" sed -e 's/^X//' <<\SHAR_EOF > README XSubject: Calculate working days using stored procedure XFrom: Jonathan Leffler XDate: Mon, 29 Jun 1998 09:23:07 -0700 (PDT) X XOn Fri, 26 Jun 1998, Nils Myklebust wrote: X> This is a slightly difficult problem with many solutions. [...] X X> On Wed, 24 Jun 1998 22:36:22 GMT, sjsyau@my-dejanews.com wrote: X> >Given: X> > X> >Today's date + Working Days X> >Today's date - Working Days X> > X> >I need to find out the result in date format. The working days means to X> >exclude weekends and holidays. X XOn 15th May 1998, under the subject 'Re: ESQLC and date/datetime routines', XI posted some skeletal (and bug laden) ESQL/C code to add N business days Xto a given date. As far as I recall, I also posted a follow up on 18th May Xwith a fully tested version of the code, but Dejanews denies any knowledge Xof this. I also thought I sent it to the IIUG, but that site seems to deny Xit too. I must be hallucinating. X XAnyway, here is a pair of ESQL/C routines which add N business days to a Xgiven date, where N can be positive or negative. X X typedef long Date; X X /* Declaration should be in a header for use elsewhere! */ X extern Date AddNBusinessDays(Date indate, int ndays); X extern Date AddNBusinessDaysHolidays(Date indate, int ndays); X extern int HolidaysBetween(Date d1, Date d2); X XAs distributed, the routines return the same answer. There are hooks for Xhandling holidays in AddNBusinessDaysHolidays() using the dummy function XHolidaysBetween() which takes to dates and should return the number of Xholidays (as opposed to weekend days) between the two specified dates (the Xrange includes those dates). The dummy version simply returns 0; if you Xwant to record real holidays, rewrite the function to work the way you Xthink it should. X XObviously, this isn't a direct answer to the question -- it is ESQL/C code Xand not SPL code. However, there is nothing in it that cannot be converted Xinto SPL. I'm not certain that it is the tightest possible algorithm; Xthere may be other fancy tricks which compare start and end day-of-week Xvalues and give you a better way of handling the weekends. However, there Xis test code which allows you to see that this produces what I regard as Xthe correct answer. X XRemember that if your business works 6 days a week (like many retail Xbusinesses), then the rules have to be changed. Also, if your business Xtakes Sunday and Monday off (again, this could be a shop), the rules change Xagain. X XYours, XJonathan Leffler (jleffler@informix.com) #include XGuardian of DBD::Informix -- see http://www.perl.com/CPAN SHAR_EOF if [ `wc -c < README` -ne 2568 ] then echo "Lengths do not match -- Bad Copy of README" fi echo "Extracting file busdays.ec" sed -e 's/^X//' <<\SHAR_EOF > busdays.ec X/* X@(#)File: $RCSfile: busdays.ec,v $ X@(#)Version: $Revision: 1.2 $ X@(#)Last changed: $Date: 1998/05/18 17:32:22 $ X@(#)Purpose: Calculate Business Days from Date X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1998 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X X#include /* rdayofweek() */ X Xtypedef long Date; X X/* Declaration should be in a header for use elsewhere! */ Xextern Date AddNBusinessDays(Date indate, int ndays); Xextern Date AddNBusinessDaysHolidays(Date indate, int ndays); Xextern int HolidaysBetween(Date d1, Date d2); X X#ifndef lint Xstatic const char rcs[] = "@(#)$Id: busdays.ec,v 1.2 1998/05/18 17:32:22 jleffler Exp $"; X#endif X XDate AddNBusinessDays(Date indate, int ndays) X{ X Date outdate; X int residue; X int dw; X X if (ndays == 0) X return indate; X dw = rdayofweek(indate); X if (ndays > 0) X { X outdate = indate + (ndays / 5) * 7; X residue = ndays % 5; X if (dw == 0) X { X outdate -= 2; X dw = 5; X } X else if (dw == 6) X { X outdate -= 1; X dw = 5; X } X outdate += residue; X if (dw + residue > 5) X outdate += 2; X } X else X { X ndays = -ndays; X outdate = indate - (ndays / 5) * 7; X residue = ndays % 5; X if (dw == 0) X { X outdate += 1; X dw = 1; X } X else if (dw == 6) X { X outdate += 2; X dw = 1; X } X outdate -= residue; X if (dw - residue < 1) X outdate -= 2; X } X return outdate; X} X Xint HolidaysBetween(Date d1, Date d2) X{ X return 0; X} X XDate AddNBusinessDaysHolidays(Date indate, int ndays) X{ X Date outdate = AddNBusinessDays(indate, ndays); X int nhols; X X if (ndays > 0) X { X while ((nhols = HolidaysBetween(indate, outdate)) > 0) X { X indate = outdate; X outdate = AddNBusinessDays(indate, nhols); X } X } X else if (ndays < 0) X { X while ((nhols = HolidaysBetween(outdate, indate)) > 0) X { X indate = outdate; X outdate = AddNBusinessDays(indate, -nhols); X } X } X return outdate; X} X X#ifdef TEST X X#include X Xint main(void) X{ X Date refdate = 34567; /* Mon 22 Aug 1994 */ X Date curdate; X Date nxtdate; X int offset; X int daynum; X char buffer[60]; X char *pad; X X printf("Start Date "); X for (offset = -8; offset < 9; offset++) X printf(" %-6d", offset); X putchar('\n'); X X for (daynum = 0; daynum < 7; daynum++) X { X curdate = refdate + daynum; X rfmtdate(curdate, "ddd dd mmm yyyy", buffer); X printf("%s", buffer); X pad = ": "; X for (offset = -8; offset < 9; offset++) X { X nxtdate = AddNBusinessDays(curdate, offset); X rfmtdate(nxtdate, "ddd dd", buffer); X printf("%s%s", pad, buffer); X pad = ", "; X } X putchar('\n'); X } X return 0; X} X X#endif /* TEST */ SHAR_EOF if [ `wc -c < busdays.ec` -ne 2580 ] then echo "Lengths do not match -- Bad Copy of busdays.ec" fi echo "Done." exit 0