I want to find the last friday date in each quater for the given year. For example, the output as follows
last friday in March
last friday in June
last friday in september
last friday in december
How can I find this intelligently based on given year as an input
I'm assuming you don't want the output to be literally
last friday in March
last friday in June
last friday in september
last friday in december
and you just didn't feel like looking up those dates for an example year to include in your question.
Since we know that quarters always end in those months, we can create the last dates of those months. Then, datetime.date.weekday() tells us which day of the week it is. Friday is 4, so we just need to find out how many days we need to go back to achieve this. Then, use datetime.timedelta can subtract that many days, and we should be good to go:
import datetime
def last_fridays_of_quarter(year):
last_days = [datetime.date(year, 3, 31), datetime.date(year, 6, 30), datetime.date(year, 9, 30), datetime.date(year, 12, 31)]
for day in last_days:
days_to_sub = (day.weekday() - 4) % 7
last_friday = day - datetime.timedelta(days=days_to_sub)
print(last_friday)
Testing this for 2022:
>>> last_fridays_of_quarter(2022)
2022-03-25
2022-06-24
2022-09-30
2022-12-30
Here's one way to solve it. You store last days of each quarter in a given year in an array. Then you iterate over said quarter ending dates, and calculate the offset that you would need to subtract in order to get to the previous friday.
from datetime import datetime, timedelta
year = 2022
quarter_ending_dates = [
datetime(year, 3, 31),
datetime(year, 6, 30),
datetime(year, 9, 30),
datetime(year, 12, 31)
]
for ending_date in quarter_ending_dates:
offset = (ending_date.weekday() - 4) % 7
last_friday = ending_date - timedelta(days=offset)
print(last_friday)
try:
import pandas as pd
def fridays(year):
df = pd.date_range(start=str(year), end=str(year+1),
freq='W-FRI').strftime('%m/%d/%Y')\
.to_frame().reset_index(drop = True)
df.columns = ['date']
df.index = pd.to_datetime(df['date']).dt.to_period('Q')
df = df.groupby(df.index).last()
df.index.name = 'quarter'
return df
fridays(2022)
output is:
quarter
date
2022Q1
03/25/2022
2022Q2
06/24/2022
2022Q3
09/30/2022
2022Q4
12/30/2022
Related
I am trying to count the number of days in a range of dates by month. So let's say a range of dates occurs between 2 months since the beginning and ending dates are in 2 different months. I want the output to show that x amount of days in the range fall in one month and x amount of days fall in the next month.
So far my code only outputs each day in the range from 10 days after veterans day (my start date) to 20 days after veterans day (end date):
import datetime
Veterans = datetime.datetime(2019, 11, 12)
print(Veterans)
number_of_days = 10
ten_days = datetime.timedelta(days=10)
vetplus10 = Veterans + ten_days
date_list = [(vetplus10 + datetime.timedelta(days=day)).isoformat() for day in range(number_of_days)]
print(date_list)
['2019-11-22T00:00:00', '2019-11-23T00:00:00', '2019-11-24T00:00:00',
'2019-11-25T00:00:00', '2019-11-26T00:00:00', '2019-11-27T00:00:00',
'2019-11-28T00:00:00', '2019-11-29T00:00:00', '2019-11-30T00:00:00',
'2019-12-01T00:00:00']
The idea here would be for python to tally up all the days in November (9) and all the days in December (1).
Thank you in advance!
You can try using pandas to create a date range, convert it to a month and get the value counts.
import pandas as pd
pd.date_range(start='2019-11-22', periods=10, freq='D').to_period('M').value_counts()
2019-11 9
2019-12 1
I was able to get similar output without using an additional library:
import datetime
Veterans = datetime.datetime(2019, 11, 12)
print(Veterans)
number_of_days = 10
ten_days = datetime.timedelta(days=10)
vetplus10 = Veterans + ten_days
date_list = [(vetplus10 + datetime.timedelta(days=day)) for day in range(number_of_days)]
day_counts = {}
for day in date_list:
day_counts[f"{day.year}-{day.month}"] = day_counts.get(f"{day.year}-{day.month}", 0) + 1
print(day_counts)
2019-11-12 00:00:00
{'2019-11': 9, '2019-12': 1}
Essentially, I simply iterate over the datetime objects in your original list, and build a dictionary for each year-month that it encounters.
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)
I have a pandas dataframe with one if its columns in datetime format (year-month-day). Is there any way to create a new column that says weekend or weekday, and a column that says what season it is? Even better (but this one seems much more complex so not totally necessary) would also be whether it's a federal holiday (United States) or not.
For example, an instance at date 2019-10-23 is a Wednesday, so I'd want to create a column called day_type that fills 'weekday', and a column called season that fills 'fall'.
It's easy with strftime:
import pandas as pd
df = pd.DataFrame({'string_date': ['2019-10-23', '2019-10-24', '2019-10-23']})
df['date'] = pd.to_datetime(df['string_date'], format='%Y-%m-%d', errors='ignore')
df['day_of_week'] = df['date'].dt.strftime('%A')
Result1 :
string_date date day_of_week
0 2019-10-23 2019-10-23 Wednesday
1 2019-10-24 2019-10-24 Thursday
2 2019-10-23 2019-10-23 Wednesday
For adding the season you can use this formula :
Link
df['season'] = (df['date'].dt.month%12 + 3)//3
Result2:
string_date date day_of_week season
0 2019-10-23 2019-10-23 Wednesday 4
1 2019-10-24 2019-10-24 Thursday 4
2 2019-10-23 2019-10-23 Wednesday 4
Strftime reference:
http://strftime.org/
I wrote a function that takes in a string argument in the form 'yyyy-mm-dd' and returns a tuple of season and day-of-week. Please modify the function accordingly to your needs.
from datetime import date, datetime
def date_week(date_str):
"""
this method returns season and day of week tuple from str
arg in the format 'yyyy-mm-dd'
"""
datetime_obj = datetime.strptime(date_str, '%Y-%m-%d')
weekdays = {0:'monday',
1:'tuesday',
2:'wednesday',
3:'thursday',
4:'friday',
5:'saturday',
6:'sunday'}
day_of_week = weekdays[datetime_obj.weekday()] # returns day of week
Y = 2000 # dummy leap year to allow input X-02-29 (leap day)
seasons = [('winter', (date(Y, 1, 1), date(Y, 3, 20))),
('spring', (date(Y, 3, 21), date(Y, 6, 20))),
('summer', (date(Y, 6, 21), date(Y, 9, 22))),
('autumn', (date(Y, 9, 23), date(Y, 12, 20))),
('winter', (date(Y, 12, 21), date(Y, 12, 31)))]
if isinstance(datetime_obj, datetime):
datetime_obj = datetime_obj.date()
datetime_obj = datetime_obj.replace(year=Y)
season = next(season for season, (start, end) in seasons
if start <= datetime_obj <= end) # returns season
return day_of_week, season
If we call date_week("2019-10-23"), it will return ('wednesday', 'autumn')
I want to create a event that falls on Sunday of every first week of months.
This is what I am doing for a particular week number which is first week of june to get date:
from datetime import datetime
myDate = "2017 22 0"
# here `2017` is year, `22` is week number and `0` =sunday is week day
datetime.strptime(myDate, "%Y %W %w")
#output...
datetime.datetime(2017, 6, 4, 0, 0)
So I need a list of all week numbers that are first week of month so I can loop on it and get desire dates.
Adding More information
I want a method which return list of week numbers which are first week of month where first week means week has first sunday.
def get_week_number(year=2017, day=0):
#day 0 means sunday
...
...
return [1, 5, 9, 13, 18, 22, ...]
(Edited, because of error in weekday numbering)
>>> import datetime
>>> june1 = datetime.datetime(2017,6,1)
>>> june1
datetime.datetime(2017, 6, 1, 0, 0)
>>> june1_weekday = june1.weekday()
>>> if june1_weekday < 6: # 6 indicates Sunday
first_sunday_in_june = june1 + datetime.timedelta(days=6-june1_weekday)
else:
first_sunday_in_june = june1
>>> print(first_sunday_in_june)
2017-06-04 00:00:00
Assuming you want ISO weeknumbers, you can then use the isocalendar() method. This gives a tuple (year, weeknumber, weekday). This uses the convention that weeks start with Monday, and the first week of the year is the first week with at least four days in the year (or in other words, the week with the first Thursday).
>>> first_sunday_in_june.isocalendar()
(2017, 22, 7)
If you have another convention for first-day-of-the-week or first-week-of-the-year, you will have to brew your own function to get the week number.
Use the above method in a loop over the months, and you can create the desired list with week numbers.
I've done a loop through the months, then got the first Sunday (starting at month's 1st and moving to the next day until a Sunday is found), then got the week-of-year of the date found:
from datetime import datetime
from datetime import date
# dayofweek: Sunday=0, Monday=1 and so on
def get_week_number(year=2017, dayofweek=0):
weeks = []
for month in range(1, 13):
# get first Sunday of month
d = date(year, month, 1)
while(d.weekday() != dayofweek):
d = d.replace(day=d.day + 1)
# isocalendar()[1] is the week-of-year field
weeks.append(d.isocalendar()[1])
return weeks
print(get_week_number(2017, 0))
The result, though, is different from what you expect:
[1, 6, 10, 14, 18, 23, 27, 32, 36, 40, 45, 49]
I also tried with weeks.append(int(d.strftime("%W"))) but it gives the same results - I'm using Python 3.5.2 and a week is defined as:
The ISO year consists of 52 or 53 full weeks, and where a week starts on a Monday and ends on a Sunday. The first week of an ISO year is the first (Gregorian) calendar week of a year containing a Thursday. This is called week number 1, and the ISO year of that Thursday is the same as its Gregorian year.
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.