tags:

views:

836

answers:

8

Hi

I need to calculate date (year, month, day) which is (for example) 18 working days back from another date. It would be enough to eliminate just weekends.

Example: I've got a date 2009-08-21 and a number of 18 workdays as a parameter, and correct answer should be 2009-07-27.

thanks for any help

+2  A: 

i suggest taking a look at http://docs.python.org/library/calendar.html with it you can easily figure out what day of the week a certain date is, and then you can calculate back - taking into account weekends

Niko
A: 

I would probably just loop over the days checking if the day is mon-fri.
Not as efficent but easier to get right.

Martin Beckett
+1  A: 

I recommend using scikits timeseries, with the 'business' frequency. You can download this great python package here:

http://pytseries.sourceforge.net/

Then you can write something like

import datetime
import scikits.timeseries as TS
workDay1 = TS.Date(freq='B', datetime=datetime.datetime(2009,8,21))
workDay2 = workDay1 - 7
asDatetime = workDay2.datetime
Greg
A: 

I'm assuming you're using datetime, but this should work (date is a datetime, days is an integer):

def goback(date, days):
    delta = datetime.timedelta( days=days + 2*(days//5) )
    if date.weekday() == 5:
        delta += datetime.timedelta(days=1)
    elif date.weekday() == 6:
        delta += datetime.timedelta(days=2)
    else:
        leftover = date.weekday() - days % 5
        if leftover < 0: 
            delta += datetime.timedelta(days=2)
    return date - delta

Note that the example in your description is wrong I think . . . 18 work days before the 21st is the 28th.

Walt W
+2  A: 

Here's one way of doing it. Note (1) you don't say what you expect if the start date is NOT a workday. (2) Your example is wrong.

C:\junk\so>type workdays.py
import datetime

def add_workdays(adate, nworkdays):
    if nworkdays < 0:
        incr = -1
        nworkdays = - nworkdays
    else:
        incr = 1
    delta_weeks, delta_days = divmod(nworkdays, 5)
    one_day = datetime.timedelta(days=incr)
    if delta_weeks:
        wdate = adate + one_day * 7 * delta_weeks
    else:
        wdate = adate
    while delta_days:
        wdate += one_day
        if wdate.weekday() < 5: # Mon-Fri
            delta_days -= 1
    return wdate

if __name__ == "__main__":
    start = datetime.date(2009, 8, 21)
    for i in range(10, -19, -1):
        end = add_workdays(start, i)
        print "%3d %s" % (i, end.strftime("%a %Y-%m-%d"))

C:\junk\so>\python26\python workdays.py
 10 Fri 2009-09-04
  9 Thu 2009-09-03
  8 Wed 2009-09-02
  7 Tue 2009-09-01
  6 Mon 2009-08-31
  5 Fri 2009-08-28
  4 Thu 2009-08-27
  3 Wed 2009-08-26
  2 Tue 2009-08-25
  1 Mon 2009-08-24
  0 Fri 2009-08-21
 -1 Thu 2009-08-20
 -2 Wed 2009-08-19
 -3 Tue 2009-08-18
 -4 Mon 2009-08-17
 -5 Fri 2009-08-14
 -6 Thu 2009-08-13
 -7 Wed 2009-08-12
 -8 Tue 2009-08-11
 -9 Mon 2009-08-10
-10 Fri 2009-08-07
-11 Thu 2009-08-06
-12 Wed 2009-08-05
-13 Tue 2009-08-04
-14 Mon 2009-08-03
-15 Fri 2009-07-31
-16 Thu 2009-07-30
-17 Wed 2009-07-29
-18 Tue 2009-07-28

C:\junk\so>
John Machin
zing!
vgm64
@vgm64: Please explain/translate "zing"
John Machin
A: 

If you need to count holidays as non-workdays at some point, you'll need to work out Easter/Good Friday, which is best left to a library call:

>>> from dateutil import easter
>>> easter.easter(2009)
datetime.date(2009, 4, 12)

The other major holidays are relatively simple: they either they occur the same date every year, or they fall on a sequential day of the week in a month. You may want to check out period.py (http://www.medsch.wisc.edu/~annis/creations/period.py.html) which offers an is_holiday() method, though it requires configuration.

NYSE stock market holidays provide a reasonable default holiday schedule for the United States.

A: 

Example: I've got a date 2009-08-21 and a number of 18 workdays as a parameter, and correct answer should be 2009-07-27.

Every 5 work days is 7 'real' days. Subtract the correct number of weeks, and then just work back the remaining days, taking into account if you pass through a weekend.

DateTime endDate = DateTime.Parse("2009-08-21");
int workdays = 18;

int weeks = 18 / 5;
int days = 18 % 5;
DateTime startDate = DateTime.Now - TimeSpan.FromDays(7 * weeks);

while (days > 0)
{
    startDate -= TimeSpan.FromDays(1);
    if (startDate.DayOfWeek == DayOfWeek.Sunday)
    {
        // if a Sunday, take 2 days off to go back to Monday
        startDate -= TimeSpan.FromDays(2);
    }
    days--;
}
Kirk Broadhurst
That code appears to have many Python syntax errors in it :-)
John Machin
A: 

My wife Anna has a recipe for this in the 2nd ed. of the Python Cookbook -- you can read it online with this Google Book Search url, it starts at p. 122. Recipe 3.5 is about weekdays vs weekends; the very next recipe, 3.6, adds consideration of holidays, but unfortunately can only partly be read online on Google Book Search (I have seen many pirate copies of our book being advertised for free downloads, but don't have their URLs handy).

These recipes are particularly near and dear to our hearts because they're basically how we reconnected (on the Web) after years in which we had lost track of each other -- Anna was looking for help perfecting them as she needed their functionality at her workplace, I offered some... 20 months later we were married;-).

Alex Martelli