While loop with datetime functions will not work [closed] - python

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
For some reason, this while loop will not execute it seems. Can anyone help me possibly?
EDIT: the loop now works but now the alteration in the loop is simply returning an infinite repeating string of July 15, 0559
As for the purpose of this program, it is meant to calculate certain dates of the st. Habakkuk feast day, which is based on an interval of 256 days. It is meant to print the 0th, 10th, first 20th century fest day, and the upcoming feast day along with its ordinal value.
this is an incomplete code as of right now.
from datetime import date, datetime, timedelta
origD = date(559, 7, 14)
print(f"0th Feast day: {origD.strftime('%B'), origD.strftime('%d'), origD.strftime('%Y')}")
day = int(origD.day)
month = int(origD.month)
year = int(origD.year)
dNow = date.today()
yearNow = int(dNow.year)
monthNow = int(dNow.month)
dayNow = int(dNow.day)
daysToFeast = int(0)
whichFeast = int(0)
while not (day == dayNow and month == monthNow and year == yearNow):
nextDay = timedelta(1)
newDate = origD + nextDay
daysToFeast += 1
day = int(newDate.day)
month = int(newDate.month)
year = int(newDate.year)
if daysToFeast == 256:
whichFeast += 1
daysToFeast = 0
if whichFeast == 10:
print(f"10th Feast day: {newDate.strftime('%B'), newDate.strftime('%d'), newDate.strftime('%Y')}")
if year == 1900:
print(f"First 20th Century Feast day: {newDate.strftime('%B'), newDate.strftime('%d'), newDate.strftime('%Y')}")
'''

Boolean operations like not, and and or have precedence rules which govern which operations in an expression are applied first and to which arguments.
For instance, not has higher precedence ('binds' more tightly) than and. So your while condition in the above is actually equivalent to:
(not day == dayNow) and (month == monthNow) and (year == yearNow)
which will be False rather earlier than you intend...
Instead, you can group the expressions to make explicit your intent and to override the precedence rules, e.g.
not (day == dayNow and month == monthNow and year == yearNow)
Doing so allows the while loop body to execute, but reveals some other problems. The solutions may become obvious to you once you encounter them. Nevertheless, I would say your code could be significantly shorter and simpler.
For example, why bother maintaining separate variables for days, months, years etc. when the datetime module can step through days (somedate += timedelta(days=1)) and maintain that information for you? This would certainly make the while condition more readable (not date1 == date2), and might help you (and StackOverflow contributors) identify where the problems are coming from. Here is my attempt and making your code more concise, and which prints the 10th 'feast day':
from datetime import date, timedelta
then = date(559, 7, 14)
print(f"0th Feast day: {then.strftime('(%B, %d, %Y)')}")
now = date.today()
print(f"Today: {now.strftime('(%B, %d, %Y)')}")
whichFeast = 0
while then < now:
then += timedelta(days=256)
whichFeast += 1
if whichFeast == 10:
print(f"10th Feast day: {then.strftime('(%B, %d, %Y)')}")

Disregarding the rest of the code, the loop does not start because of
while not day == dayNow and month == monthNow and year == yearNow:
( that is the same of
while not (day == dayNow) and (month == monthNow) and (year == yearNow):
that always returns False )
try
while not (day == dayNow and month == monthNow and year == yearNow):

Related

Determine a date starting from two integer numbers

Determine a date (as day, month, year) starting from two integer numbers that represent the year and the number of the day in that year.
i'm new to coding and I don't know where to start even.
If I understand correctly you can use Python datetime module to do what you need:
from datetime import datetime
print(datetime.strptime("2004-68", "%Y-%j").strftime("%d.%m.%Y"))
Result:
08.03.2004
You can start with creating algorithm in your head, or on paper.
Then you can translate it to python code.
or
you can read documentation about datetime lib in python and try to combine functions to you desired output. https://docs.python.org/3/library/datetime.html
For example:
from datetime import datetime, timedelta
year = 2004
days = 68
date = datetime(year, 1, 1) + timedelta(days=days-1)
# days-1 is needed because we already start with first day in year
print(date)
# 2004-03-08 00:00:00
year=int(input("year="))
days=int(input("number of days="))
day=int
month = days/28 + 1
m = int(month)
if ((year % 400 == 0) or
(year % 100 != 0) and
(year % 4 == 0)) :
day = days % 30
else:
day = days % 30 + 1
print(day,".", m, ".",year)
is this any good? or more preciseley is it corect? like i need this more basic aproach to the problem

Date Detection program gone wrong

I am a novice working on a short program with the purpose of detecting dates and printing out whether the given dates are valid or not. Here's how it looks like :
dateRegex = re.compile(r'''(
(0[1-9]|[12]\d|30|31)
[.\\ /]
(0[1-9]|1[0-2])
[.\\ /]
([1-2][0-9]{3})
)''', re.VERBOSE)
def dateValidation(date):
text = str(pyperclip.paste())
mo = date.findall(text)
for groups in mo:
day = groups[1]
month = groups[2]
year = groups[3]
leapyear = ''
if ( month == '04' or month == '06' or month == '09' or month == '11' ) and ( int(day) > 30 ):
print(f'The {groups[0]} string is not a date.')
continue
if int(year) % 4 == 0:
leapyear += year
if int(year) % 100 == 0:
leapyear = ''
if ( int(year) % 100 == 0 ) and ( int(year) % 400 == 0 ):
leapyear += year
if month == '02' and leapyear == year:
if int(day) > 29:
print(f'The {groups[0]} string is not a date.')
continue
elif month == '02' and leapyear != year:
if int(day) > 28:
print(f'The {groups[0]} string is not a date.')
continue
print(f'The {groups[0]} string is a date.')
dateValidation(dateRegex)
I know a lot of the code isn't clean or practical, so I'm open to suggestions about optimizing it, of course ( I'm fairly new to this after all, and apparently doing horribly ), but the question is mainly regarding the output of the program.
I copied 01.02.2016 21.6.2003 26.7.1999 to clipboard and expected to get a result regarding all three dates. Instead, the output was only ''The 01.02.2016 string is a date.'' Did I overlook something ? What could've gone wrong ?
If it isn't obvious from the code, here is a detailed description of what the program is supposed to do :
Write a regular expression that can detect dates in the DD/MM/YYYY format. Assume that the days range from 01 to 31, the months range from 01 to 12, and the years range from 1000 to 2999. Note that if the day or month is a single digit, it’ll have a leading zero.
The regular expression doesn’t have to detect correct days for each month or for leap years; it will accept nonexistent dates like 31/02/2020 or 31/04/2021. Then store these strings into variables named month, day, and year, and write additional code that can detect if it is a valid date. April, June, September, and November have 30 days, February has 28 days, and the rest of the months have 31 days. February has 29 days in leap years. Leap years are every year evenly divisible by 4, except for years evenly divisible by 100, unless the year is also evenly divisible by 400. Note how this calculation makes it impossible to make a reasonably sized regular expression that can detect a valid date.
Thanks in advance.
I think the problem with the regular expression follows from the format of the dates in the text. Since some of the dates are given as 21.6.2003 and not 21.06.2003, your regex misses that.
For the dates you can use the following one:
r'(0*[0-9]|1[0-9]|2[0-9]|3[0-1])\.(0*[0-9]|1[0-2])\.[1-2][0-9]{3})'
Here,
(0*[0-9]|1[0-9]|2[0-9]|3[0-1]) matches the days ranging in 00-31. In the first case, 0* tells regex to match zero or more of the preceding token. So, if the date is given in 06 or 6 format, it can catch both cases
Similar approach also follows in (0*[0-9]|1[0-2]), which finds the month in the range 00-12

How can i query for objects in current year , current month in django

I need to find total objects created in
1. current year
2. current month
3. last month
4. last year
I am thinking like this
this_year = datetime.now().year
last_year = datetime.now().year -1
this_month = datetime.now().month
last month = (datetime.today() - timedelta(days=30)).month
Use like
Order.objects.filter(created_at__month=this_month)
The problem is
last_month i want is calendar month not 30 days back
i am not sure whether created_at__month=this_month will match current month or same month in previous year
is it possible to get all counts in single query
today = datetime.datetime.now()
1 Current year
Order.objects.filter(created_at__year=today.year)
2 Current month
Order.objects.filter(created_at__year=today.year, created_at__month=today.month)
3 Last month
last_month = today.month - 1 if today.month>1 else 12
last_month_year = today.year if today.month > last_month else today.year - 1
Order.objects.filter(created_at__year=last_month_year, created_at__month=last_month)
4 Last year
last_year = today.year - 1
Order.objects.filter(created_at__year=last_year)
5 Single Query
As last year + current year includes last month and current month, and all orders>= last_year includes current year, the query is super simple:
Order.objects.filter(created_at__year__gte=last_year)
I don't think you'll be able to just match the "month" or "year" part of a date field without some significant fiddling or annotating. Most likely, your simplest solution is to define the start and end of the range you want and search against that. And that might involve a little bit of work.
For example, last calendar month would be:
today = datetime.now()
if today.month == 1:
last_month_start = datetime.date(today.year-1, 12, 1)
last_month_end = datetime.date(today.year-1, 12, 31)
else:
last_month_start = datetime.date(today.year, today.month -1, 1)
last_month_end = datetime.date(today.year, today.month, 1) - datetime.timedelta(days=1)
Order.objects.filter(created_at__gte=last_month_start, created_at__lte=last_month_end)
GTE and LTE are "greater than or equal" and "less than or equal". Also worth noting, we use timedelta to figure out what the day before the first of this month is rather than go through all the different cases of whether the previous month had 28, 29, 30 or 31 days.
If you want it in separate queries, do something like that.
from_this_year = Order.objects.filter(created_at__year=this_year)
from_last_year = Order.objects.filter(created_at__year=last_year)
from_june = Order.objects.filter(created_at__month='06',created_at__year=this_year)
from_this_month = Order.objects.filter(created_at__month=this_month,created_at__year=this.year)
note: in my example, I put '06' that is June, but you can change it.

Algorithm for getting current week number after changing the starting day of the week in python?

I want to design an algorithm which will calculate the week number according to the start week day set. for eg : - If I set the start day as WEDNESDAY and currently its 40 week and its TUESDAY, it should print 40 as the week number. If it is WEDNESDAY or THURSDAY, I should get 41.
Think of it like a cycle. From Wednesday till tuesday, it should be assigned a week no + 1, then when next wednesday comes, week should be incremented again.
I tried using calendar.setfirstweekday(calendar.WEDNESDAY) and then altering my system machine time, all I get is 40 as the week number everytime.
How do I design such as algorithm in python?
I have a similar problem for month, but I have designed a solution for it. Here is it.
current_date = datetime.datetime.now()
if current_date.day < gv.month_start_date:
month = current_date.month -1
if month == 0:
month = 12
else:
month = current_date.month
How can I design it for week?
I finally designed a solution for this.
if current_day >= set_week_day:
week = current_week
else:
week = current_week - 1
Works for all cases.
datetime in python has a function called isocalender to get the ISO week number (starts on Monday)
import datetime
datetime.date(2013, 9, 30).isocalendar()[1]
You can use this with a little bit of logic (this script should have the week begin on Wednesdays)
import datetime
day = 30
month = 9
year = 2013
weekcount = datetime.date(year, month, day).isocalendar()[1]
if datetime.date(year, month, day).isocalendar()[2] <= 3: # start on wednesday
weekcount -= 1
print weekcount

Given a date range how to calculate the number of weekends partially or wholly within that range?

Given a date range how to calculate the number of weekends partially or wholly within that range?
(A few definitions as requested:
take 'weekend' to mean Saturday and Sunday.
The date range is inclusive i.e. the end date is part of the range
'wholly or partially' means that any part of the weekend falling within the date range means the whole weekend is counted.)
To simplify I imagine you only actually need to know the duration and what day of the week the initial day is...
I darn well now it's going to involve doing integer division by 7 and some logic to add 1 depending on the remainder but I can't quite work out what...
extra points for answers in Python ;-)
Edit
Here's my final code.
Weekends are Friday and Saturday (as we are counting nights stayed) and days are 0-indexed starting from Monday. I used onebyone's algorithm and Tom's code layout. Thanks a lot folks.
def calc_weekends(start_day, duration):
days_until_weekend = [5, 4, 3, 2, 1, 1, 6]
adjusted_duration = duration - days_until_weekend[start_day]
if adjusted_duration < 0:
weekends = 0
else:
weekends = (adjusted_duration/7)+1
if start_day == 5 and duration % 7 == 0: #Saturday to Saturday is an exception
weekends += 1
return weekends
if __name__ == "__main__":
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for start_day in range(0,7):
for duration in range(1,16):
print "%s to %s (%s days): %s weekends" % (days[start_day], days[(start_day+duration) % 7], duration, calc_weekends(start_day, duration))
print
General approach for this kind of thing:
For each day of the week, figure out how many days are required before a period starting on that day "contains a weekend". For instance, if "contains a weekend" means "contains both the Saturday and the Sunday", then we have the following table:
Sunday: 8
Monday: 7
Tuesday: 6
Wednesday: 5
Thursday: 4
Friday: 3
Saturday: 2
For "partially or wholly", we have:
Sunday: 1
Monday: 6
Tuesday: 5
Wednesday: 4
Thursday: 3
Friday: 2
Saturday: 1
Obviously this doesn't have to be coded as a table, now that it's obvious what it looks like.
Then, given the day-of-week of the start of your period, subtract[*] the magic value from the length of the period in days (probably start-end+1, to include both fenceposts). If the result is less than 0, it contains 0 weekends. If it is equal to or greater than 0, then it contains (at least) 1 weekend.
Then you have to deal with the remaining days. In the first case this is easy, one extra weekend per full 7 days. This is also true in the second case for every starting day except Sunday, which only requires 6 more days to include another weekend. So in the second case for periods starting on Sunday you could count 1 weekend at the start of the period, then subtract 1 from the length and recalculate from Monday.
More generally, what's happening here for "whole or part" weekends is that we're checking to see whether we start midway through the interesting bit (the "weekend"). If so, we can either:
1) Count one, move the start date to the end of the interesting bit, and recalculate.
2) Move the start date back to the beginning of the interesting bit, and recalculate.
In the case of weekends, there's only one special case which starts midway, so (1) looks good. But if you were getting the date as a date+time in seconds rather than day, or if you were interested in 5-day working weeks rather than 2-day weekends, then (2) might be simpler to understand.
[*] Unless you're using unsigned types, of course.
My general approach for this sort of thing: don't start messing around trying to reimplement your own date logic - it's hard, ie. you'll screw it up for the edge cases and look bad. Hint: if you have mod 7 arithmetic anywhere in your program, or are treating dates as integers anywhere in your program: you fail. If I saw the "accepted solution" anywhere in (or even near) my codebase, someone would need to start over. It beggars the imagination that anyone who considers themselves a programmer would vote that answer up.
Instead, use the built in date/time logic that comes with Python:
First, get a list of all of the days that you're interested in:
from datetime import date, timedelta
FRI = 5; SAT = 6
# a couple of random test dates
now = date.today()
start_date = now - timedelta(57)
end_date = now - timedelta(13)
print start_date, '...', end_date # debug
days = [date.fromordinal(d) for d in
range( start_date.toordinal(),
end_date.toordinal()+1 )]
Next, filter down to just the days which are weekends. In your case you're interested in Friday and Saturday nights, which are 5 and 6. (Notice how I'm not trying to roll this part into the previous list comprehension, since that'd be hard to verify as correct).
weekend_days = [d for d in days if d.weekday() in (FRI,SAT)]
for day in weekend_days: # debug
print day, day.weekday() # debug
Finally, you want to figure out how many weekends are in your list. This is the tricky part, but there are really only four cases to consider, one for each end for either Friday or Saturday. Concrete examples help make it clearer, plus this is really the sort of thing you want documented in your code:
num_weekends = len(weekend_days) // 2
# if we start on Friday and end on Saturday we're ok,
# otherwise add one weekend
#
# F,S|F,S|F,S ==3 and 3we, +0
# F,S|F,S|F ==2 but 3we, +1
# S|F,S|F,S ==2 but 3we, +1
# S|F,S|F ==2 but 3we, +1
ends = (weekend_days[0].weekday(), weekend_days[-1].weekday())
if ends != (FRI, SAT):
num_weekends += 1
print num_weekends # your answer
Shorter, clearer and easier to understand means that you can have more confidence in your code, and can get on with more interesting problems.
To count whole weekends, just adjust the number of days so that you start on a Monday, then divide by seven. (Note that if the start day is a weekday, add days to move to the previous Monday, and if it is on a weekend, subtract days to move to the next Monday since you already missed this weekend.)
days = {"Saturday":-2, "Sunday":-1, "Monday":0, "Tuesday":1, "Wednesday":2, "Thursday":3, "Friday":4}
def n_full_weekends(n_days, start_day):
n_days += days[start_day]
if n_days <= 0:
n_weekends = 0
else:
n_weekends = n_days//7
return n_weekends
if __name__ == "__main__":
tests = [("Tuesday", 10, 1), ("Monday", 7, 1), ("Wednesday", 21, 3), ("Saturday", 1, 0), ("Friday", 1, 0),
("Friday", 3, 1), ("Wednesday", 3, 0), ("Sunday", 8, 1), ("Sunday", 21, 2)]
for start_day, n_days, expected in tests:
print start_day, n_days, expected, n_full_weekends(n_days, start_day)
If you want to know partial weekends (or weeks), just look at the fractional part of the division by seven.
You would need external logic beside raw math. You need to have a calendar library (or if you have a decent amount of time implement it yourself) to define what a weekend, what day of the week you start on, end on, etc.
Take a look at Python's calendar class.
Without a logical definition of days in your code, a pure mathematical methods would fail on corner case, like a interval of 1 day or, I believe, anything lower then a full week (or lower then 6 days if you allowed partials).

Categories

Resources