Python UK business days with pandas

Here’s how to calculate UK business days (well at least for England & Wales) using pandas ‘s holiday calendar.

First you’ll need this calendar for UK holidays:

from pandas.tseries.holiday import (
    AbstractHolidayCalendar, DateOffset, EasterMonday,
    GoodFriday, Holiday, MO,
    next_monday, next_monday_or_tuesday)
class EnglandAndWalesHolidayCalendar(AbstractHolidayCalendar):
    rules = [
        Holiday('New Years Day', month=1, day=1, observance=next_monday),
        GoodFriday,
        EasterMonday,
        Holiday('Early May bank holiday',
                month=5, day=1, offset=DateOffset(weekday=MO(1))),
        Holiday('Spring bank holiday',
                month=5, day=31, offset=DateOffset(weekday=MO(-1))),
        Holiday('Summer bank holiday',
                month=8, day=31, offset=DateOffset(weekday=MO(-1))),
        Holiday('Christmas Day', month=12, day=25, observance=next_monday),
        Holiday('Boxing Day',
                month=12, day=26, observance=next_monday_or_tuesday)
    ]

It was tested with the dates from gov.uk so should be fine to use, but please let me know if you find anything wrong with it.

Now you can do stuff like:

from datetime import date
from pandas.tseries.offsets import CDay
business = CDay(calendar=EnglandAndWalesHolidayCalendar())
>>> date.today()
datetime.date(2016, 3, 2)
>>> five_business_days_later = date.today() + 5 * business
>>> five_business_days_later
Timestamp('2016-03-09 00:00:00')
>>> five_business_days_later.date()
datetime.date(2016, 3, 9)
>>> date.today() - business
>>> date(2016, 12, 25) + business
Timestamp('2016-12-28 00:00:00')

You can also just retrieve the UK holidays for a specific year as a list of datetime objects using e.g.:

>>> holidays = EnglandAndWalesHolidayCalendar().holidays(
    start=date(2016, 1, 1),
    end=date(2016, 12, 31))
>>> holidays.tolist()
[Timestamp('2016-01-01 00:00:00'), Timestamp('2016-03-25 00:00:00'), Timestamp('2016-03-28 00:00:00'), Timestamp('2016-05-02 00:00:00'), Timestamp('2016-05-30 00:00:00'), Timestamp('2016-08-29 00:00:00'), Timestamp('2016-12-26 00:00:00'), Timestamp('2016-12-27 00:00:00')
>>> holidays.to_pydatetime()
array([datetime.datetime(2016, 1, 1, 0, 0),
       datetime.datetime(2016, 3, 25, 0, 0),
       datetime.datetime(2016, 3, 28, 0, 0),
       datetime.datetime(2016, 5, 2, 0, 0),
       datetime.datetime(2016, 5, 30, 0, 0),
       datetime.datetime(2016, 8, 29, 0, 0),
       datetime.datetime(2016, 12, 26, 0, 0),
       datetime.datetime(2016, 12, 27, 0, 0)], dtype=object)
>>> h.to_native_types()
['2016-01-01', '2016-03-25', '2016-03-28', '2016-05-02', '2016-05-30', '2016-08-29', '2016-12-26', '2016-12-27']

Pandas has all sorts of funny stuff you can do with series and time series in particular. Also check pandas’s docs about Custom Business Days especially the warning about possible timezone issues.

If you don’t need the power of pandas or you just don’t like it (maybe because it pulls in a bazillion dependencies and includes a gazillion modules), workalendar looks pretty good.

Comments

Run py.test test case inside any python class

I wanted a way to run the current test I was editing when I know the name of the method, but I’m too lazy to scroll up and see which class it’s defined in and then have to type that out as well. So I found a way for py.test to run any test in any class in a file by just giving it the name of that test or part of its name.

Instead of typing:

$ py.test path/to/my/test/test_file.py::MySuperLongClassNameTest::test_my_thing

I can just type:

$ py.test path/to/my/test/test_file.py -k test_my_thing

Or even:

$ py.test path/to/my/test/test_file.py -k test_my

Or even:

$ py.test path/to/my/test/test_file.py -k "thing or stuff"

Woo py.test!

FWIW, nosetests can also do this with the -m option which supports regexp. But AFAIK it does not support the human-friendly " or " interface like in the last example above.

Comments

AST literal_eval


Safely evaluate an expression node or a Unicode or Latin-1 encoded string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, numbers, tuples, lists, dicts, booleans, and None.

This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself. It is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.

—From python’s ast library

I used to discredit everything that meant converting a string to an arbitrary data structure. This is a nice third option which seems like it would be useful in the majority of cases.

Comments

Change the system timezone for the python interpreter

Wrapping my head around timezones is hard. And testing the implications of working with different timezones is especially difficult since I now live in GMT (which is mostly the same as UTC).

I found a way to change the timezone, that is used by most of python’s stdlib, by changing the TZ environment variable:

$ python -c 'import time; print(time.tzname)'
('GMT', 'BST')
$ TZ='Europe/Stockholm' python -c 'import time; print(time.tzname)'
('CET', 'CEST')
Comments

The XY Problem

So there’s a website devoted to The XY Problem:

The XY problem is asking about your attempted solution rather than
your actual problem. This leads to enormous amounts of wasted time and
energy, both on the part of people asking for help, and on the part of
those providing help.

Comments
Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.