python - find year given day, month, and weekday name - python

I have a column of strings that give me month and day and day name as
Date Week Day
03-03 Friday
03-19 Saturday
03-18 Saturday
03-18 Monday
....
I want to return the most recent year, given those months and days, but also given the name of day.
so
Date Week Day Max Year
03-03 Friday 2018
03-19 Saturday 2016
03-18 Saturday 2017
03-18 Monday 2013
....
EDIT: Sorry forgot to add what I was thinking and trying
So I'm trying to do something like:
from datetime import datetime
day = 28
month = 3
name = 'Monday'
t = datetime.strptime('{}_{}_{}'.format(month,day,name), '%d_%m_%A')
t.strftime('%Y')
But it stores year as 1900. I'm just not sure how to get it to return the year as the most recent, or maxyear?

As far as I know, you will have to write a for loop to search for the most recent year. This shows an example of how you can do that.
import datetime
today = datetime.date.today() # This would be the user input date
weekday = 1 # look for the most recent Tuesday on today's date
while(today.weekday() != weekday and today.year > 1000):
today = today.replace(year=today.year - 1) # subtract one year from the date
if today.weekday() == weekday:
print("The Most recent Tuesday was in:",today.year)
break
if today.year <= 1000:
print("No Date found matching critieria since year 1000 AD")

Related

Converting Year - Week to days of the week - PYTHON

I have the following data:
Year&Week
Year
Week
2022-50
2022
50
2022-51
2022
51
2022-52
2022
52
I would like to convert that information by adding a new column that reflects the last day (sunday) and the initial day (monday) of the weeks. So my final dataframe should looks like the following:
Year&Week
Year
Week
Initial day of week
Last day of week
2022-50
2022
50
12/Dec/2022
18/Dec/2022
2022-51
2022
51
19/Dec/2022
25/Dec/2022
2022-52
2022
52
26/Dec/2022
01/Jan/2023
We can use pd.to_datetime and create date base:
%W: The week number of the year
%Y: The year number
%a: Weekday, short version
%b: The first three characters of the month name
%d: The day of the month
df['Initial day of week'] = pd.to_datetime(
df['Week'].astype(str) + df['Year'].astype(str) + 'Mon', format='%W%Y%a'
).dt.strftime("%d/%b/%Y")
df['Last day of week'] = pd.to_datetime(
df['Week'].astype(str) + df['Year'].astype(str) + 'Sun', format='%W%Y%a'
).dt.strftime("%d/%b/%Y")
print(df)
Output:
Year&Week Year Week Initial day of week Last day of week
0 2022-50 2022 50 12/Dec/2022 18/Dec/2022
1 2022-51 2022 51 19/Dec/2022 25/Dec/2022
2 2022-52 2022 52 26/Dec/2022 01/Jan/2023
You can use the python datetime module to convert 2022-50 which is probably is in the "%Y%W" Format to M/d/y format, like the below's peace of codes:
from datetime import datetime
input_date = "2022-50"
right_date = datetime.strptime(input_date, "%Y-%W")
formate_date_reult = right_date.strftime("%m/%d/%Y")
print(formate_date_reult) # Output is: 12/25/2022

Get last friday of each month in python

I want last friday of each month for upcoming three months.
Friday_date = datetime.date.today()
while Friday_date.weekday() != 4:
Friday_date += datetime.timedelta(1)
This gives me the nearest friday. I want to make sure this is the last friday of this month so that i can add 28 days to get next friday.
The easiest way to do this is to use the module dateutil:
>>> from dateutil.relativedelta import FR, relativedelta
>>> datetime.date.today()+relativedelta(day=31, weekday=FR(-1))
datetime.date(2021, 6, 25)
Don't assume you can get the last Friday of subsequent months just by adding 28 days. It won't always work. Adding 28 days to the last Friday of February 2024 gives you this:
>>> datetime.date(2024,2,1)+relativedelta(day=31, weekday=FR(-1), days=28)
datetime.date(2024, 3, 22)
but the last Friday of that month is 29 March. Let dateutil do that correctly for you:
>>> datetime.date(2024,2,1)+relativedelta(day=31, weekday=FR(-1), months=1)
datetime.date(2024, 3, 29)
If needed with standard library only, here is with calendar and datetime:
import calendar
from datetime import date
today = date.today()
year, month = today.year, today.month
n_months = 4
friday = calendar.FRIDAY
for _ in range(n_months):
# get last friday
c = calendar.monthcalendar(year, month)
day_number = c[-1][friday] or c[-2][friday]
# display the found date
print(date(year, month, day_number))
# refine year and month
if month < 12:
month += 1
else:
month = 1
year += 1
where the line c[-1][friday] or c[-2][friday] first checks the last week of the month: is Friday nonzero there? if so take it, else look at the week before where there must be a Friday.
This prints
2021-06-25
2021-07-30
2021-08-27
2021-09-24
This formula gets you the day of the last Friday of any given month:
import calendar
year = 2021
month = 6
last_day = calendar.monthrange(year, month)[1]
last_weekday = calendar.weekday(year, month, last_day)
last_friday = last_day - ((7 - (4 - last_weekday)) % 7)
# ^ ^
# | Friday
# days in a week
This is my first coffee, so this can probably be condensed a bit, but it illustrates the logic. last_day is the last calendar day (30 for June), last_weekday is what weekday it is (2 for Wednesday), and based on that we simply calculate how many days to subtract to land on the last Friday (25).
If you want to know the last friday you can do this :
from datetime import date
from datetime import timedelta
today = date.today()
offset = (today.weekday() - 4) % 7
last_wednesday = today - timedelta(days=offset)

Calculate date from given weekday and month, but variable year (e.g. 3rd Sunday in August in "x" year)

My pandas dataframe "MSYs" has a "start_yr" variable built from a datetime column "Start Date" showing the year of someone's start date (note that month and day of "Start Date" also vary).
start_yr = pd.DatetimeIndex(MSYs['Start Date']).year
I want to use start_yr to help me return a datetime date in another column "Grant Start" showing the third Sunday in August of that variable year. I am stumped.
This is an answer to a similar quesion which might help you.
Use the datetime library.
Loop through subset of days in august of that year.
Check if if it is thursday.
Python: third Friday of a month
Here is a solution based on one of the answers in that thread. It is a generalized solution so you should be able to pick a month, a day of the week and the number in the month you want and get that date.
Note: Week days are 0 indexed starting at Monday. So Sunday's index is 6 and monday's index is 0. So when you feed the day_of_week into this function make sure you choose numbers between 0 and 6.
I have defaulted it to choose the 3rd Sunday of the month given the year.
import datetime as dt
def get_year_day(year,month=8,day_of_week=6,num_in_month=3):
## set up possible ranges
range_1 = 7*(num_in_month-1)+1
range_2 = 7*num_in_month+1
## loop through possible range in the year and the month
for i in range(range_1,range_2):
date = dt.datetime(year=year,month=month, day=i)
## if we have our weekday we can break
if date.weekday()==day_of_week:
break
return date
for i in range(2015,2021):
print(i,get_year_day(i))
2015 2015-08-16 00:00:00
2016 2016-08-21 00:00:00
2017 2017-08-20 00:00:00
2018 2018-08-19 00:00:00
2019 2019-08-18 00:00:00
2020 2020-08-16 00:00:00

Getting conflicting information from datetime isocalendar

I have a list that gives me the weekday (1-7 as if Monday-Sunday) and the week number for a given year. I need to convert that info to a given date.
So, I wrote a simple script that takes three arguments:
1. year
2. weekday
3. weeknumber
and then finds out the date.
My script basically iterates over all days of the given year, and creates a datetime object where I then extract isocalendar()[1] to compare it to the weeknumber.
I found that if I give the input 2017 7 52 I get two outputs!
In its most basic essence this is what happens:
#!/Library/Frameworks/Python.framework/Versions/3.7/bin/python3
import datetime
def print_dt(year, month, day):
dt = datetime.date(year, month, day)
print("%d-%d-%d (%d) -> week# %d" % (year, month, day, dt.weekday(), dt.isocalendar()[1]))
print_dt(2017, 1, 1)
print_dt(2017, 12, 31)
And the output is the same:
Anibals-iMac:RPEG anibal$ ./findDate-fixed-date.py
2017-1-1 (6) -> week# 52
2017-12-31 (6) -> week# 52
How's that possible? That would mean that week 52 in year 2017 has two day #6, i.e., two different Sundays. This situation is causing problems for my script.
Any idea on how to get around this?
My original problem is that I have events given in YYYYMMDD and I need to group them by week# by year. So, that I can say that X number of events occurred on week#4 of year 2017. With the situation above it doesn't work when it comes down to week 52 since there's more than one solution to a YYYYMMDD.
You are mixing up ISO calendar years and Gregorian calendar years.
The date 2017-1-1 is day 7 of week 52 of year 2016 in the ISO calendar.
The ISO calendar defines the first week of an ISO calendar year to be the one containing the first Thursday of the corresponding Gregorian calendar year. This could be anywhere from 1st to 7th January. As ISO numbers days with Monday = 1 to Sunday = 7, this means that for up to three days around each New Year the Gregorian calendar year and ISO calendar year of a date do not agree.
January 1st 2015 fell on a Thursday, so the Monday, Tuesday and Wednesday before it have ISO calendar year 2015 despite being in December 2014. Similarly, January 7th 2016 fell on a Thursday, and Friday January 1st to Sunday January 3rd 2016 have ISO calendar year 2015 despite being in 2016.
Your script appears to be taking a year in the Gregorian calendar, iterating through all days of this Gregorian calendar year and looking for the day with the matching day-of-week and ISO week-number. What you have found out is that the three values (Gregorian year, ISO week-number ISO day-of-week) do not uniquely identify a date. Your script needs to take into account the fact that ISO calendar years and Gregorian calendar years do not always agree and match on ISO calendar year instead of Gregorian calendar year. One way to do this is to:
include the last three days of the previous Gregorian calendar year and the first three days of the next Gregorian calendar year in the range of dates you search through, and
as well as matching on ISO week number and ISO day of week, ensure that the ISO year matches too. The ISO year is in dt.isocalendar()[0].
Or, as an alternative, you could avoid the ISO calendar system altogether and instead consider something like the following:
def get_week_and_day(year, month, day):
wday_of_jan_1 = datetime.date(year, 1, 1).timetuple().tm_wday
daytuple = datetime.date(year, month, day).timetuple()
wday = daytuple.tm_wday
week = (daytuple.tm_yday - 1 + wday_of_jan_1) // 7 + 1
return (week, wday)
Given a year, month and day this will return the week-of-the-year and day-of-week, with weeks starting on Monday (as per time.struct_time) and week 1 being the week that January 1 falls in. Weeks normally go up to 53 but if December 31 of a leap year falls on a Monday (as it did in 2012) this day will have week number 54.
This works by using the date.timetuple() method to get the weekday and day-of-year of a date, plus also the day-of-the-week of January 1 of that year. In the calculation of week, we:
Subtract 1 from the day-of-the-year of the given date (daytuple.tm_yday), so that January 1 is 0, January 2 is 1 and so on.
Add to this the day-of-the-week of January 1. We do this because the day-of-the-week of January 1 is also the number of days in the first week of the year that are 'lost' to the previous year. For example, if January 1 falls on a Wednesday, wday_of_jan_1 will be 2, the Monday and Tuesday before it will be missing from week 1 and hence the first week will only have 5 days in it.
The calculations so far give us number of days between the given date and the first Monday on or before January 1. We can then divide this by 7 to get the number of whole weeks since this Monday, and finally add 1 so that January 1 is in week 1 rather than week 0.
This approach also avoids looping over an entire year's worth of dates and performing calculations on them.

Is there a simple way to increment a datetime object one month in Python? [duplicate]

This question already has answers here:
How do I calculate the date six months from the current date using the datetime Python module?
(47 answers)
Closed 7 years ago.
So I am trying to find a way to increment a datetime object by one month. However, it seems this is not so simple, according to this question.
I was hoping for something like:
import datetime as dt
now = dt.datetime.now()
later = now + dt.timedelta(months=1)
But that doesn't work. I was also hoping to be able to go to the same day (or the closest alternative) in the next month if possible. For example, a datetime object set at January 1st would increment to Feb 1st whereas a datetime object set at February 28th would increment to March 31st as opposed to March 28th or something.
To be clear, February 28th would (typically) map to March 31st because it is the last day of the month, and thus it should go to the last day of the month for the next month. Otherwise it would be a direct link: the increment should go to the day in the next month with the same numbered day.
Is there a simple way to do this in the current release of Python?
Check out from dateutil.relativedelta import *
for adding a specific amount of time to a date, you can continue to use timedelta for the simple stuff i.e.
import datetime
from dateutil.relativedelta import *
use_date = datetime.datetime.now()
use_date = use_date + datetime.timedelta(minutes=+10)
use_date = use_date + datetime.timedelta(hours=+1)
use_date = use_date + datetime.timedelta(days=+1)
use_date = use_date + datetime.timedelta(weeks=+1)
or you can start using relativedelta
use_date = use_date+relativedelta(months=+1)
use_date = use_date+relativedelta(years=+1)
for the last day of next month:
use_date = use_date+relativedelta(months=+1)
use_date = use_date+relativedelta(day=31)
Right now this will provide 29/02/2016
for the penultimate day of next month:
use_date = use_date+relativedelta(months=+1)
use_date = use_date+relativedelta(day=31)
use_date = use_date+relativedelta(days=-1)
last Friday of the next month:
use_date = use_date+relativedelta(months=+1, day=31, weekday=FR(-1))
2nd Tuesday of next month:
new_date = use_date+relativedelta(months=+1, day=1, weekday=TU(2))
As #mrroot5 points out dateutil's rrule functions can be applied, giving you an extra bang for your buck, if you require date occurences.
for example:
Calculating the last day of the month for 9 months from the last day of last month.
Then, calculate the 2nd Tuesday for each of those months.
from dateutil.relativedelta import *
from dateutil.rrule import *
from datetime import datetime
use_date = datetime(2020,11,21)
#Calculate the last day of last month
use_date = use_date+relativedelta(months=-1)
use_date = use_date+relativedelta(day=31)
#Generate a list of the last day for 9 months from the calculated date
x = list(rrule(freq=MONTHLY, count=9, dtstart=use_date, bymonthday=(-1,)))
print("Last day")
for ld in x:
print(ld)
#Generate a list of the 2nd Tuesday in each of the next 9 months from the calculated date
print("\n2nd Tuesday")
x = list(rrule(freq=MONTHLY, count=9, dtstart=use_date, byweekday=TU(2)))
for tuesday in x:
print(tuesday)
Last day
2020-10-31 00:00:00
2020-11-30 00:00:00
2020-12-31 00:00:00
2021-01-31 00:00:00
2021-02-28 00:00:00
2021-03-31 00:00:00
2021-04-30 00:00:00
2021-05-31 00:00:00
2021-06-30 00:00:00
2nd Tuesday
2020-11-10 00:00:00
2020-12-08 00:00:00
2021-01-12 00:00:00
2021-02-09 00:00:00
2021-03-09 00:00:00
2021-04-13 00:00:00
2021-05-11 00:00:00
2021-06-08 00:00:00
2021-07-13 00:00:00
rrule could be used to find the next date occurring on a particular day.
e.g. the next 1st of January occurring on a Monday (Given today is the 4th November 2021)
from dateutil.relativedelta import *
from dateutil.rrule import *
from datetime import *
year = rrule(YEARLY,dtstart=datetime.now(),bymonth=1,bymonthday=1,byweekday=MO)[0].year
year
2024
or the next 5 x 1st of January's occurring on a Monday
years = rrule(YEARLY,dtstart=datetime.now(),bymonth=1,bymonthday=1,byweekday=MO)[0:5]
for i in years:print(i.year)
...
2024
2029
2035
2046
2052
The first Month next Year that starts on a Monday:
>>> month = rrule(YEARLY,dtstart=datetime.date(2023, 1, 1),bymonthday=1,byweekday=MO)[0]
>>> month.strftime('%Y-%m-%d : %B')
'2023-05-01 : May'
If you need the months that start on a Monday between 2 dates:
months = rrule(YEARLY,dtstart=datetime.date(2025, 1, 1),until=datetime.date(2030, 1, 1),bymonthday=1,byweekday=MO)
>>> for m in months:
... print(m.strftime('%Y-%m-%d : %B'))
...
2025-09-01 : September
2025-12-01 : December
2026-06-01 : June
2027-02-01 : February
2027-03-01 : March
2027-11-01 : November
2028-05-01 : May
2029-01-01 : January
2029-10-01 : October
This is by no means an exhaustive list of what is available.
Documentation is available here: https://dateutil.readthedocs.org/en/latest/
Note: This answer shows how to achieve this using only the datetime and calendar standard library (stdlib) modules - which is what was explicitly asked for. The accepted answer shows how to better achieve this with one of the many dedicated non-stdlib libraries. If you can use non-stdlib libraries, by all means do so for these kinds of date/time manipulations!
How about this?
def add_one_month(orig_date):
# advance year and month by one month
new_year = orig_date.year
new_month = orig_date.month + 1
# note: in datetime.date, months go from 1 to 12
if new_month > 12:
new_year += 1
new_month -= 12
new_day = orig_date.day
# while day is out of range for month, reduce by one
while True:
try:
new_date = datetime.date(new_year, new_month, new_day)
except ValueError as e:
new_day -= 1
else:
break
return new_date
EDIT:
Improved version which:
keeps the time information if given a datetime.datetime object
doesn't use try/catch, instead using calendar.monthrange from the calendar module in the stdlib:
import datetime
import calendar
def add_one_month(orig_date):
# advance year and month by one month
new_year = orig_date.year
new_month = orig_date.month + 1
# note: in datetime.date, months go from 1 to 12
if new_month > 12:
new_year += 1
new_month -= 12
last_day_of_month = calendar.monthrange(new_year, new_month)[1]
new_day = min(orig_date.day, last_day_of_month)
return orig_date.replace(year=new_year, month=new_month, day=new_day)
Question: Is there a simple way to do this in the current release of Python?
Answer: There is no simple (direct) way to do this in the current release of Python.
Reference: Please refer to docs.python.org/2/library/datetime.html, section 8.1.2. timedelta Objects. As we may understand from that, we cannot increment month directly since it is not a uniform time unit.
Plus: If you want first day -> first day and last day -> last day mapping you should handle that separately for different months.
>>> now
datetime.datetime(2016, 1, 28, 18, 26, 12, 980861)
>>> later = now.replace(month=now.month+1)
>>> later
datetime.datetime(2016, 2, 28, 18, 26, 12, 980861)
EDIT: Fails on
y = datetime.date(2016, 1, 31); y.replace(month=2) results in ValueError: day is out of range for month
Ther is no simple way to do it, but you can use your own function like answered below.

Categories

Resources