I'm trying to find out fourth to the last month with a snippet.
Considering, september, fourth to the last month would be june.
I tried to get the required month and year like this
from datetime import date, datetime, timedelta
current_date = datetime.now()
last_4th_month = current_date.month - 3
year = current_date.year
if current_date.month == 3:
last_4th_month = 12
year = current_date.year - 1
if current_date.month == 2:
last_4th_month = 11
year = current_date.year - 1
if current_date.month == 1:
last_4th_month = 10
year = current_date.year - 1
print(last_4th_month, year)
Is there any efficient and better way to do this?
TYPO IN THE QUESTION:
Get fourth to the last month in python
Try this:
from datetime import date
from dateutil.relativedelta import relativedelta
four_months = date.today() + relativedelta(months=-3)
print(four_months.strftime("%B, %Y"))
Output: June, 2021
If 3rd party libraries can be used, dateutil's relativedelta module offers a simple solution:
from datetime import datetime
from dateutil.relativedelta import relativedelta
current_date = datetime.now()
target_date = current_date - relativedelta(months=3)
print(target_date.month, target_date.year)
Outputs 6 2021
You can compute the last_4th_month with the standard library:
from datetime import date
today = date.today()
month = today.month - 3 if today.month - 3 > 0 else 12 - today.month
year = today.year if today.month > month else today.year - 1
last_4th_month = date(year, month, today.day)
# today = date(2021, 9, 6)
>>> last_4th_month.strftime('%B, %Y')
'June, 2021'
# today = date(2021, 2, 18)
>>> last_4th_month.strftime('%B, %Y')
'October, 2020'
I have df with column day_name. I'm trying to get number of week_days present in last month?
I'm trying to get number of week_days present in last month.
For ex: There are 4 Fridays and 5 Thrusdays in April
df
day_name
0 Friday
1 Sunday
2 Thursday
3 Wednesday
4 Monday
As per python for a single day:
import calendar
year = 2020
month = 4
day_to_count = calendar.WEDNESDAY
matrix = calendar.monthcalendar(year,month)
num_days = sum(1 for x in matrix if x[day_to_count] != 0)
How do i use this in dataframe or any suggestions?
expected output
day_name last_months_count
0 Friday 4
1 Sunday 4
2 Thursday 5
3 Wednesday 5
4 Monday 4
For last month:
year, month = 2020, 4
start,end = f'{year}/{month}/1', f'{year}/{month+1}/1'
# we exclude the last day
# which is first day of next month
last_month = pd.date_range(start,end,freq='D')[:-1]
df['last_month_count'] = df['day_name'].map(last_month.day_name().value_counts())
Output:
day_name last_month_count
0 Friday 4
1 Sunday 4
2 Thursday 5
3 Wednesday 5
4 Monday 4
Bonus: to extract last month programatically:
from datetime import datetime
now = datetime.now()
year, month = now.year, now.month
# first month of the year
if month == 1:
year, month = year-1, 12
Here you go:
from datetime import date, timedelta
from calendar import day_name
import pandas as pd
today = date.today()
dt = date(today.year, today.month, 1) - timedelta(days=1)
day_to_count = {}
month = dt.month
while dt.month == month:
key = day_name[dt.weekday()]
day_to_count[key] = day_to_count.get(key, 0) + 1
dt -= timedelta(days = 1)
df = pd.DataFrame({
'day_name': ['Friday', 'Sunday', 'Thursday', 'Wednesday', 'Monday']
})
df['last_months_count'] = df['day_name'].apply(lambda day : day_to_count[day])
print(df)
Output:
day_name last_months_count
0 Friday 4
1 Sunday 4
2 Thursday 5
3 Wednesday 5
4 Monday 4
I'm looking to build code that represents the definition of months in a piece of Australian legislation - the Interpretations Act (1987).
Please note that I am still a relative novice to Python.
The legal definition
The definition reads as follows:
(1) In any Act, month means a period:
(a) starting at the start of any day of one of the calendar months; and.
(b) ending: (i) immediately before the start of the corresponding day of the next calendar month; or.
(ii) if there is no such day--at the end of the next calendar month.
I've been advised that this definition means that if the start of a month begins on 16/07/2019, for the purposes of a) for example, the relevant month does not conclude until 11:59:59:etc:pm on 15/08/2019 - or functionally, 16/08/2019.
For the purpose of b), then, the "end of a month" is defined at similarly 11:59:59:etc:pm on the relevant final day of the month. So if you have two dates - 31/08/2019 and 30/09/2019 - the relevant month does not conclude until 11:59:59:etc:pm on 30/09/2019 - or functionally, 01/10/2019.
I need to output the difference between two dates in months in order to reflect that the legislation I'm coding asks for a difference between two dates specifically in months.
I'm looking to do this with either datetime or datetime64 objects if possible, to avoid converting between variables unnecessarily.
What I've tried so far.
I've used the below code to find the difference between two dates in months, using relativedelta:
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-15', '%Y-%m-%d')
date2 = datetime.strptime('2020-02-05', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
print(r)
My expected output for this is 5 months, as there are five complete months and then a fraction of a month that isn't completed by date2. This returns the expected result, and replicates the functionality of a) in the legislation.
However, when I try to replicate b) with the below code:
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-31', '%Y-%m-%d')
date2 = datetime.strptime('2019-11-30', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
print(r)
This returns the result of 4 months. Because 2019-11-30 is not the end of the relevant calendar month, this is incorrect - I should be getting a result of 3 months for this code, as the month is not completed until 11:59:59:etc.
Expected results
Below are four test cases that I've used to test the results of this code.
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-25', '%Y-%m-%d')
date2 = datetime.strptime('2019-09-10', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 0
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-25', '%Y-%m-%d')
date2 = datetime.strptime('2019-09-25', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 1
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-31', '%Y-%m-%d')
date2 = datetime.strptime('2019-11-30', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 3
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-31', '%Y-%m-%d')
date2 = datetime.strptime('2019-12-01', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 4
EDIT: I've written the inputs for the second two test cases, and after reviewing Alain T.'s response, have revised to the below.
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-01', '%Y-%m-%d')
date2 = datetime.strptime('2019-11-30', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 3
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-01', '%Y-%m-%d')
date2 = datetime.strptime('2019-12-01', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 4
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2019-08-31', '%Y-%m-%d')
date2 = datetime.strptime('2019-12-01', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
r.months = 3
This can be calculated without converting to date types except for the edge case where dates are the last day of the month (where they actually correspond to day zero of the next month).
from datetime import date
def isLastDay(y,m,d):
return date.fromordinal(date(y,m,d).toordinal()+1).month != m
def legalMonthDif(date1,date2):
y1,m1,d1 = map(int,date1.split("-"))
y2,m2,d2 = map(int,date2.split("-"))
if isLastDay(y1,m1,d1): m1,d1 = m1+1,0
if isLastDay(y2,m2,d2): m2,d2 = m2+1,0
return y2*12+m2 -y1*12-m1 -(d2<d1)
output:
legalMonthDif('2019-08-15','2020-02-05') #5
legalMonthDif('2019-08-31','2019-11-30') #3
legalMonthDif('2019-08-25','2019-09-10') #0
legalMonthDif('2019-08-25','2019-09-25') #1
legalMonthDif('2019-08-31','2019-11-30') #3
legalMonthDif('2019-08-01','2019-12-01') #4
legalMonthDif('2019-08-31','2019-12-01') #3
legalMonthDif('2019-08-15','2019-12-01') #3
You could also do it completely without the datetime library by implementing a daysOfMonth function to compute the number of days in any month:
def daysOfMonth(y,m):
return 30+(m+m//8)%2-(m==2)*(2-(y%4==0 and not y%100==0 or y%400==0))
def legalMonthDif(date1,date2):
y1,m1,d1 = map(int,date1.split("-"))
y2,m2,d2 = map(int,date2.split("-"))
if daysOfMonth(y1,m1) == d1: m1,d1 = m1+1,0
if daysOfMonth(y2,m2) == d2: m2,d2 = m2+1,0
return y2*12+m2 -y1*12-m1 -(d2<d1)
dates = [('2019-07-16','2019-08-15'),('2019-08-31','2019-09-30'),
('2019-08-15','2020-02-05'),('2019-08-31','2019-11-30'),
('2019-08-25','2019-09-10'),('2019-08-25','2019-09-25'),
('2019-08-31','2019-12-01'),('2019-08-15' , '2019-12-01'),
('2019-08-01', '2019-11-30'),('2019-08-01', '2019-12-01')]
Using Pandas date-time functionality. This relies on the fact that adding months to a timestamp will truncate to the end of the month if the resulting date doesn't exist - providing a means to test for the (b)(ii) part of the spec.
import pandas as pd
def f(a,b):
earlier,later = sorted((a,b))
rel_months = later.month - earlier.month
delta_months = rel_months + (later.year - earlier.year) * 12
period_end = earlier + pd.DateOffset(months=delta_months)
# sentinals for implementing logic of (b)(ii) of the definition
period_end_isEOM = period_end + pd.tseries.offsets.MonthEnd(0)
later_isEOM = later == later + pd.tseries.offsets.MonthEnd(0)
next_month = period_end + pd.tseries.offsets.MonthBegin(0)
# begin with the delta - period_end == later - then adjust
months = delta_months
# this is straightforward
if period_end > later:
months -= 1
# did period_end get truncated to the end of a month
if period_end_isEOM and (period_end.day < earlier.day):
# actual end of period would be beginning of next month
if later < next_month: # probably also means later_isEOM or later == period_end
months -= 1
return months
for a,b in dates:
a, b = map(pd.Timestamp, (a,b))
c = f(a,b)
print(f'{a.date()} - {b.date()} --> {c}')
>>>
2019-07-16 - 2019-08-15 --> 0
2019-08-31 - 2019-09-30 --> 0
2019-08-15 - 2020-02-05 --> 5
2019-08-31 - 2019-11-30 --> 2
2019-08-25 - 2019-09-10 --> 0
2019-08-25 - 2019-09-25 --> 1
2019-08-31 - 2019-12-01 --> 3
2019-08-15 - 2019-12-01 --> 3
2019-08-01 - 2019-11-30 --> 3
2019-08-01 - 2019-12-01 --> 4
>>>
pd.TimeStamp is an instance of datetime.datetime
This appears to work - only the OP can judge - but I can't help thinking that there is some builtin functionality I'm still not utilizing. Should be able to subclass pandas.DateOffset and customize it to make the calcs easier.
Solutions using a subclass of Pandas.DateOffset.
from pandas import DateOffset, Timestamp
from pandas.tseries.offsets import MonthBegin
class LegislativeMonth(DateOffset):
def __init__(self, n=1, normalize=False, months=1):
# restricted to months
kwds = {'months':months}
super().__init__(n=1, normalize=False, **kwds)
def apply(self,other):
end_date = super().apply(other)
if end_date.day < other.day:
# truncated to month end
end_date = end_date + MonthBegin(1)
return end_date
for a,b in dates:
earlier,later = sorted(map(Timestamp, (a,b)))
delta_months = later.month - earlier.month
delta_months += (later.year - earlier.year) * 12
end_of_period = earlier + LegislativeMonth(months=delta_months)
if end_of_period > later:
delta_months -= 1
print(f'{earlier.date()} - {later.date()} --> {delta_months}')
# another
one_month = LegislativeMonth(months=1)
for a,b in dates:
earlier,later = sorted(map(Timestamp, (a,b)))
end_period = earlier
months = 0
while later >= end_period + one_month:
months += 1
end_period += one_month
print(f'{earlier.date()} - {later.date()} --> {months}')
Finally it looks like relativedelta will do what you want if you ensure that it is called with the earlier date as the first item - (earlier,later)
from datetime import datetime
from dateutil.relativedelta import relativedelta
for a,b in dates:
## earlier,later = sorted(map(Timestamp, (a,b)))
earlier,later = sorted((datetime.strptime(a, '%Y-%m-%d'),
datetime.strptime(b, '%Y-%m-%d')))
rd = relativedelta(earlier,later)
print(f'{earlier.date()} - {later.date()} --> {abs(rd.months)}')
Using the dates at the top of this post all print the following:
2019-07-16 - 2019-08-15 --> 0
2019-08-31 - 2019-09-30 --> 0
2019-08-15 - 2020-02-05 --> 5
2019-08-31 - 2019-11-30 --> 2
2019-08-25 - 2019-09-10 --> 0
2019-08-25 - 2019-09-25 --> 1
2019-08-31 - 2019-12-01 --> 3
2019-08-15 - 2019-12-01 --> 3
2019-08-01 - 2019-11-30 --> 3
2019-08-01 - 2019-12-01 --> 4
I ended up writing the below functions which capture the intended functionality of this legislation:
def find_corresponding_date(start_date):
day = start_date.day
month = start_date.month
year = start_date.year
next_month = month + 1
next_year = year
if month == 12:
next_month = 1
next_year = year + 1
try:
new_date = py_datetime(year=next_year, month=next_month, day=day)
except ValueError:
next_month = next_month + 1
if next_month == 13:
next_month = 1
next_year = next_year + 1
new_date = py_datetime(year=next_year, month=next_month, day=1)
return new_date
else:
return new_date
def toPyDateTime(numpyDate):
return py_datetime.strptime(str(numpyDate), "%Y-%m-%d")
def count_months(sdate, edate):
start_date = toPyDateTime(sdate)
end_date = toPyDateTime(edate)
count = 0
corres_date = start_date
while(True):
corres_date = find_corresponding_date(corres_date)
if(corres_date > end_date):
return count
break
else:
count = count + 1
How do I check if the current time is between Sunday 5:30 PM and Friday 5:30 PM?
I can find the day and time ranges separately but I think the time has to be combined with the day.
import datetime
import time
now = datetime.datetime.now()
if 0 <= now.weekday() <= 4:
print ("It's a weekday!")
if time(17, 30) <= now.time() <= time(17, 30):
print "and it's in range"
You can check the following 3 conditions:
The day is not Saturday
Either the day is not Friday, or the time is before 17:30
Either the day is not Sunday, or the time is after 17:30
In code, this is equivalent to:
from datetime import datetime, time
now = datetime.now()
if (now.weekday != 5
and (now.weekday != 4 or now.time() <= time(17, 30))
and (now.weekday != 6 or now.time() >= time(17, 30))):
print("In range!")
You can simply get the datetime of the most recent Sunday at 5:30pm, and then check if it's within exactly 5 days:
import datetime
now = datetime.datetime.now()
last_sunday = next((now - i * datetime.timedelta(days=1)) for i in range(7) if (now - i * datetime.timedelta(days=1)).weekday() == 6)
last_sunday_at_530 = datetime.datetime(year=last_sunday.year, month=last_sunday.month, day=last_sunday.day, hour=17, minute=30)
if (now - last_sunday_at_530) < datetime.timedelta(days=5):
print("now is between Sunday at 5:30pm and Friday at 5:30pm")
else:
print("Now is after 5:30pm on Friday but before Sunday at 5:30pm
If you want to check the reverse (after Friday but before Sunday) then you can simply start at last_friday and count forwards by only two days.
Which time of day is within the range depends on the day:
On Sunday, times after 5:30 PM are in.
From Monday to Thursday, the time does not matter.
On Friday, times before 5:30 are in.
This leads to the following code:
import datetime
now = datetime.datetime.now()
d = now.weekday()
t = now.time()
if (
d == 6 and t >= datetime.time(17, 30) # Sunday
or 0 <= d <= 3 # Monday–Thursday
or d == 4 and t <= datetime.time(17, 30) # Friday
):
print("In range")
I am trying to get the week ending date for a list of dates. I found the below code and it works for a traditional Monday thru Sunday week but I need a Sunday thru Saturday week (Sunday being the first day of the week).
from datetime import datetime, timedelta
date_str = '2019-06-16'
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
start_of_week = date_obj - timedelta(days=date_obj.weekday())
end_of_week = start_of_week + timedelta(days=6)
print(start_of_week)
print(end_of_week)
The above code returns:
2019-06-10 00:00:00
2019-06-16 00:00:00
and I need it to return this:
2019-06-16 00:00:00
2019-06-22 00:00:00
So weekday returns 0 for Monday and 6 for Sunday. To change that to 0 for Sunday and 6 for Saturday do
start_of_week = date_obj - timedelta((date_obj.weekday() + 1) % 7)
I think in python, Sunday is the start day of a week, LOL. Code below may help you.
from datetime import datetime, timedelta
# this may be the func you need.
def date_start_end(date):
"""
calc the start and end date for input date. Sunday is the start day for a week.
:param date: any date
:return: st: the start date(Sunday) for this week
:return: end: the end date(Saturday) for this week
"""
weekDate = int(date.strftime('%w'))
st = date + timedelta(-weekDate)
end = date + timedelta(6 - weekDate)
return st, end
date = datetime(2019, 6, 16)
st, end = date_start_end(date)
print(st)
print(end)
>>>2019-06-16 00:00:00
>>>2019-06-22 00:00:00
Adding in my two cents. Datetime also has an isoweekday method that has Monday at 1 and Sunday at 7. Similar to #Deepstop's answer but a bit shorter:
start_of_week = date_sample - timedelta(days=date_sample.isoweekday()%7)