Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given two timestamps, how do I return the delta in terms of "business minutes"

Looking for a clean function for this, ideally in Pandas/Numpy. I'm currently building something messy out of CustomBusinessHour() and TimeDelta() functions from Pandas, but I think that there must be a better way. If Pandas had a CustomBusinessMinute() feature, this would be as easy as len(pd.date_range(timestamp1,timestamp,freq=CustomBusinessMinute())).

By "Business Minute," I mean a minute that meets certain criteria. For example, in my case this means 1) does not fall on weekend, 2) falls between 9am and 5pm, and 3) does not fall on Federal Holiday.

Thanks

like image 530
Bryan Gnipp Avatar asked Jan 27 '26 13:01

Bryan Gnipp


1 Answers

Consider the following:

You will only have to closely examine the aspects of the start and end dates. IE Carefully calculate the business minutes for those two days.

For every other date in between, you only need to know one or things (1) If it's a weekday and if it is: (2) is it a Federal Holiday

For every qualifying date in the date range, you know exactly how many "Business minutes" are in each day: 480 minutes.

Pandas offers a way to get business days based on US Federal Holidays. That takes care of the hardest part. The rest should be easy to implement.

There's probably a more elegant way, but here's something to start with. Most of the code is for dealing with the start and end dates. Getting all the minutes in between is about 4 lines.

from dateutil.relativedelta import relativedelta
import pandas as pd
from pandas.tseries.offsets import CDay
from pandas.tseries.holiday import USFederalHolidayCalendar

business_day = CDay(calendar=USFederalHolidayCalendar())

def is_weekday(dt):
    return dt.weekday() < 5

def is_holiday(dt):
    return not len(pd.date_range(dt, dt, freq=business_day))

def weekend_or_holiday(dt):
    '''helper function'''
    if not is_weekday(dt):
        return True
    if is_holiday(dt):
        return True
    return False

def start_day_minutes(dt, end_of_day=None):
    '''returns number of business minutes left in the day given a start datetime'''
    if not end_of_day:
        end_of_day = dt.replace(hour=17, minute=0)
    if dt > end_of_day or weekend_or_holiday(dt):
        return 0
    num_of_minutes = (end_of_day - dt).seconds / 60
    return num_of_minutes

def end_day_minutes(dt):
    '''like start_day_minutes, but for the ending day.'''
    start_of_day = dt.replace(hour=9, minute=0)
    if dt < start_of_day or weekend_or_holiday(dt):
        return 0
    num_of_minutes = (dt - start_of_day).seconds / 60
    return num_of_minutes

def business_minutes(t1, t2):
    '''returns num of busniess minutes between t1 and t2'''
    start = t1.replace(hour=0, minute=0) + relativedelta(days=1)
    end = t2.replace(hour=0, minute=0) + relativedelta(days=-1)
    days_between = pd.date_range(start, end, freq=business_day)
    minutes_between = (len(days_between) * 480)
    if (t1.year, t1.day) == (t2.year, t2.day):
        start_end_minutes = start_day_minutes(t1, t2)
    else:
        start_end_minutes = start_day_minutes(t1) + end_day_minutes(t2)

    minutes = minutes_between + start_end_minutes
    return minutes

Example:

start=datetime(2016,1,1)
end=datetime(2017,1,1)
print(business_minutes(start,end))
#120480
like image 189
sytech Avatar answered Jan 30 '26 02:01

sytech



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!