python days until specified date without date library - python

MONTHLEN = [ 0, # No month zero
31, # 1. January
28, # 2. February (ignoring leap years)
31, # 3. March
30, # 4. April
31, # 5. May
30, # 6. June
31, # 7. July
31, # 8. August
30, # 9. September
31, #10. October
30, #11. November
31, #12. December
]
def count_normal_year(year, start_month, start_date, end_month, end_date):
day_count=0
if start_month == end_month and start_date==end_date:
print(0)
else:
day_count+=(MONTHLEN[start_month]-start_date)
next_start_month_index=start_month + 1
for months in range(MONTHLEN[next_start_month_index, MONTHLEN[end_month]):
day_count+=MONTHLEN[months]
day_count +=end_date
print(day_count)
I'm not allowed to use the date library.
my idea for this loop is to subtract the days left in the starting month, and then loop through MONTHLEN starting at MONTHLEN index of the next starting month up until the end month.
IE 1 2 12 1
it would give 29 then start February first and add the total days of all months up until December (but not including December) then add the end date
so 28 +31 +30+31+30+31+31+30+31+30 for adding the total days in the month to get me to December then add the end date of 1
I'm not sure the correct syntax to code my idea

Related

Adding days to any date even a leap year

Adding days to any date and even February and leap year too if any.
Here is the date i have : 2020-07-08T07:03:08Z
I need to add days to the above date with some if else conditions.
if(month):
# i need to increment 30/31 days as per the month have
next_upcoming_date = 2020-08-07T07:03:08Z
if(year):
# i need to increment 365/366(when leap year) as per the year
next_upcoming_date = 2021-07-07T07:03:08Z
Example: if 31st
If the subscription terms are $25.99 USD a month and the subscriber signs up on Thursday, July 31. The subscriber is billed as follows:
Thursday, July 31 = $25.99 USD
Saturday, August 31 = $25.99 USD
Wednesday, October 1= $25.99 USD
Saturday, November 1= $25.99 USD
and so on
Example: if 30th
If the subscription terms are $25.99 USD a month and the subscriber signs up on Tuesday, December 30, the subscriber is billed as follows:
Tuesday, December 30 = $25.99 USD
Friday, January 30 = $25.99 USD
Sunday, March 1= $25.99 USD
Wednesday, April 1= $25.99USD
and so on
please Suggest me how can i achieve this.
the dateutil package offers a convenient way to do this:
from datetime import datetime
from dateutil.relativedelta import relativedelta
# convert string to datetime object:
s = '2020-07-08T07:03:08Z'
dt = datetime.fromisoformat(s.replace('Z', '+00:00'))
# datetime.datetime(2020, 7, 8, 7, 3, 8, tzinfo=datetime.timezone.utc)
# add a month:
dt_plus_month = dt + relativedelta(months=1)
# datetime.datetime(2020, 8, 8, 7, 3, 8, tzinfo=datetime.timezone.utc)
dt_plus_year = dt + relativedelta(years=1)
# datetime.datetime(2021, 7, 8, 7, 3, 8, tzinfo=datetime.timezone.utc)
for the year you could do
from re import split
a="2020-08-07T07:03:08Z"
parsed = split("-", a)
if (parsed[0] % 4) == 0:
print("Leap Year!")
else:
print("Not Leap Year ):.")
maybe a bit complicated but it's pure python
for the month you could do:
from re import split
a="2020-08-07T07:03:08Z"
parsed = split("-", a)
if parsed[1] in [01, 03, 05, 07, 08, 10, 12]:
print("31 month!")
else:
print("30 month ): .")
here's a suggestion how to get the desired output, after OP's update of the question. It is significantly different from the original version, so I decided to make it a new answer.
from datetime import datetime
from calendar import monthrange
from dateutil.relativedelta import relativedelta
s = '2020-07-31T07:03:08Z'
dt = datetime.fromisoformat(s.replace('Z', '+00:00'))
for _ in range(6):
print(dt)
# first, add a month
dt += relativedelta(months=1)
# then analyse the result if it falls on a month with less than 31 days:
mr = monthrange(dt.year, dt.month)
if mr[1] < 31 and dt.day != 1: # also check if day==1, leave it at that if so.
dt += relativedelta(days=1)
# 2020-07-31 07:03:08+00:00
# 2020-08-31 07:03:08+00:00
# 2020-10-01 07:03:08+00:00
# 2020-11-01 07:03:08+00:00
# 2020-12-01 07:03:08+00:00
# 2021-01-01 07:03:08+00:00
s = '2020-12-30T07:03:08Z'
dt = datetime.fromisoformat(s.replace('Z', '+00:00'))
for _ in range(6):
print(dt)
dt += relativedelta(months=1)
mr = monthrange(dt.year, dt.month)
if mr[1] < 31 and dt.day != 1:
dt += relativedelta(days=1)
# 2020-12-30 07:03:08+00:00
# 2021-01-30 07:03:08+00:00
# 2021-03-01 07:03:08+00:00
# 2021-04-01 07:03:08+00:00
# 2021-05-01 07:03:08+00:00
# 2021-06-01 07:03:08+00:00

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.

Capturing Holiday when computing 3 business days prior from given date

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.

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

Why doesn't this project euler #19 code work? (Python)

I made a code for project euler's #19 problem in Python, and it doesn't give me the right answer.
The question is: You are given the following information, but you may prefer to do some research for yourself.
1 Jan 1900 was a Monday.
Thirty days has September,
April, June and November.
All the rest have thirty-one,
Saving February alone,
Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?
What is the problem here?:
months = {'January': 31,
'February': 28,
'March': 31,
'April': 30,
'May': 31,
'June': 30,
'July': 31,
'August': 31,
'September': 30,
'November': 30,
'December': 31
}
years = range(1900, 2001)
day = 1
def main():
global day
for year in years:
if year % 4 == 0 or year % 400 == 0:
months['February'] = 29
else:
months['February'] = 28
for month in months:
if months[month] == 31:
day += 31 % 7 +1
while day > 7:
day = day - 7
if day == 1:
yield day
result = sum(main())
print (result)
Also it generates different answers everytime I use,
Thanks :)
I don't understand the algorithm and your code. But I can say about this.
it generates different answers everytime I use
Probably you forgot to initialize month, year and day before executing result = sum(main()).
The solution will be simpler, if you use dateutil library:
In [16]: from datetime import datetime
In [17]: from dateutil.relativedelta import relativedelta
In [18]: current = datetime(1901, 1, 1)
In [19]: end = datetime(2001, 1, 1)
In [20]: ans = 0
In [21]: while current < end:
....: if current.weekday() == 6:
....: ans += 1
....: current += relativedelta(months=1)
....:
In [22]: ans
Out[22]: 171

Categories