Here is a mathematical way to do it.
Step by step
Let's investigate about working seconds for a TimeStamp some_time
.
We are going to need the TimeStamp of some_time
at midnight. Let's call it d_day
:
d_day = some_time.replace(hour=0, minute=0, second=0, microsecond=0)
Today
First, let's define our working days. It start at 9 AM
and lasts until 5 PM
. In seconds, it gives:
start_time = 9*3600
max_work_time = (17-9) * 3600
Now, let's get the timestamp for now and the timestamp for today at midnight, in seconds.
now = datetime.now()
today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
seconds_today = (now - today).seconds
To get today's seconds of work, we must substract start_time
then keep at max max_work_time
:
worked_seconds_today = min(seconds_today - start_time, max_work_time)
But of course, we want to keep it only if today
and d-day
are different days, else we want to calculate the worked_seconds since some_time
:
secs_today = min(seconds_today - start_time, max_work_time) \
if today > d_day \
else min(seconds_today - start_time, max_work_time) - min((some_time - today).seconds - start_time, max_work_time)
Days in between d-day and today
We want full days of work only here. So let's simply do:
inbetween_days = max((datetime.today() - d_day).days - 1, 0)
Now we can simply calculate the worked seconds of full days by doing so:
secs_inbetween_days = inbetween_days * max_work_time
D-day
Finally we also want the worked seconds of d_day since some_time
. We can apply the same logic than for today. If today and some_time
are the same day, we simply put zero, as we already calculated today.
def worked_secs(x, since):
return min((x - since).seconds - start_time, max_work_time)
secs_day_d = max_work_time - worked_secs(some_time, d_day) if today != d_day else 0
Total
The total is the sum of the three previous components:
total = secs_day_d + secs_inbetween_days + secs_today
Final function
def busy_seconds(some_time):
# Outside the function is OK also
start_time = 9*3600
max_work_time = (17-9)*3600
# We must calculate all times with the same timezone
tz = some_time.tz
now = datetime.now(tz=tz) # now
today = now.replace(hour=0, minute=0, second=0, microsecond=0) # today at midnight
d_day = some_time.replace(hour=0, minute=0, second=0, microsecond=0) # d-day at midnight
def worked_secs(x, since): # a function is more convenient
return min((x - since).seconds - start_time, max_work_time)
n_complete_days = max((today - d_day).days - 1, 0)
secs_day_d = max_work_time - worked_secs(some_time, d_day) if today != d_day else 0
secs_inbetween_days = max_work_time * n_complete_days
secs_today = worked_secs(now, today) \
if d_day < today \
else worked_secs(now, today) - worked_secs(some_time, today)
return secs_day_d + secs_inbetween_days + secs_today
Finally
We can apply this function to the column:
times.timestamp.apply(busy_seconds)
# > (Example)
# 0 67420800
# 1 57340800
# 2 28800
# Name: timestamp, dtype: int64