Capturing Holiday when computing 3 business days prior from given date - python

Lets say we have a date as 2019-11-19 which is Tuesday. Now I want to get the 3 business days back from Tuesday i.e. I want to get 2019-11-15 as 16th and 17th are Saturday and Sunday respectively. To achieve this I have the following code:
dt_tue = datetime.strptime('2019-11-19','%Y-%m-%d')
bd-3 = dt_tue - timedelta(days=3) #<--- 3 business days prior
for i in range(bd_3.day,dt_tue.day+1):
dt_in = datetime(dt_tue.year,dt_tue.month,i)
if dt_in.weekday() > 5:
bd_3 = dt_tue - timedelta(4)
The above code generates bd_3 as 15th Nov 2019 which is Friday, and this is correct.
I want to handle a holiday (as provided in dataframe) in the above code. So for example, if dt_in falls on any holiday (including bd_3 and the dt_tue), then the bd_3 should be 14th Nov. Except that Holiday falls on Saturday or Sunday then bd_3 should be 15th Nov only.
Can any body please throw some light? Assume holiday Dataframe looks like below:
Date Holiday_name Day
January 1, 2019 New Year's Day Tuesday
January 21, 2019 Martin Luther King Day Monday
February 18, 2019 Presidents' Day* Monday
May 27, 2019 Memorial Day Monday

Since you're busy looping over all the days anyway, I suggest just doing a simple back-up and check each day as you go, something like:
dt_tue = datetime.strptime('2019-11-19','%Y-%m-%d')
current_day = dt_tue
days_before = 0
while days_before < 2:
# Skip weekends and holiday (without counting as a business day)
while current_day.weekday() >= 5 or current_day in df['Date']:
current_day -= timedelta(days=1)
# Step back a business day
current_day -= timedelta(days=1)
days_before += 1
bd_3 = current_day
You may need to tweak that a bit as I'm not 100% sure how your holidays dataframe is formatted.

Related

Calculating calendar weeks from fiscal weeks

So I am really new to this and struggling with something, which I feel should be quite simple.
I have a Pandas Dataframe containing two columns: Fiscal Week (str) and Amount sold (int).
Fiscal Week
Amount sold
0
2019031
24
1
2019041
47
2
2019221
34
3
2019231
46
4
2019241
35
My problem is the fiscal week column. It contains strings which describe the fiscal year and week . The fiscal year for this purpose starts on October 1st and ends on September 30th. So basically, 2019031 is the Monday (the 1 at the end) of the third week of October 2019. And 2019221 would be the 2nd week of March 2020.
The issue is that I want to turn this data into timeseries later. But I can't do that with the data in string format - I need it to be in date time format.
I actually added the 1s at the end of all these strings using
df['Fiscal Week']= df['Fiscal Week'].map('{}1'.format)
so that I can then turn it into a proper date:
df['Fiscal Week'] = pd.to_datetime(df['Fiscal Week'], format="%Y%W%w")
as I couldn't figure out how to do it with just the weeks and no day defined.
This, of course, returns the following:
Fiscal Week
Amount sold
0
2019-01-21
24
1
2019-01-28
47
2
2019-06-03
34
3
2019-06-10
46
4
2019-06-17
35
As expected, this is clearly not what I need, as according to the definition of the fiscal year week 1 is not January at all but rather October.
Is there some simple solution to get the dates to what they are actually supposed to be?
Ideally I would like the final format to be e.g. 2019-03 for the first entry. So basically exactly like the string but in some kind of date format, that I can then work with later on. Alternatively, calendar weeks would also be fine.
Assuming you have a data frame with fiscal dates of the form 'YYYYWW' where YYY = the calendar year of the start of the fiscal year and ww = the number of weeks into the year, you can convert to calendar dates as follows:
def getCalendarDate(fy_date: str):
f_year = fy_date[0:4]
f_week = fy_date[4:]
fys = pd.to_datetime(f'{f_year}/10/01', format= '%Y/%m/%d')
return fys + pd.to_timedelta(int(f_week), "W")
You can then use this function to create the column of calendar dates as follows:
df['Calendar Date]'] = list(getCalendarDate(x) for x in df['Fiscal Week'].to_list())

Update Financial Week based on Multiple Parameters using Pandas/Python

I have a table that is updated manually every week using excel. I would like to automate this process using python/pandas. I want to update report week number(This number indicates how many times we have reported on that month so far for a given quarter) based on financial week and month. Obviously we are now in September but I will show you the first week to give you an idea of how its updated. The first week for 2021 would start on 01/04/2021 (First Monday of the year) and end on 12/27/2021 (Last Monday of the year).
This script is to be run weekly so next time it is run 01/04/201 --> 01/11/2021, 1 week is added & the "Report Week" should update by 1 too, unless report week is greater than 13. If "Report Week" is greater than 13 than we stop updating that month and add the next month. So in this case we drop December and start reporting March and its report Week becomes 1, as this is the first month we are reporting on it.
Month
Finance Week
Report Week
December
01/04/2021
13
January
01/04/2021
8
February
01/04/2021
4
January
01/11/2021
9
February
01/11/2021
5
March
01/11/2021
1
When January hits Report week 13 we will stop updating that month and move onto April and give it a value of 1 for tis Report Week and so on for every month.
I am not sure what is the best way to go about this. I read here https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html that one should not update when iterating a df so I'm not sure what to do.

Python iso calendar 1st Jan

I'm using this function to compute the week number from a date ( it counts the weeks starting from 0 ):
time.strftime("%U", datetime(2017,1,1).timetuple())
it is returning 1. If you try another year, I.e:
time.strftime("%U", datetime(2018,1,1).timetuple())
it return 0. Fine, it is the 1st week for 2018 year.
It is crystal clear that the 2017 begins with Sunday and this day actually belongs to the week before: December 26, 2016 January 1, 2017
But the last week of 2016 is number 52, so why the function is returning 1 instead 51?
%U can return a value ranging from 0 to 53, where each week is defined as starting on a Sunday The values 1 though 52 make sense, as you typically think of a year as containing 52 weeks. So let's look at the situations where a day occurs in week 0 or week 53.
January 1, 2017 was on a Sunday, so as expected, it occurs during Week 1:
>>> datetime(2017, 1, 1).strftime("%U")
'01'
December 24, 2017 is the Sunday that starts Week 52
>>> datetime(2017, 12, 24).strftime("%U")
'52'
But what, then, to make of December 31? Clearly, there is slightly more than 52 weeks in a year (since 7 * 52 == 364), so we treat the week that "mostly" bleeds into the following year as Week 53.
>>> datetime(2017, 12, 31).strftime("%U")
'53'
This week coincides with the week 0 from the perspective of 2018:
>>> datetime(2018, 1, 1).strftime("%U")
'00'
since the first Sunday of 2018 is January 7:
>>> datetime(2018, 1, 7).strftime("%U")
'01'
So Week 53 of 2017 and Week 00 of 2018 refer to the same same span of days, December 31, 2017 through January 6, 2018. We just use different numbers to refer to it, depending on whether we are asking about it as a week containing a day from 2017 or as a week continuing days from 2018.
This also implies that some years (like 2017) don't have a Week 0, and other years (like 2016) do not have a Week 53.
>>> datetime(2016, 12, 31).strftime("%U")
'52'
No year has both Week 0 and Week 53. But in all years, Weeks 1 through 52 consist of 7 days in the given year. Further, if a year has a Week 53, the following year will have a Week 0.
And as a final bit of trivia, Week 53 will usually start on December 31. The occasional exception is a leap year that begins on a Sunday, where Week 53 starts on December 30. The last one was 2012; the next such year is 2040.

Get week start date from week number

This question is related to Get date from week number, and is possibly a duplicate of the latter, however, I think what is suggested in the accepted answer to that question does not really work.
In [6]: datetime.datetime.strptime('2019-18-1', "%Y-%W-%w")
Out[6]: datetime.datetime(2019, 5, 6, 0, 0)
Notice how it returns Monday 2019-5-6. However, according to the calendar (I use http://whatweekisit.org for reference), 2019-5-6 the Monday of week 19.
Similarly, the example provided in the original question:
In [7]: datetime.datetime.strptime('2013-26-1', "%Y-%W-%w")
Out[7]: datetime.datetime(2013, 7, 1, 0, 0)
According to http://whatweekisit.org/calendar-2013.html 2013-7-1 is the Monday of week 27.
Also
In [8]: datetime.datetime.strptime('2019-18-1', "%Y-%W-%w").isocalendar()[1]
Out[8]: 19
Notice how I give week 18 to strptime, and get week 19 back from isocalendar.
I am completely lost and would very much appreciate if someone could explain what is going on here. My original goal though is to get week start date from week number.
Based off of my testing, datetime does not consider the first week of 2019 (i.e. Jan 1-Jan 6) as week 1 because it isn't a full week; December 31st, 2018 is part of the week but is not in 2019. I suppose you'll have to accomodate for that by checking the output of datetime.datetime.strptime('year-1-1', "%Y-%W-%w") == datetime.datetime.strptime('year-0-1', "%Y-%W-%w"). If false, subtract 1.
2018 is an example of a year where datetime does return the same value as isocalendar because the first Monday of the year is Jan 1.
From the isocalendar docs:
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.
On the other hand, strptime starts from the first full week, in fact, since 2019 starts from Tuesday, they start from different weeks:
import datetime as dt
strp_first = dt.datetime.strptime('2019-1-1', "%Y-%W-%w")
>>> print(strp_first)
2019-01-07 00:00:00
>>> print(strp_first.isocalendar()[1])
2
While in 2021, which starts from Friday:
strp_first = dt.datetime.strptime('2021-1-1', "%Y-%W-%w")
>>> print(strp_first)
2021-01-04 00:00:00
>>> print(strp_first.isocalendar()[1])
1

Django filter if weekend between range

I have the following models:
class Destination_Deal(models.Model):
name = models.CharField(_("Nombre"),max_length=200)
duration = models.IntegerField(_(u"Días"))
class Departure_Date(models.Model):
date_from= models.DateField(_('Desde'))
date_to= models.DateField(_('Hasta'))
destination_deal = models.ForeignKey(Destination_Deal,verbose_name = _("Oferta de Destino"))
I would like to filter Destination Deals that are suitable to travel in a weekend. That means:
Departure Day = Friday or Saturday
Return Day = Sunday. So duration must be 3 or 2 if departure day is Friday or Saturday.
Example
Destination_Deal
id name duration
1 Deal1 3
2 Deal2 5
3 Deal3 2
4 Deal4 7
Departure_Date
id date_from date_to destination_deal_id
1 2012-11-05 2012-11-15 1
2 2012-11-01 2012-12-16 2
3 2013-01-21 2013-01-27 3
4 2013-01-14 2013-01-18 3
5 2013-01-04 2013-01-11 4
Desired Result
ID1: 2012-11-09 was Friday and the deal's duration is 3. So in this case, Friday, Saturday and Sunday conform a valid weekend.
ID3: 2013-01-26 is Saturday and the deal's duration is 2. Also is valid.
--Edit--
Ok, sorry if I was not clear. I need to filter the Destination Deals based on the above weekend rule. I was thinking to do it by getting the date_from from the model (DateField) to python (datetime), iterate it until date_to and use weekday() function to check if it is a Friday or Saturday. I am aware of django weekday function but it will only work on a specific date (no range) so I would like to know if there is a simpler approach for this scenario.
where = '(deparure_date__date_from - deparure_date__date_to) > 4 || \
DAYOFWEEK(deparure_date__date_from) IN (1, 6, 7) || \
DAYOFWEEK(deparure_date__date_to) IN (1, 6, 7)'
Destination_Deal.objects.filter(duration__in=[1, 2, 3]) \
.extra(where=[where])
If duration in 1, 2, 3
And If difference of date_from and date_to > 4 then definitely there is going to be either Friday, or Saturday or Sunday in between.
or check if date_from is Friday, or Saturday or Sunday
or date_to is Friday, or Saturday or Sunday

Categories