Skip to content

fix business day calculation #5220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 15, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions pandas/src/period.c
Original file line number Diff line number Diff line change
Expand Up @@ -1127,8 +1127,16 @@ npy_int64 get_period_ordinal(int year, int month, int day,
{
goto onError;
}
weeks = days / 7;
return (npy_int64)(days - weeks * 2) - BDAY_OFFSET;
// calculate the current week assuming sunday as last day of a week
weeks = (days - BASE_WEEK_TO_DAY_OFFSET) / DAYS_PER_WEEK;
// calculate the current weekday (in range 1 .. 7)
delta = (days - BASE_WEEK_TO_DAY_OFFSET) % DAYS_PER_WEEK + 1;
// return the number of business days in full weeks plus the business days in the last - possible partial - week
return (npy_int64)(weeks * BUSINESS_DAYS_PER_WEEK)
+ (delta <= BUSINESS_DAYS_PER_WEEK
? delta
: BUSINESS_DAYS_PER_WEEK + 1)
- BDAY_OFFSET;
}

if (freq_group == FR_WK)
Expand Down
3 changes: 3 additions & 0 deletions pandas/src/period.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
#define ORD_OFFSET 719163LL // days until 1970-01-01
#define BDAY_OFFSET 513689LL // days until 1970-01-01
#define WEEK_OFFSET 102737LL
#define BASE_WEEK_TO_DAY_OFFSET 1 // difference between day 0 and end of week in days
#define DAYS_PER_WEEK 7
#define BUSINESS_DAYS_PER_WEEK 5
#define HIGHFREQ_ORIG 0 // ORD_OFFSET * 86400LL // days until 1970-01-01

#define FR_ANN 1000 /* Annual */
Expand Down
8 changes: 6 additions & 2 deletions pandas/tseries/tests/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ def test_period_constructor(self):

# Biz day construction, roll forward if non-weekday
i1 = Period('3/10/12', freq='B')
i2 = Period('3/10/12', freq='D')
self.assertEquals(i1, i2.asfreq('B'))
i2 = Period('3/11/12', freq='D')
self.assertEquals(i1, i2.asfreq('B'))
i2 = Period('3/12/12', freq='D')
self.assertEquals(i1, i2.asfreq('B'))

Expand Down Expand Up @@ -292,7 +296,7 @@ def test_start_time(self):
p = Period('2012', freq=f)
self.assertEquals(p.start_time, xp)
self.assertEquals(Period('2012', freq='B').start_time,
datetime(2011, 12, 30))
datetime(2012, 1, 2))
self.assertEquals(Period('2012', freq='W').start_time,
datetime(2011, 12, 26))

Expand Down Expand Up @@ -321,7 +325,7 @@ def _ex(*args):
p = Period('2012', freq='H')
self.assertEquals(p.end_time, xp)

xp = _ex(2012, 1, 2)
xp = _ex(2012, 1, 3)
self.assertEquals(Period('2012', freq='B').end_time, xp)

xp = _ex(2012, 1, 2)
Expand Down
32 changes: 31 additions & 1 deletion pandas/tseries/tests/test_tslib.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from pandas.core.api import Timestamp

from pandas.tslib import period_asfreq
from pandas.tslib import period_asfreq, period_ordinal

from pandas.tseries.frequencies import get_freq

Expand Down Expand Up @@ -254,6 +254,36 @@ def test_intraday_conversion_factors(self):

self.assertEqual(period_asfreq(1, get_freq('U'), get_freq('N'), False), 1000)

def test_period_ordinal_start_values(self):
# information for 1.1.1970
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('Y')))
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('M')))
self.assertEqual(1, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('W')))
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('D')))
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('B')))

def test_period_ordinal_week(self):
self.assertEqual(1, period_ordinal(1970, 1, 4, 0, 0, 0, 0, 0, get_freq('W')))
self.assertEqual(2, period_ordinal(1970, 1, 5, 0, 0, 0, 0, 0, get_freq('W')))

self.assertEqual(2284, period_ordinal(2013, 10, 6, 0, 0, 0, 0, 0, get_freq('W')))
self.assertEqual(2285, period_ordinal(2013, 10, 7, 0, 0, 0, 0, 0, get_freq('W')))

def test_period_ordinal_business_day(self):
# Thursday
self.assertEqual(11415, period_ordinal(2013, 10, 3, 0, 0, 0, 0, 0, get_freq('B')))
# Friday
self.assertEqual(11416, period_ordinal(2013, 10, 4, 0, 0, 0, 0, 0, get_freq('B')))
# Saturday
self.assertEqual(11417, period_ordinal(2013, 10, 5, 0, 0, 0, 0, 0, get_freq('B')))
# Sunday
self.assertEqual(11417, period_ordinal(2013, 10, 6, 0, 0, 0, 0, 0, get_freq('B')))
# Monday
self.assertEqual(11417, period_ordinal(2013, 10, 7, 0, 0, 0, 0, 0, get_freq('B')))
# Tuesday
self.assertEqual(11418, period_ordinal(2013, 10, 8, 0, 0, 0, 0, 0, get_freq('B')))


if __name__ == '__main__':
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
exit=False)