Skip to content

Commit db7cd2e

Browse files
Tobias BrandtTobias Brandt
Tobias Brandt
authored and
Tobias Brandt
committed
TST: Copied existing tests for BusinessDay for CustomBusinessDay to test that
the CustomBusinessDay class can be used as a drop-in replacement of the BusinessDay class. TST: Added tests for the holidays and weekmask functionality of the CustomBusinessDay class. TST: Added checks to skip tests involving CustomBusinessDay when this class isn't available due to numpy requirements not being met. TST: Added tests for holidays and weekmask functionality in cdate_range.
1 parent 11ce405 commit db7cd2e

File tree

5 files changed

+478
-13
lines changed

5 files changed

+478
-13
lines changed

pandas/core/datetools.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
try:
1212
cday = CDay()
1313
customBusinessDay = CustomBusinessDay()
14-
except ImportError:
15-
# Don't create CustomBusinessDay instances when not available
16-
pass
14+
except NotImplementedError:
15+
cday = None
16+
customBusinessDay = None
1717
monthEnd = MonthEnd()
1818
yearEnd = YearEnd()
1919
yearBegin = YearBegin()

pandas/tseries/frequencies.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,14 @@ def _get_freq_str(base, mult=1):
121121
BQuarterEnd, YearBegin, YearEnd,
122122
BYearBegin, BYearEnd,
123123
)
124+
try:
125+
cday = CDay()
126+
except NotImplementedError:
127+
cday = None
124128

125129
_offset_map = {
126130
'D': Day(),
127-
'C': CDay(),
131+
'C': cday,
128132
'B': BDay(),
129133
'H': Hour(),
130134
'T': Minute(),

pandas/tseries/offsets.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,9 @@ def __init__(self, n=1, **kwds):
385385
# Check we have the required numpy version
386386
from distutils.version import LooseVersion
387387
if LooseVersion(np.__version__) < '1.7.0':
388-
raise ImportError("CustomBusinessDay requires numpy >= 1.7.0. "
389-
"Current version: "+np.__version__)
388+
raise NotImplementedError("CustomBusinessDay requires numpy >= "
389+
"1.7.0. Current version: " +
390+
np.__version__)
390391

391392
self.n = int(n)
392393
self.kwds = kwds

pandas/tseries/tests/test_daterange.py

+242-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime
22
import pickle
33
import unittest
4+
import nose
45

56
import numpy as np
67

@@ -9,7 +10,7 @@
910

1011
from pandas import Timestamp
1112
from pandas.tseries.offsets import generate_range
12-
from pandas.tseries.index import bdate_range, date_range
13+
from pandas.tseries.index import cdate_range, bdate_range, date_range
1314
import pandas.tseries.tools as tools
1415

1516
import pandas.core.datetools as datetools
@@ -23,6 +24,11 @@ def _skip_if_no_pytz():
2324
raise nose.SkipTest
2425

2526

27+
def _skip_if_no_cday():
28+
if datetools.cday is None:
29+
raise nose.SkipTest("CustomBusinessDay not available.")
30+
31+
2632
def eq_gen_range(kwargs, expected):
2733
rng = generate_range(**kwargs)
2834
assert(np.array_equal(list(rng), expected))
@@ -37,6 +43,12 @@ def test_generate(self):
3743
rng2 = list(generate_range(START, END, time_rule='B'))
3844
self.assert_(np.array_equal(rng1, rng2))
3945

46+
def test_generate_cday(self):
47+
_skip_if_no_cday()
48+
rng1 = list(generate_range(START, END, offset=datetools.cday))
49+
rng2 = list(generate_range(START, END, time_rule='C'))
50+
self.assert_(np.array_equal(rng1, rng2))
51+
4052
def test_1(self):
4153
eq_gen_range(dict(start=datetime(2009, 3, 25), periods=2),
4254
[datetime(2009, 3, 25), datetime(2009, 3, 26)])
@@ -364,7 +376,235 @@ def test_month_range_union_tz(self):
364376
early_dr.union(late_dr)
365377

366378

379+
class TestCustomDateRange(unittest.TestCase):
380+
381+
def setUp(self):
382+
_skip_if_no_cday()
383+
self.rng = cdate_range(START, END)
384+
385+
def test_constructor(self):
386+
rng = cdate_range(START, END, freq=datetools.cday)
387+
rng = cdate_range(START, periods=20, freq=datetools.cday)
388+
rng = cdate_range(end=START, periods=20, freq=datetools.cday)
389+
self.assertRaises(ValueError, date_range, '2011-1-1', '2012-1-1', 'C')
390+
self.assertRaises(ValueError, cdate_range, '2011-1-1', '2012-1-1', 'C')
391+
392+
def test_cached_range(self):
393+
rng = DatetimeIndex._cached_range(START, END,
394+
offset=datetools.cday)
395+
rng = DatetimeIndex._cached_range(START, periods=20,
396+
offset=datetools.cday)
397+
rng = DatetimeIndex._cached_range(end=START, periods=20,
398+
offset=datetools.cday)
399+
400+
self.assertRaises(Exception, DatetimeIndex._cached_range, START, END)
401+
402+
self.assertRaises(Exception, DatetimeIndex._cached_range, START,
403+
freq=datetools.cday)
404+
405+
self.assertRaises(Exception, DatetimeIndex._cached_range, end=END,
406+
freq=datetools.cday)
407+
408+
self.assertRaises(Exception, DatetimeIndex._cached_range, periods=20,
409+
freq=datetools.cday)
410+
411+
def test_comparison(self):
412+
d = self.rng[10]
413+
414+
comp = self.rng > d
415+
self.assert_(comp[11])
416+
self.assert_(not comp[9])
417+
418+
def test_copy(self):
419+
cp = self.rng.copy()
420+
repr(cp)
421+
self.assert_(cp.equals(self.rng))
422+
423+
def test_repr(self):
424+
# only really care that it works
425+
repr(self.rng)
426+
427+
def test_getitem(self):
428+
smaller = self.rng[:5]
429+
self.assert_(np.array_equal(smaller, self.rng.view(np.ndarray)[:5]))
430+
self.assertEquals(smaller.offset, self.rng.offset)
431+
432+
sliced = self.rng[::5]
433+
self.assertEquals(sliced.offset, datetools.cday * 5)
434+
435+
fancy_indexed = self.rng[[4, 3, 2, 1, 0]]
436+
self.assertEquals(len(fancy_indexed), 5)
437+
self.assert_(isinstance(fancy_indexed, DatetimeIndex))
438+
self.assert_(fancy_indexed.freq is None)
439+
440+
# 32-bit vs. 64-bit platforms
441+
self.assertEquals(self.rng[4], self.rng[np.int_(4)])
442+
443+
def test_getitem_matplotlib_hackaround(self):
444+
values = self.rng[:, None]
445+
expected = self.rng.values[:, None]
446+
self.assert_(np.array_equal(values, expected))
447+
448+
def test_shift(self):
449+
shifted = self.rng.shift(5)
450+
self.assertEquals(shifted[0], self.rng[5])
451+
self.assertEquals(shifted.offset, self.rng.offset)
452+
453+
shifted = self.rng.shift(-5)
454+
self.assertEquals(shifted[5], self.rng[0])
455+
self.assertEquals(shifted.offset, self.rng.offset)
456+
457+
shifted = self.rng.shift(0)
458+
self.assertEquals(shifted[0], self.rng[0])
459+
self.assertEquals(shifted.offset, self.rng.offset)
460+
461+
rng = date_range(START, END, freq=datetools.bmonthEnd)
462+
shifted = rng.shift(1, freq=datetools.cday)
463+
self.assertEquals(shifted[0], rng[0] + datetools.cday)
464+
465+
def test_pickle_unpickle(self):
466+
pickled = pickle.dumps(self.rng)
467+
unpickled = pickle.loads(pickled)
468+
469+
self.assert_(unpickled.offset is not None)
470+
471+
def test_union(self):
472+
# overlapping
473+
left = self.rng[:10]
474+
right = self.rng[5:10]
475+
476+
the_union = left.union(right)
477+
self.assert_(isinstance(the_union, DatetimeIndex))
478+
479+
# non-overlapping, gap in middle
480+
left = self.rng[:5]
481+
right = self.rng[10:]
482+
483+
the_union = left.union(right)
484+
self.assert_(isinstance(the_union, Index))
485+
486+
# non-overlapping, no gap
487+
left = self.rng[:5]
488+
right = self.rng[5:10]
489+
490+
the_union = left.union(right)
491+
self.assert_(isinstance(the_union, DatetimeIndex))
492+
493+
# order does not matter
494+
self.assert_(np.array_equal(right.union(left), the_union))
495+
496+
# overlapping, but different offset
497+
rng = date_range(START, END, freq=datetools.bmonthEnd)
498+
499+
the_union = self.rng.union(rng)
500+
self.assert_(isinstance(the_union, DatetimeIndex))
501+
502+
def test_outer_join(self):
503+
# should just behave as union
504+
505+
# overlapping
506+
left = self.rng[:10]
507+
right = self.rng[5:10]
508+
509+
the_join = left.join(right, how='outer')
510+
self.assert_(isinstance(the_join, DatetimeIndex))
511+
512+
# non-overlapping, gap in middle
513+
left = self.rng[:5]
514+
right = self.rng[10:]
515+
516+
the_join = left.join(right, how='outer')
517+
self.assert_(isinstance(the_join, DatetimeIndex))
518+
self.assert_(the_join.freq is None)
519+
520+
# non-overlapping, no gap
521+
left = self.rng[:5]
522+
right = self.rng[5:10]
523+
524+
the_join = left.join(right, how='outer')
525+
self.assert_(isinstance(the_join, DatetimeIndex))
526+
527+
# overlapping, but different offset
528+
rng = date_range(START, END, freq=datetools.bmonthEnd)
529+
530+
the_join = self.rng.join(rng, how='outer')
531+
self.assert_(isinstance(the_join, DatetimeIndex))
532+
self.assert_(the_join.freq is None)
533+
534+
def test_intersection_bug(self):
535+
# GH #771
536+
a = cdate_range('11/30/2011', '12/31/2011')
537+
b = cdate_range('12/10/2011', '12/20/2011')
538+
result = a.intersection(b)
539+
self.assert_(result.equals(b))
540+
541+
def test_summary(self):
542+
self.rng.summary()
543+
self.rng[2:2].summary()
544+
545+
def test_summary_pytz(self):
546+
_skip_if_no_pytz()
547+
import pytz
548+
cdate_range('1/1/2005', '1/1/2009', tz=pytz.utc).summary()
549+
550+
def test_misc(self):
551+
end = datetime(2009, 5, 13)
552+
dr = cdate_range(end=end, periods=20)
553+
firstDate = end - 19 * datetools.cday
554+
555+
assert len(dr) == 20
556+
assert dr[0] == firstDate
557+
assert dr[-1] == end
558+
559+
def test_date_parse_failure(self):
560+
badly_formed_date = '2007/100/1'
561+
562+
self.assertRaises(ValueError, Timestamp, badly_formed_date)
563+
564+
self.assertRaises(ValueError, cdate_range, start=badly_formed_date,
565+
periods=10)
566+
self.assertRaises(ValueError, cdate_range, end=badly_formed_date,
567+
periods=10)
568+
self.assertRaises(ValueError, cdate_range, badly_formed_date,
569+
badly_formed_date)
570+
571+
def test_equals(self):
572+
self.assertFalse(self.rng.equals(list(self.rng)))
573+
574+
def test_daterange_bug_456(self):
575+
# GH #456
576+
rng1 = cdate_range('12/5/2011', '12/5/2011')
577+
rng2 = cdate_range('12/2/2011', '12/5/2011')
578+
rng2.offset = datetools.CDay()
579+
580+
result = rng1.union(rng2)
581+
self.assert_(isinstance(result, DatetimeIndex))
582+
583+
def test_cdaterange(self):
584+
rng = cdate_range('2013-05-01', periods=3)
585+
xp = DatetimeIndex(['2013-05-01', '2013-05-02', '2013-05-03'])
586+
self.assert_(xp.equals(rng))
587+
588+
def test_cdaterange_weekmask(self):
589+
rng = cdate_range('2013-05-01', periods=3,
590+
weekmask='Sun Mon Tue Wed Thu')
591+
xp = DatetimeIndex(['2013-05-01', '2013-05-02', '2013-05-05'])
592+
self.assert_(xp.equals(rng))
593+
594+
def test_cdaterange_holidays(self):
595+
rng = cdate_range('2013-05-01', periods=3,
596+
holidays=['2013-05-01'])
597+
xp = DatetimeIndex(['2013-05-02', '2013-05-03', '2013-05-06'])
598+
self.assert_(xp.equals(rng))
599+
600+
def test_cdaterange_weekmask_and_holidays(self):
601+
rng = cdate_range('2013-05-01', periods=3,
602+
weekmask='Sun Mon Tue Wed Thu',
603+
holidays=['2013-05-01'])
604+
xp = DatetimeIndex(['2013-05-02', '2013-05-05', '2013-05-06'])
605+
self.assert_(xp.equals(rng))
606+
607+
367608
if __name__ == '__main__':
368-
import nose
369609
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
370610
exit=False)

0 commit comments

Comments
 (0)