Skip to content

Commit 724a1f3

Browse files
committed
Merge branch 'bdays_fix' of https://github.com/wuan/pandas into wuan-bdays_fix
2 parents 20e7d89 + 688834a commit 724a1f3

File tree

5 files changed

+52
-5
lines changed

5 files changed

+52
-5
lines changed

doc/source/release.rst

+2
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ Bug Fixes
609609
(:issue:`5215`)
610610
- Fixed bug where filtering a grouped DataFrame or Series did not maintain
611611
the original ordering (:issue:`4621`).
612+
- Fixed ``Period`` with a business date freq to always roll-forward if on a
613+
non-business date. (:issue:`5203`)
612614

613615
pandas 0.12.0
614616
-------------

pandas/src/period.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -1127,8 +1127,16 @@ npy_int64 get_period_ordinal(int year, int month, int day,
11271127
{
11281128
goto onError;
11291129
}
1130-
weeks = days / 7;
1131-
return (npy_int64)(days - weeks * 2) - BDAY_OFFSET;
1130+
// calculate the current week assuming sunday as last day of a week
1131+
weeks = (days - BASE_WEEK_TO_DAY_OFFSET) / DAYS_PER_WEEK;
1132+
// calculate the current weekday (in range 1 .. 7)
1133+
delta = (days - BASE_WEEK_TO_DAY_OFFSET) % DAYS_PER_WEEK + 1;
1134+
// return the number of business days in full weeks plus the business days in the last - possible partial - week
1135+
return (npy_int64)(weeks * BUSINESS_DAYS_PER_WEEK)
1136+
+ (delta <= BUSINESS_DAYS_PER_WEEK
1137+
? delta
1138+
: BUSINESS_DAYS_PER_WEEK + 1)
1139+
- BDAY_OFFSET;
11321140
}
11331141

11341142
if (freq_group == FR_WK)

pandas/src/period.h

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#define ORD_OFFSET 719163LL // days until 1970-01-01
3939
#define BDAY_OFFSET 513689LL // days until 1970-01-01
4040
#define WEEK_OFFSET 102737LL
41+
#define BASE_WEEK_TO_DAY_OFFSET 1 // difference between day 0 and end of week in days
42+
#define DAYS_PER_WEEK 7
43+
#define BUSINESS_DAYS_PER_WEEK 5
4144
#define HIGHFREQ_ORIG 0 // ORD_OFFSET * 86400LL // days until 1970-01-01
4245

4346
#define FR_ANN 1000 /* Annual */

pandas/tseries/tests/test_period.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ def test_period_constructor(self):
115115

116116
# Biz day construction, roll forward if non-weekday
117117
i1 = Period('3/10/12', freq='B')
118+
i2 = Period('3/10/12', freq='D')
119+
self.assertEquals(i1, i2.asfreq('B'))
120+
i2 = Period('3/11/12', freq='D')
121+
self.assertEquals(i1, i2.asfreq('B'))
118122
i2 = Period('3/12/12', freq='D')
119123
self.assertEquals(i1, i2.asfreq('B'))
120124

@@ -292,7 +296,7 @@ def test_start_time(self):
292296
p = Period('2012', freq=f)
293297
self.assertEquals(p.start_time, xp)
294298
self.assertEquals(Period('2012', freq='B').start_time,
295-
datetime(2011, 12, 30))
299+
datetime(2012, 1, 2))
296300
self.assertEquals(Period('2012', freq='W').start_time,
297301
datetime(2011, 12, 26))
298302

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

324-
xp = _ex(2012, 1, 2)
328+
xp = _ex(2012, 1, 3)
325329
self.assertEquals(Period('2012', freq='B').end_time, xp)
326330

327331
xp = _ex(2012, 1, 2)

pandas/tseries/tests/test_tslib.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from pandas.core.api import Timestamp
1010

11-
from pandas.tslib import period_asfreq
11+
from pandas.tslib import period_asfreq, period_ordinal
1212

1313
from pandas.tseries.frequencies import get_freq
1414

@@ -254,6 +254,36 @@ def test_intraday_conversion_factors(self):
254254

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

257+
def test_period_ordinal_start_values(self):
258+
# information for 1.1.1970
259+
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('Y')))
260+
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('M')))
261+
self.assertEqual(1, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('W')))
262+
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('D')))
263+
self.assertEqual(0, period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('B')))
264+
265+
def test_period_ordinal_week(self):
266+
self.assertEqual(1, period_ordinal(1970, 1, 4, 0, 0, 0, 0, 0, get_freq('W')))
267+
self.assertEqual(2, period_ordinal(1970, 1, 5, 0, 0, 0, 0, 0, get_freq('W')))
268+
269+
self.assertEqual(2284, period_ordinal(2013, 10, 6, 0, 0, 0, 0, 0, get_freq('W')))
270+
self.assertEqual(2285, period_ordinal(2013, 10, 7, 0, 0, 0, 0, 0, get_freq('W')))
271+
272+
def test_period_ordinal_business_day(self):
273+
# Thursday
274+
self.assertEqual(11415, period_ordinal(2013, 10, 3, 0, 0, 0, 0, 0, get_freq('B')))
275+
# Friday
276+
self.assertEqual(11416, period_ordinal(2013, 10, 4, 0, 0, 0, 0, 0, get_freq('B')))
277+
# Saturday
278+
self.assertEqual(11417, period_ordinal(2013, 10, 5, 0, 0, 0, 0, 0, get_freq('B')))
279+
# Sunday
280+
self.assertEqual(11417, period_ordinal(2013, 10, 6, 0, 0, 0, 0, 0, get_freq('B')))
281+
# Monday
282+
self.assertEqual(11417, period_ordinal(2013, 10, 7, 0, 0, 0, 0, 0, get_freq('B')))
283+
# Tuesday
284+
self.assertEqual(11418, period_ordinal(2013, 10, 8, 0, 0, 0, 0, 0, get_freq('B')))
285+
286+
257287
if __name__ == '__main__':
258288
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
259289
exit=False)

0 commit comments

Comments
 (0)