How to get last wednesday for current month in python? - python-3.x

my code as follows
today = todayte
print('today1 =', today)
offset = (today.weekday() - 2) % 7
print('offset1=', offset)
last_wednesday = today - timedelta(days=offset)
print('last_wednesday1 = ', last_wednesday)
my current output as follows
today1 = 2018-03-05
offset1 = 5
last_wednesday1 = 2018-02-28
in the above case i am getting previous month last wednesday
but i need current month last wednesday.
my expected output is as follows
last_wednesday = 2018-03-28

Here is a way:
from datetime import datetime , timedelta
todayDT = datetime.today()
currentMonth = todayDT.month
nWed = todayDT
while todayDT.month == currentMonth:
todayDT += timedelta(days=1)
if todayDT.weekday()==2: #this is Wednesday
nWed = todayDT
print (nWed)

you can use a combination of datetime and calendar modules:
from datetime import datetime, timedelta
import calendar
today = datetime.now()
# find first day of the month and then, find first wednesday of the month and replace it
# weekday of wednesday == 2
first_day = datetime.today().replace(day=1)
while first_day.weekday() != 2:
first_day += timedelta(days=1)
number_of_days_in_month = calendar.monthrange(today.year, today.month)[1]
last_wend = first_day + timedelta(days=(((number_of_days_in_month - first_day.day) // 7) * 7))
print(last_wend)
or as #Mark Ransom suggested:
from datetime import datetime, timedelta
day_ = (datetime.now().replace(day=1) + timedelta(days=32)).replace(day=1)
while True:
day_ -= timedelta(days=1)
if day_.weekday() == 2:
break
print(day_)

How about this, we first jump to the next month and re-use your existing code:
import datetime as dt
todayte = dt.date(2018, 3, 5)
today = todayte
d = dt.date(today.year, today.month, 28) # the 28th day of a month must exist
d = d + dt.timedelta(days=7) # so we are sure d is in the next month
# then we apply your original logic
offset = (d.weekday() - 2) % 7
last_wednesday = d - dt.timedelta(days=offset)
print(last_wednesday)
Result:
2018-04-04

Related

Restricting last item value in python list to a given date

I am trying to create a list using a given start and end date range in a specific format. I would like to have the elements in the list incremented by 30 days. The last item in the list should not exceed the end date that i have set.
With the logic that I have below, it exceeds the end date that i have set.
from datetime import timedelta, date
start_date = date(2021,1,1)
end_date = date(2021,6,30)
n = 30
next_date = start_date
res = []
while start_date < end_date:
next_date = (start_date + timedelta(n))
next_date_str = next_date.strftime('%Y-%m-%d')
var = start_date.strftime('%Y-%m-%d')+'#'+next_date_str
res.append(var)
start_date = next_date + timedelta(1)
print(res)
Result of above code:
['2022-01-01#2022-01-31', '2022-02-01#2022-03-03', '2022-03-04#2022-04-03', '2022-04-04#2022-05-04', '2022-05-05#2022-06-04', '2022-06-05#2022-07-05']
Expected output:
['2022-01-01#2022-01-31', '2022-02-01#2022-03-03', '2022-03-04#2022-04-03', '2022-04-04#2022-05-04', '2022-05-05#2022-06-04', '2022-06-05#2022-06-30']
Please guide me how to restrict the end date for the list
One way to fix this problem would be to add a check to your while loop to check if the next date is greater than the end date. If it is, you can set the next date to the end date, and then break out of the loop.
Here's how you could update your code to do this:
from datetime import timedelta, date
start_date = date(2021,1,1)
end_date = date(2021,6,30)
n = 30
next_date = start_date
res = []
while start_date < end_date:
next_date = (start_date + timedelta(n))
if next_date > end_date:
next_date = end_date
break
next_date_str = next_date.strftime('%Y-%m-%d')
var = start_date.strftime('%Y-%m-%d')+'#'+next_date_str
res.append(var)
start_date = next_date + timedelta(1)
print(res)

In Django, how do I construct my queryset to filter by time over slices of time?

I'm using Python 3.9 and Django 3.2. I have this price model
class Price(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
price = models.FloatField(null=False)
created = models.DateTimeField(null=False, default=datetime.now)
If I want to get the price per hour over the last 24 hours, I can run a method like this
def _get_prices_per_time_slice(self, last_hours=24):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time],
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
but let's say I want to get the price every last X hours.
def _get_prices_per_time_slice(self, last_hours=24, time_slice_in_hours=4):
so if the current time is midnight (and zero seconds and minutes), I would want to get the prices for midnight, 8 pm, 4 pm, noon, 8 am and 4 am. How do I add a filter to screen for prices every X hours?
The range function in python helps to specify the incremental step value
SYNTAX : range(start, stop, step)
x = range(3, 20, 4)
for n in x:
print(n)
#Gives output
>>> 3
>>> 7
>>> 11
>>> 15
>>> 19
Just rewrite created_range to add the step value time_slice_in_hours
created__range=[start_time, end_time, time_slice_in_hours]
OPTION 1
def _get_prices_per_time_slice(self, last_hours= 24,time_slice_in_hours=4):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time, time_slice_in_hours],
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
However the syntax in django query set api official documentation fails to mention step parameter in __range() function i.e., it might not be supported by created__range.
OPTION 2
In that case you can use the below function where you can calculate time range x_time_slice_list using Python’s DateTimeRange function(official documentation) and evaluate to created__in
from datetimerange import DateTimeRange
from dateutil.relativedelta import relativedelta
def _get_prices_per_time_slice(self, last_hours= 24,time_slice_in_hours=4):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
x_time_slice_list= []
time_range = DateTimeRange(start_time, end_time)
for value in time_range.range(relativedelta(hours=+time_slice_in_hours)):
x_time_slice_list.append(value)
qset = Price.objects.filter(
created__in= x_time_slice_list,
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
You could achieve this by handling the time slice filtering in python.
What I mean is if you take the function you have. i.e.
def _get_prices_per_time_slice(self, last_hours=24):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time],
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
And rewrite it as follows you to return the created data and handle the filtering in the return list comprehension.
def _get_prices_per_time_slice(self, last_hours=24, time_slice_in_hours=4):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time],
created__minute=end_time.minute
).values('price', 'created')
return [r.get('price') for r in qset if r.get('created').hour % time_slice_in_hours == 0]

Track database history

def startlog():
id = enteruser.id
x = time.localtime()
sec = x.tm_sec
min = x.tm_min
hour = x.tm_hour + 1
day = x.tm_mday
date = f"{x.tm_mon}-{x.tm_mday}-{x.tm_year}"
starttime = (day * 86400) + (hour * 3600) + (min * 60) + sec
updatestart = "UPDATE log SET start = ?, date = ? WHERE ID = ?"
c.execute(updatestart, (starttime, date, id,))
conn.commit()
I have this function startlog, and a clone of it endlog.
My database log is consisted of (name, starttime, endtime, date)
Is there any way to keep track of the changes?
Desired output:
Name / Time / Date
x / time1 / date1
x / time2 / date2
I tried creating a list so everytime I'm calling out the function it will append on the list but it disappears after the session.
I used csv for my case since it's just a personal project. I used columns like ID/Time in / Time out / Total Time and used ID to determine which value to display. This is the snippet of my code (using tkinter for gui)
def csvwrite():
with open ('test.cvs', 'a', newline="") as csvfile:
writer = csv.writer(csvfile)
tup1 = (enteruser.id, log.start, log.end)
writer.writerow(tup1)
csvfile.close()
def csvread():
with open('test.cvs', 'r') as csvfile:
reader = csv.reader(csvfile)
filtered = filter(filterer, reader)
res = []
for i in filtered:
print(i)
historylbl = Label(historyWindow.historywndw, text = i)
historylbl.pack()

How can I add a function parameter to get days of current (default), previous or next week

My function returns days of the previous week. I want to replace this part:
- datetime.timedelta(days=7)
with a function parameter that will receive 'prevweek', 'nextweek' or 'current'. Thanks.
import datetime
def days_of_week():
days = []
sunday = (datetime.datetime.today() - datetime.timedelta(days=7)
- datetime.timedelta(days=datetime.datetime.today().isoweekday() % 7))
friday = (datetime.datetime.today() - datetime.timedelta(days=7)
- datetime.timedelta(days=(datetime.datetime.today().weekday() - 4) % 7,
weeks=-1))
while sunday.date() <= friday.date():
days.append(sunday.date())
sunday += datetime.timedelta(days=1)
return days
I thought maybe something like:
import datetime
def days_of_week(rel_week='current'):
days = []
week_options = {
'prevweek''prevweek': datetime.timedelta(days=-7),
'nextweek''nextweek': datetime.timedelta(days=7)7,
'current': datetime.timedelta(days=0)0
}
sunday = (datetime.datetime.today() + f'{week_options[rel_week]}' +
- datetime.timedelta(days=datetime.datetime.today().isoweekday() % 7)
+ datetime.timedelta(days=week_options[rel_week]))
friday = (datetime.datetime.today() + f'{week_options[rel_week]}' +
+ datetime.timedelta(days=(4-datetime.datetime.today().weekday() - 4) % 7,)
+ weeks=-1datetime.timedelta(days=week_options[rel_week]))
print(sunday,friday)
while sunday.date() <= friday.date():
days.append(sunday.date())
sunday += datetime.timedelta(days=1)
return days
I figure it out, and also found a bug where looking for friday of the current week. The solution for the bug were taken from How to calculate next Friday?
import datetime
def days_of_week(rel_week='current'):
days = []
week_options = {
'prevweek': -7,
'nextweek': 7,
'current': 0
}
sunday = (datetime.datetime.today()
- datetime.timedelta(days=datetime.datetime.today().isoweekday() % 7)
+ datetime.timedelta(days=week_options[rel_week]))
friday = (datetime.datetime.today()
+ datetime.timedelta(days=(4-datetime.datetime.today().weekday()) % 7)
+ datetime.timedelta(days=week_options[rel_week]))
while sunday.date() <= friday.date():
days.append(sunday.date())
sunday += datetime.timedelta(days=1)
return days

odoo 11 / Python 3: How to find expiry date of subscription if some weekdays are excluded

I am working on Subscription Management system. I have issue calculating Subscription Expiry Date. The scenario is as follows,
If a person subscribe for 1 month say 1/11/2018 to 30/11/2018 [DD/MM/YYYY] total 30 days but he want to exclude Friday & Saturday from each week of 30 days. So how should I calculate the Expiry Date?
Logic is : Say End Date = Expiry Date then find Fri/Sat from 1/11/2018 to 30/11/2018 which comes out 5 Fri & 4 Sat = 9 days. Add to Expiry Date which will be 09/12/2018. Now search Fur & Sat between End Date & Expiry Date which comes out 1 Fri & 2 Sat = 3 days. Now End Date = Expiry Date and Expiry Date + 3 Days = 12/12/2018. Search between End Date & Expiry Date for Fri & Sat which is 0 so the Expiry Date is 12/12/2018 return value <<
Following code does this but method returns 09/12/2018 instead 12/12/2018. What is wrong in this??
#api.one
def get_expiry_date(self, start, end, day_selection):
print("i am in 2nd", start, end, day_selection)
dayformat = '%Y-%m-%d'
current_date = datetime.now().date()
if start:
start = datetime.strptime(str(start), dayformat).date()
if end:
end = datetime.strptime(str(end), dayformat).date()
if day_selection:
selected_days = day_selection
if start < current_date:
start = datetime.strptime(str(start), dayformat).date()
weekdays = self.weekday_count(start,end)
print("days for start and end date",start,end, day_selection)
adddays = 0
if weekdays:
for i in range(len(day_selection)):
for item in weekdays[0]:
weekdays_dict = item
print("dict", type(weekdays), type(weekdays[0]), weekdays_dict)
print("compare", selected_days[i], weekdays_dict, selected_days[i] == weekdays_dict)
if selected_days[i] == item:
adddays = adddays + weekdays[0].get(item)
new_start = end
end = datetime.strptime(str(end), dayformat).date() + timedelta(days=adddays)
start = new_start
print("New Expiry Date", start, end, adddays)
if adddays > 0:
self.get_expiry_date(start, end, day_selection)
print("type of end is ", type(end))
print("selected days are", selected_days[i],weekdays[0], weekdays[0].get(item), adddays)
print("last returned values is",end)
return end
In your question, there is no definition of day_selection nor weekday_count thus hard to see what is going on. Probably the issue is with recursion, which is not necessary.
If day_selection is defined as the days selected to exclude (list of strftime('%a'), like ['Mon', 'Tue']), then:
from datetime import datetime, timedelta
def get_expiry_date(start, end, day_selection):
dayformat = '%Y-%m-%d'
weekdays= {}
start = datetime.strptime(str(start), dayformat).date()
end = datetime.strptime(str(end), dayformat).date()
# Count weekdays
for i in range((end - start).days+1):
weekday = (start + timedelta(i)).strftime('%a')
weekdays[weekday] = 1 + weekdays.setdefault(weekday, 0)
# Count subscription days to add
sub_days_to_add = 0
for selected in day_selection:
sub_days_to_add += weekdays.setdefault(selected, 0)
# Count calender days to add
cal_days_extension = 0
while sub_days_to_add > 0:
if (end + timedelta(days=cal_days_extension + 1)).strftime('%a') not in day_selection:
sub_days_to_add -= 1
cal_days_extension += 1
# Add to end day
return end + timedelta(days=cal_days_extension)
Tests:
print (get_expiry_date('2018-11-01', '2018-11-30', ['Fri', 'Sat']))
# ---> 2018-12-12
print (get_expiry_date('2018-11-01', '2018-11-30', []))
# ---> 2018-11-30
Also it would be safer to use from odoo.tools import DEFAULT_SERVER_DATE_FORMAT than dayformat = '%Y-%m-%d'. And for parameters like start & end, if they are mandatory, can make these fields required in Odoo.
The logic that you are trying to do is hard
From what I understand you are running a subcription system
based on number of days not dates so in order to compute the
end date you just need this:
start date (ex: 01/11/2018)
number of days (ex: 30 days)
excluded days (ex: Friday, Saturday)
# day are like this
# Fri
# Sat
# Sun
# Mon
# Tue
# Wed
# Thu
# start day
def get_expiry_date(start_date, number_of_days, excluded_days = None):
""" compute end date of subcription period """
if excluded_days is None:
excluded_days = []
# check params
start_date = str(start_date)
if number_of_days < 1:
raise exception.UserError(_('Number of days should be > 0!!')) # import the translate "_" method
if len(excluded_days) > 5:
raise exception.UserError(_('To much excluded days!!')) # import the translate "_" method
date_format = '%Y-%m-%d'
end_date = datetime.strptime(start_date, date_format)
# compute end date
# keeping adding one day until you finish your days
add_one_day = timedelta(days=1)
while number_of_days > 1:
end_date += add_one_day
if end_date.strftime('%a') not in excluded_days:
# day is not excluded compute it
number_of_days += -1
return end_date.strftime(date_format)
And this a test value that you can checkout:
print get_expiry_date('2018-11-01', 30, ['Fri', 'Sat']) # 2018-12-12
print get_expiry_date('2018-11-01', 30, ['Fri']) # 2018-12-05
print get_expiry_date('2018-11-01', 30, []) # 2018-11-30
print get_expiry_date('2018-11-01', 90, []) # 2019-01-29
print get_expiry_date('2018-11-01', 30, ['Mon', 'Thu']) # 2018-12-11
If you have all ready a running system you can use this:
def get_expiry_date(start_date, end_date, excluded_days = None):
if excluded_days is None:
excluded_days = []
start_date = str(start_date)
end_date = str(end_date)
date_format = '%Y-%m-%d'
# compute number of days
number_of_days = (datetime.strptime(end_date, date_format) - datetime.strptime(start_date, date_format)).days
expiry_date = datetime.strptime(start_date, date_format)
if len(excluded_days) > 5:
raise Exception('To much excluded days!!')
# keeping adding one day until you finish your days
one_day = timedelta(days=1)
while number_of_days > 0:
expiry_date += one_day
# if day is not excluded reduce the number of left days
if expiry_date.strftime('%a') not in excluded_days:
number_of_days += -1
return expiry_date.strftime(date_format)
print get_expiry_date('2018-11-01', '2018-11-30', ['Fri', 'Sat']) # 2018-12-12

Resources