accept multiple date arguments in a function - python

Bonjour!
I want to write program to calculate 28 day cycle for when rent is due, but if atthe end of year i calculate the no of days i paid the rent late , i wish to total those days and calculate them based on paid dates in my code.
the question is how to insert multiplepaidDates for the whole year i.e. 12 for calculating the days i paid late? how to insert multiple arguments for it in the function datespan?
link: How to iterate over a timespan after days, hours, weeks and months in Python?
help appreciated.
Merci.
from datetime import date, datetime, timedelta
def datespan(startDate, endDate, paidDate,delta=timedelta(days=1)):
currentDate = startDate
while currentDate < endDate:
yield currentDate
currentDate += delta
dl = paidDate - currentDate
print (dl)
for day in datespan(date(2015,3,12 ), date(2015,12,31), date((2015,4,15), date(2015,5,14)),
delta=timedelta(days=28)):
print (day)

It looks like you want to use varargs. This is possible with python. You would change your method signature to be:
def datespan(startDate, endDate, delta, *paidDates)
This will allow you to pass in as many paidDates arguments as you want. Note that you will not be able to use the default arg for delta anymore.
datespan(date(2015,3,12), date(2015,12,31), timedelta(days=28), date(2015,4,15), date(2015,5,14))
should work fine in this case. You will also need to modify your function to handle a sequence of paidDates as opposed to just one coming in as an argument.

Related

Filter Django query by week of month?

In a Django query, how would you filter by a timestamp's week within a month?
There's a built-in week accessor, but that refers to week-of-the-year, e.g. 1-52. As far as I can tell, there's no other built-in option.
The only way I see to do this is to calculate the start and end date range for the week, and then filter on that using the conventional means.
So I'm using a function like:
def week_of_month_date(year, month, week):
"""
Returns the date of the first day in the week of the given date's month,
where Monday is the first day of the week.
e.g. week_of_month_date(year=2022, month=8, week=2) -> date(2022, 8, 7)
"""
assert 1 <= week <= 5
assert 1 <= month <= 12
for i in range(1, 32):
dt = date(year, month, i)
_week = week_of_month(dt)
if _week == week:
return dt
and then to calculate for, say, the 3rd week of July, 2022, I'd do:
start_date = week_of_month_date(2022, 7, 3)
end_date = week_of_month_date(2022, 7, 3) + timedelta(days=7)
qs = MyModel.objects.filter(created__gte=start_date, created__lte=end_date)
Is there an easier or more efficient way to do this with the Django ORM or SQL?
The easiest way to do this using datetime objects is to quite simply subtract the current date weekly year value, with the yearly week value for the 1st day (or 1st week) of the month.
You can use the .isocalendar() function to achieve this:
dt.isocalendar[1] - dt.replace(day=1).isocalendar()[1] + 1
Basically if the week is 46 and that means the first week is week 44 then the resulting output should be 2.
UPDATE
I misunderstood the question, the answer is clear below. However, you may want to consider revising your function based on my above comments.
Come to think of it, if you have a datetime object, you can get the isocalendar week and filter using that like so:
MyModel.objects.filter(created__week=dt.isocalendar()[1])
dt.isocalendar() returns essentially a tuple of 3 integers, [0], is the year, [1], is the iso week (1-52 or 53) and [2], the day of the week (1-7).
As per the docs here:
https://docs.djangoproject.com/en/4.1/ref/models/querysets/#week
There is a built-in filter for isoweek out of the box :)
However, filtering by "week of month" is not possible within the realms of "out of the box".
You might consider writing your own query expression object which accepts an isocalendar object and converts that? But I think you would be better off converting a datetime object and use the isoweek filter.
There's a neat little blog post here to get you started if you really want to do that:
https://dev.to/idrisrampurawala/writing-custom-django-database-functions-4dmb

How can I find the elapsed business hours between two dates using pandas' CustomBusinessHour objects?

If I want to find the number of hours between two datetime objects, I can do something like this:
from datetime import datetime
today = datetime.today()
day_after_tomorrow = datetime(2022, 9, 24)
diff = (day_after_tomorrow - today).total_seconds() / 3600
print(diff)
which returns: 37.58784580333333 hours.
But this is the number of real hours between two dates. I want to know the number of specific business hours between two dates.
I can define two CustomBusinessHour objects with pandas to specify those business hours (which are 8AM to 4:30PM M-F, and 8AM to 12PM on Saturday, excluding US Federal holidays):
from pandas.tseries.offsets import CustomBusinessHour
from pandas.tseries.holiday import USFederalHolidayCalendar
business_hours_mtf = CustomBusinessHour(calendar=USFederalHolidayCalendar(), start='08:00', end='16:30')
business_hours_sat = CustomBusinessHour(calendar=USFederalHolidayCalendar(), start='08:00', end='12:00')
My understanding is that CustomBusinessHour is a type of pandas DateOffset object, so it should behave just like a relativedelta object. So I should be able to use it in the datetime arithmetic somehow, to get the number I want.
And that's as far as I was able to get.
What I think I'm struggling to understand is how relativedeltas work, and how to actually use them in datetime arithmetic.
Is this the right approach? If so, how can I use these CustomBusinessHour objects to get an accurate amount of elapsed business hours between the two dates?
I figured out a solution. It feels ugly and hacky, but it seems to work. Hopefully someone else has a simpler or more elegant solution.
Edit: I cleaned up the documentation a little bit to make it easier to read. Also added a missing kwarg in business_hours_sat. Figuring this out was a headache, so if anyone else has to deal with this problem, hopefully this solution helps.
from datetime import datetime, timedelta
from pandas.tseries.offsets import CustomBusinessHour
from pandas.tseries.holiday import USFederalHolidayCalendar
business_hours_mtf = CustomBusinessHour(calendar=USFederalHolidayCalendar(), start='08:00', end='16:30')
business_hours_sat = CustomBusinessHour(calendar=USFederalHolidayCalendar(), weekmask='Sat', start='08:00', end='12:00')
def get_business_hours_range(earlier_date: datetime, later_date: datetime) -> float:
"""Return the number of business hours between `earlier_date` and `later_date` as a float with two decimal places.
Algorithm:
1. Increment `earlier_date` by 1 "business hour" until it's further in the future than `later_date`.
2. Also increment an `elapsed_business_hours` variable by 1.
3. Once `earlier_date` is larger (further in the future) than `later_date`...
a. Roll back `earlier_date` by one business hour.
b. Get the close of business hour for `earlier_date` ([3a]).
c. Get the number of minutes between [3b] and [3a] (`minutes_remaining`).
d. Create a timedelta with `elapsed_business_hours` and `minutes_remaining`
e. Represent this timedelta as a float with two decimal places.
f. Return this float.
"""
# Count how many "business hours" have elapsed between the `earlier_date` and `later_date`.
elapsed_business_hours = 0.0
current_day_of_week = 0
while earlier_date < later_date:
day_of_week = earlier_date.isoweekday()
# 6 = Saturday
if day_of_week == 6:
# Increment `earlier_date` by one "business hour", as specified by the `business_hours_sat` CBH object.
earlier_date += business_hours_sat
# Increment the counter of how many "business hours" have elapsed between these two dates.
elapsed_business_hours += 1
# Save the current day of the week in `earlier_date`, in case this is the last iteration of this while loop.
current_day_of_week = day_of_week
# 1 = Monday, 2 = Tuesday, ...
elif day_of_week in (1, 2, 3, 4, 5):
# Increment `earlier_date` by one "business hour", as specified by the `business_hours_mtf` CBH object.
earlier_date += business_hours_mtf
# Increment the counter of how many "business hours" have elapsed between these two dates.
elapsed_business_hours += 1
# Save the current day of the week in `earlier_date`, in case this is the last iteration of this while loop.
current_day_of_week = day_of_week
# Once we've incremented `earlier_date` to a date further in the future than `later_date`, we know that we've counted
# all the full (60min) "business hours" between `earlier_date` and `later_date`. (We can only increment by one hour when using
# CBH, so when we make this final increment, we may be skipping over a few minutes in that last day.)
#
# So now we roll `earlier_date` back by 1 business hour, to the last full business hour before `later_date`. Then we get the
# close of business hour for that day, and subtract `earlier_date` from it. This will give us whatever minutes may be remaining
# in that day, that weren't accounted for when tallying the number of "business hours".
#
# But before we do these things, we need to check what day of the week the last business hour is, so we know which closing time
# to use.
if current_day_of_week == 6:
ed_rolled_back = earlier_date - business_hours_sat
ed_closing_time = datetime.combine(ed_rolled_back, business_hours_sat.end[0])
elif current_day_of_week in (1, 2, 3, 4, 5):
ed_rolled_back = earlier_date - business_hours_mtf
ed_closing_time = datetime.combine(ed_rolled_back, business_hours_mtf.end[0])
minutes_remaining = (ed_closing_time - ed_rolled_back).total_seconds() / 60
if 0 < minutes_remaining < 60:
delta = timedelta(hours=elapsed_business_hours, minutes=minutes_remaining)
else:
delta = timedelta(hours=elapsed_business_hours)
delta_hours = round(float(delta.total_seconds() / 3600), 2)
return delta_hours

How can I get the year, month, and day from a Deephaven DateTime in Python?

I have a Deephaven DateTime in the New York (US-East) timezone and I'd like to get the year, month, and day (of the month) numbers from it as integers in Python.
Deephaven's time module has these utilities. You may have used it to create a Deephaven DateTime in the first place.
from deephaven import time as dhtu
timestamp = dhtu.to_datetime("2022-04-01T12:00:00 NY")
The following three methods will give you what you're looking for:
year - Gets the year
month_of_year - Gets the month
day_of_month - Gets the day of the month
All three methods will give you what you want based on the DateTime itself and your preferred time zone.
tz_ny = dhtu.TimeZone.NY
year = dhtu.year(timestamp, tz_ny)
month = dhtu.month_of_year(timestamp, tz_ny)
day = dhtu.day_of_month(timestamp, tz_ny)

How to retrieve previous NYSE trading day in Pandas?

I'm trying to get the previous trading day in relation to a given trading day. To start I am simply trying to make a function which returns the previous business day when given a date, like so:
import datetime
def get_previous_trading_day(day):
day = pd.to_datetime(day)
previous_trading_day = day - datetime.timedelta(days=1)
return previous_trading_day
But when I call my function and print the current vs previous date, the previous date is not the previous day:
2021-05-01 curr
1885-02-22 00:00:00 prev
How do I make this work?
If you change the calculation to use pd.tseries.offsets.BDay instead, you will get the previous business day (instead of the day before). I realise this will not work for bank holidays where no trading occurs. Your function works well when I try it for returning the previous day's date.
def get_previous_trading_day(day):
day = pd.to_datetime(day)
previous_trading_day = day - pd.tseries.offsets.BDay(1)
return previous_trading_day
Calling the function for tomorrow's date will return Friday's date:
get_previous_trading_day("2022-05-16")
#Out: Timestamp('2022-05-13 00:00:00')
For your returned date, you may have put a date format that was not read correctly by the pd.to_datetime. If you require a specific format, add the kwarg format= to this.

Python - Calendar / Date Library for Arithmetic Date Operations

This is for Python:
I need a library that is able to do arithmetic operations on dates while taking into account the duration of a month and or year.
For example, say I add a value of "1 day" to 3/31/2020, the result of should return:
1 + 3/31/2020 = 4/1/2020.
I also would need to be able to convert this to datetime format, and extract day, year and month.
Does a library like this exist?
import datetime
tday = datetime.date.today() # create today
print("Today:", tday)
""" create one week time duration """
oneWeek = datetime.timedelta(days=7)
""" create 1 day and 1440 minutes of time duraiton """
eightDays = datetime.timedelta(days=7, minutes=1440)
print("A week later than today:", tday + oneWeek) # print today +7 days
And the output to this code snippet is:
Today: 2020-03-25
A week later than today: 2020-04-01
>>>
As you see, it takes month overflows into account and turns March to April. datetime module has lots of things, I don't know all its attributes well and haven't used for a long time. However, I believe you can find nice documentation or tutorials on the web.
You definitely can create any specific date(there should be some constraints though) instead of today by supplying day, month and year info. I just don't remember how to do it.

Categories