I need to check if a given date is exactly a month ago from today, for example, if today is 01-Nov-2021 then exactly a month ago will be 01-Oct-2021 (not exactly 30 days.)
I wrote a code and it works fine
today = fields.Date.from_string(fields.Date.today())
if today.month == 1:
one_month_ago = today.replace(year=today.year - 1, month=12)
else:
extra_days = 0
while True:
try:
one_month_ago = today.replace(month=today.month - 1, day=today.day -
extra_days)
break
except ValueError:
extra_days += 1
if one_month_ago == given_date:
# do something
else:
# do something
It handles well mostly all the cases but mishandles some cases. For example, the given date is 31-March-2021 and today date is 30-April-2021 and 31-April-2021 will not come to compare. I need my code to run daily and check something. It also mishandles cases of 29-31 January because 29-31 February will not come to compare.
From the given date, you can find the previous month and year and using these two obtained values, find the length of the previous month. The last thing to be done will be to compare the day of the given date with the length of the previous month and accordingly, return the desired date.
Demo:
from datetime import date
from calendar import monthrange
def date_a_month_ago(today):
x = today.month - 1
previous_month = 12 if x == 0 else x
year = today.year - 1 if x == 0 else today.year
last_day_of_previous_month = monthrange(year, previous_month)[1]
day = last_day_of_previous_month if today.day > last_day_of_previous_month else today.day
return date(year, previous_month, day)
# Tests
print(date_a_month_ago(date(2021, 11, 1)))
print(date_a_month_ago(date(2021, 1, 31)))
print(date_a_month_ago(date(2021, 12, 31)))
print(date_a_month_ago(date(2021, 3, 29)))
print(date_a_month_ago(date(2020, 3, 29)))
print(date_a_month_ago(date(2021, 3, 30)))
print(date_a_month_ago(date(2020, 3, 30)))
Output:
2021-10-01
2020-12-31
2021-11-30
2021-02-28
2020-02-29
2021-02-28
2020-02-29
ONLINE DEMO
May be like this:
from typing import Tuple
def last_month(year: int, month: int) -> Tuple[int, int]:
y, m = year, month - 1
if m == 0:
y, m = y - 1, 12
return y, m
def one_month_ago(today: datetime) -> datetime:
y, m = last_month(today.year, today.month)
dt = today.replace(year=y, month=m)
for day in range(today.day, 0, -1):
try:
return dt.replace(day=day)
except ValueError:
...
I did this and it's giving me the required output:
from datetime import date
from calendar import monthrange
def is_one_month(given_date, today):
x = today.month - 1
previous_month = 12 if x == 0 else x
year = today.year - 1 if x == 0 else today.year
last_day_of_previous_month = monthrange(year, previous_month)[1]
day = last_day_of_previous_month if today.day > last_day_of_previous_month else today.day
one_month_ago = date(year, previous_month, day)
if today.month == 2:
if given_date.month == today.month-1 and given_date.year == today.year and given_date.day >= 28:
return 'it is one month before'
if today.month == 4 or today.month == 6 or today.month == 9 or today.month == 11:
if given_date.month == today.month-1 and given_date.day == 31:
return 'it is one month before'
if one_month_ago == given_date:
return 'it is one month before'
else:
return 'it is NOT one month before'
print(is_one_month(date(2021, 1, 30), date(2021, 2, 28)))
Output:
it is one month before
I have an assignment in my computer science class and need help fixing a function but I have no clue what's wrong! The function is called 'days_left'. This function takes in three variables, Day, Month, and Year. It is supposed to output how many total days are left in that year. I have tried my best but cannot figure it out! Any help will be greatly appreciated. Here's the script:
def leap_year(year):
if (year % 4) == 0:
if (year % 100) == 0:
if (year % 400) == 0:
return True
else:
return False
else:
return True
else:
return False
def number_of_days(month, year):
days31 = [1, 3, 5, 7, 8, 10, 12]
days30 = [4, 6, 9, 11]
if month in days31:
return 31
elif month in days30:
return 30
else:
if not leap_year(year):
return 28
else:
return 29
def days_left(month, day, year):
days = 0
for i in range(1, month):
days += number_of_days(i, year)
days += day
for x in range(year):
if leap_year(year):
return 366 - days
else:
return 365 - days
if __name__ == '__main__':
print("Please enter a date: \n")
d = int(input("Day: "))
m = int(input("Month: "))
y = int(input("Year: "))
print("\nMenu:")
print("1) Calculate the number of days in the given month.")
print("2) Calculate the number of days left in the given year.")
selection = int(input())
if selection == 1:
print(number_of_days(m, y))
elif selection == 2:
print(days_left(d, m, y))
else:
print('')
def days_left(day, month, year):
It should be 'day, month' instead 'month, day' since you are calling function with days_left(d, m, y)
This is my first question here so please forgive and educate on any formatting errors. I am new to Python and going through automate the boring stuff. Decided to expand the date detection project by using the clipboard and formatting some things also. The problem I have is in any operation taken on the year part of the REGEX.
I have commented out my last attempt to validate the year and gave up and changed the REGEX to only find dates from 1000 to 2999 and skipped code validation of the dates.
I now need to validate leap years but I'm back to having to work with the year variable, but once again no operation has any effect.
Basically the problem is I can extract the year value and display it but I cannot modify it or do checks against it.
#! python3
#! detect dates in a block of text
import pyperclip
import re
#!import numpy as np
text = str(pyperclip.paste())
def datedetection(text):
dateRegex = re.compile(
r"""(
(\d|\d\d) #! match day
(/{1}) #! match /
(\d|\d\d) #! match month
(/{1}) #! match /
([1|2][0-9][0-9][0-9]) #! match year
)""",
re.VERBOSE,
)
matches = []
for groups in dateRegex.findall(text):
day = str(groups[1])
slash1 = str(groups[2])
month = str(groups[3])
slash2 = str(groups[4])
year = str(groups[5])
month_range_30 = ["04", "06", "09", "11"]
month_range_31 = ["01", "03", "05", "07", "08", "10", "12"]
month_range_Feb = ["02"]
#!year_range = np.arange(1000, 3000, 1).tolist()
if len(day) == 1:
day = "0" + day
else:
day = day
if len(month) == 1:
month = "0" + month
else:
month = month
if month in month_range_31:
if int(day) > 31:
day = "Too many days in a month with only 31 days."
slash1 = month = slash2 = year = ""
elif month in month_range_30:
if int(day) > 30:
day = "Too many days in a month with only 30 days."
slash1 = month = slash2 = year = ""
elif month in month_range_Feb:
if int(day) > 29:
day = "Too many days in February."
slash1 = month = slash2 = year = ""
elif int(month) > 12:
day = "Found an invalid month."
slash1 = month = slash2 = year = ""
elif month in month_range_Feb:
if (
int(day) == 29
and (int(year) % 4 == 0)
and (int(year) % 400 == 0)
and (int(year) % 100 == 0)
):
day = day
elif month in month_range_Feb:
if (
int(day) == 29
and (int(year) % 4 == 0)
and (int(year) % 100 != 0)
):
day = "Found an invalid leap year."
slash1 = month = slash2 = year = ""
#!elif year not in year_range:
#!day = "Year is out of range."
#!slash1 = month = slash2 = year = ""
dates = "".join([day, slash1, month, slash2, year])
matches.append(dates)
if len(matches) > 0:
pyperclip.copy("\n".join(matches))
print("Copied to clipboard:")
print("\n".join(matches))
else:
print("No dates found.")
datedetection(text)
In my approach to this project, I considered validating the days, months, and year ranges as part of the regular expression. I then defined functions to check for the leap year, and validate the number of days according to the months.
I found that way simpler and easier to understand and follow. As below:
dateRegex = re.compile(r'([0-3][0-9])/([0-1][0-9])/([1-2][0-9]{3})')
def is_leap_year(year):
year = int(year)
if year % 4 == 0:
if year % 100 == 0:
return year % 400 == 0
else:
return True
else:
return False
def is_valid_date(day, month, year):
if month == '02':
if is_leap_year(year):
return int(day) <= 29
else:
return int(day) <= 28
elif month in ['04', '06', '09', '11']:
return int(day) <= 30
else:
return int(day) <= 31
You can find the rest of my code below.
https://gist.github.com/alialbusaidi/f56e4c9342f622434f8bff0549f94884
The problem was the operations before the year operations. The day and month operations were overwriting the year values. Not entirely sure how or why at this point, but moving the year code above the day and month code has started to fix the issue.
This question already has answers here:
Why does this python method gives an error saying global name not defined?
(3 answers)
Closed 4 years ago.
below is my code (all the methods are indented in original), after running that code I get this error:
NameError: global name 'checkDate' is not defined
the method are defined so i don't know whats the issue here (the code was in Java and work well there I just did the modification for this to work in python)
class Date:
def __init__(self, *args):
if len(args) == 3:
self._day, self._month, self._year = args
elif len(args) == 1:
self._day = args[0]._day
self._month = args[0]._month
self._year = args[0]._year
else:
raise TypeError("wrong number of arguments to Date constructor")
# methods
# checkDate - Check if the date is valid
def checkDate (self, day, month, year):
if year > 0 and month > 0 and month < 13 and day > 0 and day < 32:
if month == 1 or 3 or 5 or 7 or 8 or 10 or 12: return True
if month == 4 or 6 or 9 or 11:
if day == 31: return False
return True
if month == 2:
if (month % 4 == 0 and (month % 100 != 0 or (month % 100 == 0 and month % 400 == 0))):
if day > 28: return False
elif day > 27: return False
return False
# calculateDate - Computes the day number since the beginning of the Christian counting of years.
def calculateDate (self, day, month, year):
if month < 3:
year -= 1
month = month + 12
return 365 * year + year/4 - year/100 + year/400 + ((month+1) * 306)/10 + (day - 62);
# getDay - Return the day
def getDay(self): return self._day
# getMonth - Return the month
def getMonth(self): return self._month
# getYear - Return the year
def getYear(self): return self._year
# setDay - Sets the day (only if date remains valid)
def setDay(self, dayToSet):
if checkDate(dayToSet,_month,_year): _day = dayToSet
# setMonth - Sets the month (only if date remains valid)
def setMonth (self, monthToSet):
if checkDate(_day,monthToSet,_year): _month = monthToSet
# setYear - sets the year (only if date remains valid)
def setYear(self, yearToSet):
if checkDate(_day,_month,yearToSet): _year = yearToSet
def main():
birthDate = Date(1,1,2000)
print(birthDate.getDay())
print(birthDate.getMonth())
print(birthDate.getYear())
birthDate.setDay(8)
birthDate.setMonth(8)
birthDate.setYear(1987)
print(birthDate.getDay())
print(birthDate.getMonth())
print(birthDate.getYear())
if __name__ == "__main__": main()
You need to do self.checkDate() since checkDate is a method of your Date class.
Additionally, in setDay(), setMonth() and setYear() the variables being assigned to (_day, _month, and _year respectively) need a self. before them as well.
I think I might have an infinite loop because I keep getting back an error message whenever I run the code, it says "program shut down for using 13 CPU seconds."
The entire code, should take a date as input and output the next day, this code assumes all months are 30 days. Aside from the daysBetweenDates function, everything else seems to be working fine. Does anyone have any suggestions? Or maybe someone can tell me what I'm missing?
def daysInMonth(year, month):
return 30
def nextDay(year, month, day):
if day < 30:
return year, month, day + 1
else:
if month == 12:
return year + 1, 1, 1
else:
return year, month + 1, 1
return
def dateIsBefore(year1, month1, day1, year2, month2, day2):
if year1 < year2:
return True
def daysBetweenDates(year1, month1, day1, year2, month2, day2):
days = 0
while dateIsBefore(year1, month1, day1, year2, month2, day2):
days += 1
return days
def test():
test_cases = [((2012,1,1,2012,2,28), 58),
((2012,1,1,2012,3,1), 60),
((2011,6,30,2012,6,30), 366),
((2011,1,1,2012,8,8), 585 ),
((1900,1,1,1999,12,31), 36523)]
for (args, answer) in test_cases:
result = daysBetweenDates(*args)
if result != answer:
print "Test with data:", args, "failed"
print result
else:
print "Test case passed!"
test()
Your daysBetweenDates() algorithm does indeed have an infinite loop.
while dateIsBefore(year1, month1, day1, year2, month2, day2):
days += 1
You never modify any of the year/month/day values here in the loop, so if the condition is true once, it will be true forever.
The solution, in concept, would be to decrease day2 by one every time, so when the two dates become equal, days will be the day difference between them. However, since you said you made the assumption that each month has 30 days, you're overcomplicating things for yourself. You can find the day difference between two dates by converting the (year, month, day) tuple to a day value. For example,
def days_between_dates(y1, m1, d1, y2, m2, d2):
date1 = y1*360 + (m1-1)*30 + d1
date2 = y2*360 + (m2-1)*30 + d2
# assuming you want to return 0 if date2 is before date1
return date2 - date1 if date2 >= date1 else 0
This solution counts daysInMonth correctly, doesn't involve any kind of infinite loop, counts correct dates in a leap year and provides an exact answer to inputs.
The solution is written in Python.
def daysInMonth(m,y):
if m == 1 or m == 3 or m == 5 or m == 7 or m == 8 or m ==10 or m == 12:
return 31
else :
if m == 2:
if isLeapYear(y):
return 29
return 28
return 30
def nextDay(y,m,d):
if d < daysInMonth(m,y):
return y, m, d+1
else:
if m == 12:
return y + 1, 1, 1
else:
return y, m + 1, 1
def isLeapYear(y):
if y % 400 == 0:
return True
elif y % 4 == 0:
return True
else:
if y % 100 == 0:
return False
return False
def dateIsBefore(y1, m1, d1, y2, m2, d2):
days = 0
if y1 < y2 :
return True
if y1 == y2:
if m1 < m2:
return True
if m1 == m2:
return d1<d2
return False
def daysbetweendates(y1, m1, d1, y2, m2, d2):
days = 0
while dateIsBefore(y1, m1, d1, y2, m2, d2):
y1, m1, d1 = nextDay(y1, m1, d1)
days = days + 1
return days
def test():
test_cases = [((2012,1,1,2012,2,28), 58),
((2012,1,1,2012,3,1), 60),
((2011,6,30,2012,6,30), 366),
((2011,1,1,2012,8,8), 585 ),
((1900,1,1,1999,12,31), 36523)]
for (args, answer) in test_cases:
result = daysbetweendates(*args)
if result != answer:
print "Test with data:", args, "failed"
print result
else:
print "Test case passed!"
test()
The function dateIsBefore is incomplete.
def dateIsBefore(year1, month1, day1, year2, month2, day2):
if year1 < year2:
return True
if year1 == year2 and month1 < month2:
return True
if year1 == year2 and month1 == month2 and day1 < day2:
return True
return False
(It could be simplified, but I left it like this for clarity. Also, of course, you could use datetime.date--someone else already made that comment; see also other answer that tells you to increment year1,month1, and day1)
dateIsBefore will always return True.