Cognitive Complexity of functions should not be too high - python

When I use SonarLint to check code, it notifies Critical that Cognitive Complexity is a measure of how hard the control flow of a function is to understand. Functions with high Cognitive Complexity will be difficult to maintain.
I use a lot of if else statement, but can not use switch case.
This is my code:
str_time = str_time.lower()
if (bool(re.search(r'\d', str_time)) == True) and ('tối' or 'chiều' in str_time):
day = re.findall(r'\d+', str_time)[0]
if int(day) < datetime.date.today().day:
month = datetime.date.today().month + 1
else:
month = datetime.date.today().month
year = datetime.date.today().year
day = f'{year}-{month}-{day}'
return format_datetime(day)
elif 'hôm nay' in str_time or 'hn' in str_time or 'chiều nay' in str_time or 'tối nay' in str_time:
return format_datetime(datetime.date.today())
elif 'ngày mai' in str_time or 'mai' in str_time:
day = datetime.date.today() + datetime.timedelta(days=1)
elif 'ngày mốt' in str_time or 'mốt' in str_time:
day = datetime.date.today() + datetime.timedelta(days=2)
elif 'thứ 2 tuần sau' in str_time:
num = 7 - datetime.date.today().weekday() + 0
day = datetime.date.today() + datetime.timedelta(days=num)
elif 'thứ 3 tuần sau' in str_time:
num = 7 - datetime.date.today().weekday() + 1
day = datetime.date.today() + datetime.timedelta(days=num)

Sonar lint is right. It seems your code complexity is high. You should create smaller methods or change the logic. But if that is not possible, just ignore the linter.

Related

Finding weekday with specific date

I am learning python and going through some interactive exercises. Specifically, I'm working on Friday the 13th.
I have rewritten several iterations of this but can never seem to lock it down. With this version, it seems to get hung up when run with the simulated start date of 2025-06-12 which means there's a problem with the "this month" section. Since it returns an accurate Friday the 13th except not 2025-06-13, I suspect it's a problem with the elif statement, particularly the
and date.fromisoformat(current_year + '-' + current_month + '-13').weekday == 4:
Here's the most recent iteration of this.
def friday_the_13th():
from datetime import date
current_year = str(date.today().year)
current_month = str(date.today().month)
if len(current_month) == 1: current_month = '0' + current_month
#Function to increment to the 13th of next month
def NextMonth13(startdate):
lst_date = str(startdate)
lst_date = lst_date.split('-')
month = int(lst_date[1])
if month == 12:
year = str(int(lst_date[0]) + 1)
month = '01'
return str(year + '-' + month + '-' + '13')
else:
year = lst_date[0]
month = str(month + 1)
if len(month) == 1: month = '0' + month
return str(year + '-' + month + '-' + '13')
# Return today if today is Friday the 13th
if date.today().weekday() == 4 and date.today().day == 13:
return date.today()
# Check if this month's 13th is in the future and if it's a Friday
elif date.today().day < 13 and date.fromisoformat(current_year + '-' + current_month + '-13').weekday == 4:
return str(date.fromisoformat(current_year + '-' + current_month + '-13'))
#Check next month and return result if Friday 13
else:
result = NextMonth13(date.today())
while not (date.fromisoformat(result).weekday() == 4):
result = NextMonth13(result)
if date.fromisoformat(result).weekday() == 4:
return result
Would someone mind giving me some guidance on what I might be doing wrong?
First, your error is that you forgot the parenthesis after the weekday method call: date.fromisoformat(current_year + '-' + current_month + '-13').weekday() == 4 (FYI, date.fromisoformat(current_year + '-' + current_month + '-13').weekday returns the memory address of the method, something like this <built-in method weekday of datetime.date object at 0x7fa4e36058f0>. As you can see, it is nowhere near the result you were expecting, so it was normal for your program to behave this way.)
Second, you are needlessly complicating yourself by doing str conversions all the time:
def friday_the_13th():
from datetime import datetime, timedelta
days_passed = 0
today = datetime.today()
while True:
curr = today + timedelta(days=days_passed)
if curr.day == 13 and datetime.weekday(curr) == 4:
return str(datetime.date(curr))
days += 1
This is more readable and less prone to error as you only convert to string at the end, after you've handled all your calculations.
Not sure if it helps but to calculate the future Friday 13th you can do something like:
import datetime
def get_13th_future(startdate, months):
result=[]
year=startdate.year
month=startdate.month
checkdate = datetime.date(year=year, month=month, day=13)
for i in range(month):
if checkdate.weekday()==4:
result.append(checkdate.isoformat())
month+=1
if month==13:
year+=1
month=1
checkdate=datetime.date(year=year,month=month,day=13)
return result
startdate=datetime.datetime.now()
print(get_13th_future(startdate,1000))
If you like to search for a specific date you might construct a set instead of the list.

Print something if the date changes

What exactly I am trying to do here is when Tomorrow comes (00:00) it should print Yes
import datetime
from datetime import date
r = True
rr = True
while r:
Today_Date = date.today()
while rr:
Tomorrow_Date = datetime.date.today() + datetime.timedelta(days=1)
if Today_Date == Tomorrow_Date:
print("Yes")
The way you have it now, your rr while loop will continually run and update Tomorrow_Date, and when the day rolls over, it will update before it has a chance to be compared to Today_Date. You should set both Today_Date and Tomorrow_Date outside that loop, and only update them when the dates change.
This should do the trick:
If you want two loops for other reasons:
import datetime
r = True
while r:
rr = True
Tomorrow_Date = datetime.date.today() + datetime.timedelta(days=1)
while rr:
if datetime.date.today() >= Tomorrow_Date:
print("Yes")
rr = False
Or as a single loop:
import datetime
r = True
Tomorrow_Date = datetime.date.today() + datetime.timedelta(days=1)
while r:
if datetime.date.today() >= Tomorrow_Date:
print("Yes")
Tomorrow_Date = datetime.date.today() + datetime.timedelta(days=1)
It might be a good idea to add a time.sleep() in to slow down the loops, depending on how accurate you need to be also.

Is there a best way to change given number of days to years months weeks days in Python?

I am giving number of days to convert them to years, months, weeks and days, but I am taking default days to 365 and month days to 30. How do I do it in an effective way?
def get_year_month_week_day(days):
year = days / 365
days = days % 365
month = days / 30
days = days % 30
week = days / 7
day = days % 7
return year,month,week,day
def add_s(num):
if num > 1:
return 's '
return ' '
#register.filter
def daysleft(fdate):
cdate = datetime.datetime.now().date()
days = (fdate.date() - cdate).days
if days == 0:
return "Today"
elif days == 1:
return "Tomorrow"
elif days > 0:
year, month, week, day = get_year_month_week_day(days)
print year, month, week, day
days_left = ""
if year > 0:
days_left += str(year) + " year" + add_s(year)
if month > 0:
days_left += str(month) + " month" + add_s(month)
if week > 0:
days_left += str(week) + " week" + add_s(week)
if day > 0:
days_left += str(day) + " day" + add_s(day)
return days_left + " left"
else:
return "No time left"
It is much easier if you use a third-party library named python-dateutil:
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> now = datetime.datetime.now()
>>> td = datetime.timedelta(days=500)
>>> five_hundred_days_ago = now - td
>>> print relativedelta(now, five_hundred_days_ago)
relativedelta(years=+1, months=+4, days=+13)

Checking the format/contents of a string

This program is intended to ask for the date as dd/mm/yyyy. It should then check to see if the user inputted the date in the correct format (dd/mm/yyyy). My program is not able to recognize the format correctly. This is my program:
date = (input("enter the date as dd/mm/yyyy: "))
date = day, month, year = date.split("/")
if date == (day + '/' + month + '/' + year):
print (date)
if len(day) == 1 or len(day) == 2:
print("1")
if len(month) == 1 or len(month) == 2:
print("2")
if len(year) == 4:
print ("3")
else:
if len(day) == 1 or len(day) == 2:
print("4")
if len(month) == 1 or len(month) == 2:
print("5")
if len(year) == 4:
print ("6")
The numbers being printed currently have no other purpose than to just check the validity of the date. So far, only 4,5, and 6 are being printed, meaning my program is not recognizing the formatting of the date.
Your solution doesn't work because date=day, month, year = date.split("/") sets date to a list, then you're comparing it to a string (day + '/' + month + '/' + year). However, your solution is a solved problem, do instead:
import datetime
date = (input("enter the date as dd/mm/yyyy: "))
try: datetime.datetime.strptime(date,"%d/%m/%Y")
except ValueError: # incorrect format
In addition, you probably are turning this into a datetime object later on anyway, so you can do so in the try block!
As a further optimization, be aware that many users won't WANT to enter their dates using / as a datesep! Do some introspection on your input, and adjust your datesep appropriately.
date = input("enter the date: ")
if "-" in date: datesep = "-"
elif "/" in date: datesep = "/"
elif "." in date: datesep = "."
else: datesep = ""
if len(date) < 6: yeartype = "%y"
elif date[-4:-2] not in ("19","20"): yeartype = "%y"
else: yeartype = "%Y"
try: date = datetime.datetime.strptime(date,"%d{0}%m{0}{1}".format(datesep,yeartype))
except ValueError: # invalid date
Now your code will end up with a valid datetime object of Feb 2nd 2014 for:
02022014
222014
0222014
222014
020214
02214
2214
02-02-2014
02/02/2014
2-2-14
2/2/2014
2/2/14
etc etc etc
You can use the datetime module:
import datetime
def checkdate(date):
try:
datelist = date.split('/')
datetime.datetime(year=int(datelist[2]), month=int(datelist[1]),day=int(datelist[0]))
return True
except:
return False

How do I check if a given datetime object is "between" two datetimes?

my_event = Event.objects.get(id=4)
current_time = datetime.datetime.now()
How do I do check if my current time is between them?
my_event.start_time < current_time < my_event.end_time
Your answer is the way to go as long as start_time and end_time don't have an associated tzinfo class. You can't directly compare a naive datetime with a timezoned-datetime.
you can use a simple if comparing three dates, like this
if date1 < yourdate < date2:
...do something...
else:
...do ...
I know old, but since this is so high on Google results, answers here don't take into consideration two cases:
If your time equals either of your range, ie your range is 6-8 and it is 6.
If your time range is say 18:00 to 6:00, valid range; however 19:00 would not match.
I wrote a function to take care of time comparison, hope this helps anyone viewing this old question.
def process_time(intime, start, end):
if start <= intime <= end:
return True
elif start > end:
end_day = time(hour=23, minute=59, second=59, microsecond=999999)
if start <= intime <= end_day:
return True
elif intime <= end:
return True
return False
The datetimes getting tested need to all naive (no timezone) or all aware (timezone). An exception should occur if you try to compare aware and naive. If all the datetimes are aware the timezones don't actually have to match that appears to be taken into consideration when comparing.
e.g.
class RND(datetime.tzinfo):
""" Random timezone UTC -3 """
def utcoffset(self, dt):
return datetime.timedelta(hours=-3)
def tzname(self, dt):
return "RND"
def dst(self, dt):
return datetime.timedelta(hours=0)
april_fools = datetime.datetime(year=2017, month=4, day=1, hour=12, tzinfo=pytz.UTC)
random_dt = datetime.datetime(year=2017, month=4, day=1, hour=9, tzinfo=RND())
random_dt == april_fools
# True as the same time when converted back to utc.
# Between test of 3 naive datetimes
start_spring = datetime.datetime(year=2018, month=3, day=20)
end_spring = datetime.datetime(year=2018, month=6, day=21)
april_fools = datetime.datetime(year=2018, month=4, day=1)
if start_spring < april_fools < end_spring:
print "April fools is in spring"
This is my script of checking the time between two different timeslots. One for morning and one for evening. This is the extended script using #Clifford's script
def Strategy_Entry_Time_Check():
current_time = datetime.datetime.now()
#current_time = current_time.replace(hour=13, minute=29, second=00, microsecond=00) #For testing, edit the time
morning_start = current_time.replace(hour=9, minute=30, second=00, microsecond=00)
morning_end = current_time.replace(hour=11, minute=00, second=00, microsecond=00)
evening_start = current_time.replace(hour=13, minute=00, second=00, microsecond=00)
evening_end = current_time.replace(hour=15, minute=00, second=00, microsecond=00)
if morning_start <= current_time <= morning_end:
print("Morning Entry")
return True
elif evening_start <= current_time <= evening_end:
print("Evening Entry")
return True
print("No Entry")
return False

Categories

Resources