What is the best way to handle datetime value overflow - python

I am attempting to build a program to handle alerts. I want it to be able to handle specific dates like 8/23/2015 7:00 and relative dates like 5 days and 7 hours from now. specific dates are fine but for relative dates if I try and just add 5 days and 7 hours to the date time it can overflow the values intended for that spot
import datetime
dt = datetime.datetime.now()
dayslater = 5
hourslater = 7
minuteslater = 30
alarmTime = datetime.datetime(dt.year, dt.month, dt.day + dayslater,
dt.hour + hourslater,
dt.minute + minuteslater, 0,0)
this is fine sometimes but if dayslater was 40 days it would overflow the value. I did set up a simple
if hours >= 24:
hours -= 24
days++
however this won't work for overflowing months whose length in days isn't consistent.

Don't. Dates are hard, and it's very easy to get it wrong.
Instead, use timedelta:
In [1]: from datetime import datetime, timedelta
In [2]: dt = datetime.now()
In [3]: dt
Out[3]: datetime.datetime(2015, 7, 23, 15, 2, 55, 836914)
In [4]: alarmTime = dt + timedelta(days=5, hours=7, minutes=30)
In [5]: alarmTime
Out[5]: datetime.datetime(2015, 7, 28, 22, 32, 55, 836914)

Use a datetime.timedelta() object and leave calculations to the datetime library:
import datetime
delta = datetime.timedelta(days=dayslater, hours=hourslater, minutes=minuteslater)
alarmTime = datetime.datetime.now() + delta
Demo:
>>> import datetime
>>> dt = datetime.datetime.now()
>>> dayslater = 5
>>> hourslater = 7
>>> minuteslater = 30
>>> delta = datetime.timedelta(days=dayslater, hours=hourslater, minutes=minuteslater)
>>> delta
datetime.timedelta(5, 27000)
>>> dt
datetime.datetime(2015, 7, 23, 21, 4, 59, 987926)
>>> dt + delta
datetime.datetime(2015, 7, 29, 4, 34, 59, 987926)
Note how the hours carried over to the next day (from 21:04 to 04:34), and thus the date went from the 23rd to the 29th. I did not have to worry about 'overflow' here.
This continues to work at month boundaries, at year boundaries, and in leap years, with February 29th:
>>> datetime.datetime(2015, 7, 26, 22, 42) + delta
datetime.datetime(2015, 8, 1, 6, 12)
>>> datetime.datetime(2015, 12, 26, 22, 42) + delta
datetime.datetime(2016, 1, 1, 6, 12)
>>> datetime.datetime(2016, 2, 23, 22, 42) + delta
datetime.datetime(2016, 2, 29, 6, 12)

Related

set datetime object time value to 0

I need to set the time value of detatime objects to 00:00, so I can easily compare two 'dates'
Now I do:
extracted_start_date = datetime.strptime(extracted_start_date.strftime('%Y-%m-%d'), '%Y-%m-%d')
so I have my datetime object
$ datetime.datetime(2021, 3, 18, 11, 13, 53, 782088),
I extract the date string strftime('%Y-%m-%d')
$ '2021-03-18'
and put this back into a datetime object now wch has time 00:00
$ datetime.datetime(2021, 3, 18, 0, 0)
This seems quite elaborate, is there a more efficient way, or is it OK like this?
datetime.datetime objects have method date which return datetime.date instance and these might be used for day-based comparison, consider following example:
import datetime
d1 = datetime.datetime(2021, 3, 17, 9, 0, 0) # yesterday 9:00
d2 = datetime.datetime(2021, 3, 18, 9, 0, 0) # today 9:00
d3 = datetime.datetime(2021, 3, 18, 12, 0, 0) # today 12:00
print(d1.date() == d2.date())
print(d2.date() == d3.date())
output:
False
True
You could use the replace method instead.
from datetime import datetime
d = datetime(2021, 3, 18, 11, 13, 53, 782088)
d.replace(hour=0, minute=0, second=0, microsecond=0)
> datetime.datetime(2021, 3, 18, 0, 0)

How to get datetime.now().isoformat() of future time?

I have
In [1]: from datetime import datetime
In [2]: datetime.now().isoformat()
Out[2]: '2019-11-05T14:55:58.267650'
and I want to get the isoformat of 10 seconds from now + change the format to yyyymmddThhmmss.
The format change can be done by:
In [6]: datetime.now().isoformat()
Out[6]: '2019-11-05T14:58:36.572646'
In [7]: datetime.now().isoformat().split('.')[0].replace('-', '').replace(':', '')
Out[7]: '20191105T145923'
But how can I add time?
Maybe use datetime.timedelta(), like this:
>>> import datetime
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2019, 11, 5, 10, 9, 16, 129672)
>>> new_date = now + datetime.timedelta(seconds=30)
>>> new_date
datetime.datetime(2019, 11, 5, 10, 9, 46, 129672)
Now format the new date as string:
>>> new_date.isoformat().split('.')[0].replace('-', '').replace(':', '')
'20191105T100946'
Or way cleaner using .strftime():
>>> new_date.strftime("%Y%m%dT%H%M%S")
'20191105T100946'

Why isn't my code working to make datetime objects timezone-aware?

I have two python datetime objects that represent the same moment in time:
a = datetime.datetime(2018, 10, 28, 13, 26, 30)
b = datetime.datetime(2018, 10, 28, 7, 26, 30)
Both are coming from different sources.
I know that the first is in UTC, and the second is in "America/Edmonton" (MDT).
Neither initially have a timezone attached to them.
I need to add timezones to these objects and compare them in a way where a == b is True.
What I did was this:
import datetime
from pytz import timezone
a = datetime.datetime(2018, 10, 28, 13, 26, 30)
b = datetime.datetime(2018, 10, 28, 7, 26, 30)
a = a.replace(tzinfo=timezone("UTC"))
b = b.replace(tzinfo=timezone("America/Edmonton"))
a = a.astimezone(timezone("America/Edmonton"))
b = b.astimezone(timezone("America/Edmonton"))
print(repr(a))
# Result: datetime.datetime(2018, 10, 28, 7, 26, 30, tzinfo=<DstTzInfo 'America/Edmonton' MDT-1 day, 18:00:00 DST>)
print(repr(b))
# Result: datetime.datetime(2018, 10, 28, 7, 26, 30, tzinfo=<DstTzInfo 'America/Edmonton' LMT-1 day, 16:26:00 STD>)
a == b # Results in False for some reason
What is "MDT-1 day, 18:00:00 DST" vs "LMT-1 day, 16:26:00 STD"? Why are they different? What am I doing wrong?
The proper way to do this appears to be:
import datetime
from pytz import timezone
a = datetime.datetime(2018, 10, 28, 13, 26, 30)
b = datetime.datetime(2018, 10, 28, 7, 26, 30)
a = timezone('UTC').localize(a)
b = timezone('America/Edmonton').localize(b)
a == b
As demonstrated here. This does result in a being equal to b. Still not sure why it sounds like pytz is defaulting to using a system from before 1893.

Calculating a timewindow with Python datetime

I'd like to query my DB for all records posted on a particular day (e.g. today between 00:00 and 23:59:59) given a datetime.datetime timestamp such as datetime.datetime(2010, 12, 21, 17, 59, 43, 85335).
What's the best method to calculate the start and end datetime.datetime instances, please? I'd like to end up with something like this:
>>> timestamp = datetime.datetime(2010, 12, 21, 17, 59, 43, 85335)
>>> # do something with timestamp to get start_date and end_date
>>> start_date
datetime.datetime(2010, 12, 21, 0, 0, 0, 0)
>>> end_date
datetime.datetime(2010, 12, 21, 23, 59, 59, 0)
Thanks, HC
Ah, found what I want. I struggle with datetime every time.
>>> timestamp = datetime.datetime.now()
>>> timestamp
datetime.datetime(2010, 12, 21, 18, 31, 37, 900795)
>>> timestamp.replace(hour=0, minute=0, second=0, microsecond=0)
datetime.datetime(2010, 12, 21, 0, 0)
If it's just calculating a day, then what you have is fine. Just use the day and then put the hour as 0:00 and 23:59 respectively.
If you want to get the start and end times of a month, that gets a lot more complicated since months have different days. For something like that you could use this module
http://niemeyer.net/python-dateutil
timedelta sounds just like what you need.
http://docs.python.org/library/datetime.html#timedelta-objects
from datetime import datetime, date
date.today() - timedelta(days=7)
Will give you 7 days from today.

A more suitable way to rewrite this?

I have the method:
def checkAgainstDate():
currentDate = date.today()
currentMonth = date.today().month
if currentMonth == 1
year = currentDate.year-1
return date(year, 11, 01)
elif currentMonth == 2:
year = currentDate.year-1
return date(year, 12, 01)
else
return date(currentDate.year, currentMonth-2, 01)
This just returns the first of the month 2 months ago, which is what I want is there a better approach I could have used using timedeltas? I choose my way because weeks in a month are not always constant.
dateutil is an amazing thing. It really should become stdlib someday.
>>> from dateutil.relativedelta import relativedelta
>>> from datetime import datetime
>>> (datetime.now() - relativedelta(months=2)).replace(day=1)
datetime.datetime(2010, 6, 1, 13, 16, 29, 643077)
>>> (datetime(2010, 4, 30) - relativedelta(months=2)).replace(day=1)
datetime.datetime(2010, 2, 1, 0, 0)
>>> (datetime(2010, 2, 28) - relativedelta(months=2)).replace(day=1)
datetime.datetime(2009, 12, 1, 0, 0)
Convert to an "absolute month number", subtract 2, convert back to year & month:
currentdate = date.today()
monthindex = 12*currentdate.year + (currentdate.month-1) -2
return datetime( monthindex // 12, monthindex % 12 + 1, 1)

Categories