I have the following simple setup, where fromDate and toDate are strings on the format "YYYY-MM-DD":
class SomeType(Base):
date = Column(DateTime)
def findAll(fromDate, toDate):
return session.query(SomeType).filter(SomeType.date >= fromDate, SomeType.date <= toDate).all()
The problem is that it doesn't find what I want it to find unless I modify the input dates like this:
def findAll(fromDate, toDate):
fromDate = fromDate + " 00:00"
toDate = toDate + " 24:00"
return session.query(SomeType).filter(SomeType.date >= fromDate, SomeType.date <= toDate).all()
But that doesn't look good. Any ideas on how I can do this the right way?
How about using datetime.datetime objects instead of strings for fromDate, toDate?
from datetime import datetime, timedelta
def findAll(fromDate, toDate):
fromDate = datetime.strptime(fromDate, '%Y-%m-%d')
toDate = datetime.strptime(toDate, '%Y-%m-%d') + timedelta(days=1)
return session.query(SomeType).filter(
SomeType.date >= fromDate,
SomeType.date < toDate).all()
The problem is that your SomeType.date column is not simple date, but is datetime column, so it contains also a time component.
This type mismatch is the cause of your problem. If this is the case then following should work:
session.query(SomeType).filter(func.date(SomeType.date) >= fromDate, func.date(SomeType.date) <= toDate).all()
where we basically cast datetime to date using DATE(...) function of MySql.
However, I would probably also prefer working with date(time) data types instead of strings. You are just lucky that most databases implicitly allow parsing of ISO-compliant string representations of DATEs.
I know this is old, but while trying to find my answer, I found datetime.combine
you can do
select(SomeTable)
.filter( SomeTable.datetime_issued >= datetime.combine(start_date, time.min),
SomeTable.datetime_issued <= datetime.combine(end_date, time.max))
datetime.combine will combine date and time into datetime
https://docs.python.org/3/library/datetime.html#datetime.datetime.combine
When combining, you should use time.min, time.max which will give you min and max time
print(combine(date.today(), time.min), combine(date.today(), time.max))
This will print
2022-10-14 00:00:00, 2022-10-14 23:59:59.999999
https://docs.python.org/3/library/datetime.html#datetime.time.max
Related
I'm not a Python developer but have to fix an existing code.
In this code, a method (extract) is called providing an interval of dates:
extract(start_date, end_date)
The parameters can have for exemple the values:
start_date : 2020-10-01
end_date : 2022-01-03
The problem
The issue with this call is that the extract method only support a 1 year max interval of dates. If greater, the interval must be split, for exemple as follow:
extract('2020-10-01', '2020-12-31')
extract('2021-01-01', '2021-12-31')
extract('2022-01-01', '2022-01-03')
So I'm trying to create loop where the start_date and end_date are computed dynamically. But being new to Python, I have no ideas for now how this can be done. Any help would be greatly appreciated.
EDIT
Answer to some comments here
Tried so far finding a solution starting from code like this so far:
from datetime import datetime
from dateutil import relativedelta
from datetime import datetime
from_date = datetime.strptime('2020-10-01', "%Y-%m-%d")
end_date = datetime.strptime('2022-01-03', "%Y-%m-%d")
# Get the interval between the two dates
diff = relativedelta.relativedelta(end_date, from_date)
Then I thought iterating accross the years using diff.years and adding some logic to build the start_date and end_date from there, but I thought there might be a much simplier approach.
Also saw others possibilities like here but still no final simple result found at the moment.
from_str = '2020-10-01'
end_str = '2022-01-03'
from_year = int(from_str[:4])
end_year = int(end_str[:4])
if from_year != end_year:
# from_date to end of first year
extract(from_str, f"{from_year}-12-31")
# full years
for y in range(from_year + 1, end_year):
extract(f"{y}-01-01", f"{y}-12-31")
# rest
extract(f"{end_year}-01-01", end_str)
else:
extract(from_str, end_str)
As mentioned in the comments, you can either use the datetime library or you can also use pandas if you want. The pandas version is the following (admittively not the most pretty, but it does the job):
import pandas as pd
import datetime
start = datetime.datetime(2020,10,1)
end = datetime.datetime(2022,1,3)
def extract(from_dt, to_dt):
print(f'Extracting from {from_dt} to {to_dt}')
prev_end = pd.to_datetime(start)
for next_end in pd.date_range(datetime.datetime(start.year, 12, 31), end, freq='y'):
if next_end < end:
extract(prev_end.strftime('%Y-%m-%d'), next_end.strftime('%Y-%m-%d'))
else:
extract(prev_end.strftime('%Y-%m-%d'), end.strftime('%Y-%m-%d'))
prev_end = next_end + datetime.timedelta(days=1)
if prev_end < end:
extract(prev_end.strftime('%Y-%m-%d'), end.strftime('%Y-%m-%d'))
If you need to parse the original dates from strings, check out datetime.strptime
This kind of problems are nice ones to resolve by recursion:
from datetime import datetime
start_date = '2020-10-01'
end_date = '2022-01-03'
def intervalcalc(datestart,dateend):
newdate=dateend[:4] + '-01-01'
startd = datetime.strptime(datestart, "%Y-%m-%d")
endd = datetime.strptime(newdate, "%Y-%m-%d")
if endd < startd:
print(datestart, dateend)
return True
else:
print(newdate, dateend)
previousyear=str(int(newdate[:4])-1) + '-12-31'
intervalcalc(datestart,previousyear)
intervalcalc(start_date, end_date)
output:
2022-01-01 2022-01-03
2021-01-01 2021-12-31
2020-10-01 2020-12-31
You just need to change the prints by calls to extract function.
As mentioned by #Wups the conversion to date is not really necessary, it could be an string compare as they are YYYYMMDD dates.
Also, this can be done the other way around and calculate from the start date year + '-12-31' and then compare dateend>end_date to determine the anchor for the recursion.
I am new to functions and I am trying to write a function that returns the number of days between two dates:
My attempt:
import datetime
from dateutil.parser import parse
def get_x_days_ago (date_from, current_date = None):
td = current_date - parse(date_from)
if current_date is None:
current_date = datetime.datetime.today()
else:
current_date = datetime.datetime.strptime(date_from, "%Y-%m-%d")
return td.days
print(get_x_days_ago(date_from="2021-04-10", current_date="2021-04-11"))
Expected outcome in days:
1
So there seem to be multiple issues, and as I said in the comments, a good idea would be to separate the parsing and the logic.
def get_x_days_ago(date_from, current_date = None):
if current_date is None:
current_date = datetime.datetime.today()
return (current_date - date_from).days
# Some other code, depending on where you are getting the dates from.
# Using the correct data types as the input to the get_x_days_ago (datetime.date in this case) will avoid
# polluting the actual logic with the parsing/formatting.
# If it's a web framework, convert to dates in the View, if it's CLI, convert in the CLI handling code
date_from = parse('April 11th 2020')
date_to = None # or parse('April 10th 2020')
days = get_x_days_ago(date_from, date_to)
print(days)
The error you get is from this line (as you should see in the traceback)
td = current_date - parse(date_from)
Since current_date="2021-04-11" (string), but date_from is parsed parse(date_from), you are trying to subtract date from the str.
P.S. If you have neither web nor cli, you can put this parsing code into def main, or any other point in code where you first get the initial strings representing the dates.
It looks like you're already aware that you can subtract a datetime from a datetime. I think, perhaps, you're really looking for this:
https://stackoverflow.com/a/23581184/2649560
Is there a simple way to convert numbers like 1030, 0131, 1231, etc to an mm-dd (or even mm-dd-yyyy assuming everything is 2020) format, so that I can perform date calculations with them? For example, I would like to be able to do (1231 minus 0131) = 11 months.
Of course, I could just do the following to change the formatting, but looking to see if there's a more intuitive way!
startDate = startDate[:2] + "-" + startDate[2:] + "-2020"
endDate = endDate[:2] + "-" + endDate[2:] + "-2020"
You can convert to datetime object straight away, using strptime directive "%m%d". This allows you to make timedelta calculations later on. The default year is 1900; if you wish, you can replace by 2020.
from datetime import datetime
startDate, endDate = "0131", "1231"
startDate, endDate = (datetime.strptime(startDate, "%m%d").replace(year=2020),
datetime.strptime(endDate,"%m%d").replace(year=2020))
deltaDays = (endDate-startDate).days
# 335
Let me know if it is not exactly what you want:
from datetime import datetime
s = "1030"
d = '-'.join([s[:2], s[2:]]) + '-2020'
date_object = datetime.strptime(d, '%m-%d-%Y').date()
I have a datetime object representing when a user logged, from this object I want to check whether the datetime object expresses a date in the range today -7, so if today is 2018-06-21 I would like to return ever date between today and 2018-06-14. I attach an example of my code (terribly wrong).
def joined_today(self, query):
filtered = []
today = datetime.today().date()
for x in query:
if x[1].date() = today -7:
filtered.append(x)
return filtered
A couple of things here. Firstly, you can do date calculations using datetime.timedelta. And secondly, you don't want to check that the date is equal to "today - 7", you want to check it is greater than that and less than or equal to today. So:
seven_days_ago = today - timedelta(days=7)
for x in query:
if seven_days_ago < x[1].date() <= today:
...
I'm trying to change default output formatting of strptime and datetime in my functions:
def start_week_date(year, week):
format_string = "%i %i 1" % (int(year),int(week))
start = time.strptime(format_string, '%Y %W %w')
print datetime.date(start.tm_year, start.tm_mon, start.tm_mday)
return datetime.date(start.tm_year, start.tm_mon, start.tm_mday)
output of which is being passed to another one:
for date_oncall in date_range(start_week_date(year,week), start_week_date(year,week+1)):
print date_oncall
def date_range(start_date, end_date):
"""Generator of dates in between"""
if start_date > end_date:
raise ValueError("Start date is before end date.")
while True:
yield start_date
start_date = start_date + datetime.timedelta(days=1)
if start_date >= end_date:
break
Is there an elegant way to change default formatting so if day of a month or a month is < 10 it doesn't get the '0' at the beginning?
Basically instead of '03-05-2012' I would like to get '3-5-2012'.
Thanks much in advance for any suggestions.
Regards,
Jakub
date objects have a method, strftime, to manually specify the format, but it doesn't have an option to do what you want - so, that means you need to construct the string yourself from the other attributes of date_oncall. The good news is that this is quite easy:
>>> '{d.day}-{d.month}-{d.year}'.format(d=date_oncall)
'17-1-2010'
check this link:
http://www.tutorialspoint.com/python/time_strftime.htm
by using
time.strftime(string[, format])
you can specify the day of the month format without the '0' at the beginning, by using '%e'.