What is the standard way to add N seconds to datetime.time in Python? - python

Given a datetime.time value in Python, is there a standard way to add an integer number of seconds to it, so that 11:34:59 + 3 = 11:35:02, for example?
These obvious ideas don't work:
>>> datetime.time(11, 34, 59) + 3
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'int'
>>> datetime.time(11, 34, 59) + datetime.timedelta(0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'
>>> datetime.time(11, 34, 59) + datetime.time(0, 0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.time'
In the end I have written functions like this:
def add_secs_to_time(timeval, secs_to_add):
secs = timeval.hour * 3600 + timeval.minute * 60 + timeval.second
secs += secs_to_add
return datetime.time(secs // 3600, (secs % 3600) // 60, secs % 60)
I can't help thinking that I'm missing an easier way to do this though.
Related
python time + timedelta equivalent

You can use full datetime variables with timedelta, and by providing a dummy date then using time to just get the time value.
For example:
import datetime
a = datetime.datetime(100,1,1,11,34,59)
b = a + datetime.timedelta(0,3) # days, seconds, then other fields.
print(a.time())
print(b.time())
results in the two values, three seconds apart:
11:34:59
11:35:02
You could also opt for the more readable
b = a + datetime.timedelta(seconds=3)
if you're so inclined.
If you're after a function that can do this, you can look into using addSecs below:
import datetime
def addSecs(tm, secs):
fulldate = datetime.datetime(100, 1, 1, tm.hour, tm.minute, tm.second)
fulldate = fulldate + datetime.timedelta(seconds=secs)
return fulldate.time()
a = datetime.datetime.now().time()
b = addSecs(a, 300)
print(a)
print(b)
This outputs:
09:11:55.775695
09:16:55

As others here have stated, you can just use full datetime objects throughout:
from datetime import datetime, date, time, timedelta
sometime = time(8,00) # 8am
later = (datetime.combine(date.today(), sometime) + timedelta(seconds=3)).time()
However, I think it's worth explaining why full datetime objects are required. Consider what would happen if I added 2 hours to 11pm. What's the correct behavior? An exception, because you can't have a time larger than 11:59pm? Should it wrap back around?
Different programmers will expect different things, so whichever result they picked would surprise a lot of people. Worse yet, programmers would write code that worked just fine when they tested it initially, and then have it break later by doing something unexpected. This is very bad, which is why you're not allowed to add timedelta objects to time objects.

One little thing, might add clarity to override the default value for seconds
>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)

You cannot simply add number to datetime because it's unclear what unit is used: seconds, hours, weeks...
There is timedelta class for manipulations with date and time. datetime minus datetime gives timedelta, datetime plus timedelta gives datetime, two datetime objects cannot be added although two timedelta can.
Create timedelta object with how many seconds you want to add and add it to datetime object:
>>> from datetime import datetime, timedelta
>>> t = datetime.now() + timedelta(seconds=3000)
>>> print(t)
datetime.datetime(2018, 1, 17, 21, 47, 13, 90244)
There is same concept in C++: std::chrono::duration.

Thanks to #Pax Diablo, #bvmou and #Arachnid for the suggestion of using full datetimes throughout. If I have to accept datetime.time objects from an external source, then this seems to be an alternative add_secs_to_time() function:
def add_secs_to_time(timeval, secs_to_add):
dummy_date = datetime.date(1, 1, 1)
full_datetime = datetime.datetime.combine(dummy_date, timeval)
added_datetime = full_datetime + datetime.timedelta(seconds=secs_to_add)
return added_datetime.time()
This verbose code can be compressed to this one-liner:
(datetime.datetime.combine(datetime.date(1, 1, 1), timeval) + datetime.timedelta(seconds=secs_to_add)).time()
but I think I'd want to wrap that up in a function for code clarity anyway.

If it's worth adding another file / dependency to your project, I've just written a tiny little class that extends datetime.time with the ability to do arithmetic. When you go past midnight, it wraps around zero. Now, "What time will it be, 24 hours from now" has a lot of corner cases, including daylight savings time, leap seconds, historical timezone changes, and so on. But sometimes you really do need the simple case, and that's what this will do.
Your example would be written:
>>> import datetime
>>> import nptime
>>> nptime.nptime(11, 34, 59) + datetime.timedelta(0, 3)
nptime(11, 35, 2)
nptime inherits from datetime.time, so any of those methods should be usable, too.
It's available from PyPi as nptime ("non-pedantic time"), or on GitHub: https://github.com/tgs/nptime

For completeness' sake, here's the way to do it with arrow (better dates and times for Python):
sometime = arrow.now()
abitlater = sometime.shift(seconds=3)

In a real world environment it's never a good idea to work solely with time, always use datetime, even better utc, to avoid conflicts like overnight, daylight saving, different timezones between user and server etc.
So I'd recommend this approach:
import datetime as dt
_now = dt.datetime.now() # or dt.datetime.now(dt.timezone.utc)
_in_5_sec = _now + dt.timedelta(seconds=5)
# get '14:39:57':
_in_5_sec.strftime('%H:%M:%S')

If you don't already have a timedelta object, another possibility would be to just initialize a new time object instead with the attributes of the old one and add values where needed:
new_time:time = time(
hour=curr_time.hour + n_hours,
minute=curr_time.minute + n_minutes,
seconds=curr_time.second + n_seconds
)
Admittedly this only works if you make a few assumptions about your values, since overflow is not handled here. But I just thought it was worth to keep this in mind
as it can save a line or two

Try adding a datetime.datetime to a datetime.timedelta. If you only want the time portion, you can call the time() method on the resultant datetime.datetime object to get it.

Old question, but I figured I'd throw in a function that handles timezones. The key parts are passing the datetime.time object's tzinfo attribute into combine, and then using timetz() instead of time() on the resulting dummy datetime. This answer partly inspired by the other answers here.
def add_timedelta_to_time(t, td):
"""Add a timedelta object to a time object using a dummy datetime.
:param t: datetime.time object.
:param td: datetime.timedelta object.
:returns: datetime.time object, representing the result of t + td.
NOTE: Using a gigantic td may result in an overflow. You've been
warned.
"""
# Create a dummy date object.
dummy_date = date(year=100, month=1, day=1)
# Combine the dummy date with the given time.
dummy_datetime = datetime.combine(date=dummy_date, time=t, tzinfo=t.tzinfo)
# Add the timedelta to the dummy datetime.
new_datetime = dummy_datetime + td
# Return the resulting time, including timezone information.
return new_datetime.timetz()
And here's a really simple test case class (using built-in unittest):
import unittest
from datetime import datetime, timezone, timedelta, time
class AddTimedeltaToTimeTestCase(unittest.TestCase):
"""Test add_timedelta_to_time."""
def test_wraps(self):
t = time(hour=23, minute=59)
td = timedelta(minutes=2)
t_expected = time(hour=0, minute=1)
t_actual = add_timedelta_to_time(t=t, td=td)
self.assertEqual(t_expected, t_actual)
def test_tz(self):
t = time(hour=4, minute=16, tzinfo=timezone.utc)
td = timedelta(hours=10, minutes=4)
t_expected = time(hour=14, minute=20, tzinfo=timezone.utc)
t_actual = add_timedelta_to_time(t=t, td=td)
self.assertEqual(t_expected, t_actual)
if __name__ == '__main__':
unittest.main()

Related

How do I get seconds since 1/1/1970 of a Python datetime object?

I'm using Python 3.7 and Django. I wanted to get the number of seconds (or milliseconds) since 1/1/1970 for a datetime object. Following the advice here -- In Python, how do you convert a `datetime` object to seconds?, I implemented
now = datetime.now()
...
return [len(removed_elts) == 0, score, now.total_seconds()]
but the "now.total_seconds()" line is giving the error
AttributeError: 'datetime.datetime' object has no attribute 'total_seconds'
What's the right way to get the seconds since 1/1/1970?
now = datetime.now()
...
return [len(removed_elts) == 0, score, now.timestamp()]
This should work.
import datetime
first_date = datetime.datetime(1970, 1, 1)
time_since = datetime.datetime.now() - first_date
seconds = int(time_since.total_seconds())
import time
print(time.time())
Output:
1567532027.192546
In contrast to the advice you mentioned, you don't call total_seconds() on a timedelta object but on a datetime object, which simply doesn't have this attribute.
So, one solution for Python 3.7 (and 2.7) could for example be:
import datetime
now = datetime.now()
then = datetime.datetime(1970,1,1)
...
return [len(removed_elts) == 0, score, (now - then).total_seconds()]
Another shorter but less clear (at least on first sight) solution for Python 3.3+ (credits to ababak for this one):
import datetime
now = datetime.now()
...
return [len(removed_elts) == 0, score, now.timestamp()]
You can try:
import datetime
now = datetime.datetime.now()
delta = (now - datetime.datetime(1970,1,1))
print(delta.total_seconds())
now is of type datetime.datetime and has no .total_seconds() method.
delta is of type datetime.timedelta and does have a .total_seconds() method.
Hope this helps.

Python difference in datetime now against datetime now

I have got into an issue or might quite possibly feature in turn! Not sure, wondering!! In python's datetime library, to get difference in time, as in below snippet.
>>> import datetime
>>> datetime.datetime.now() - datetime.datetime.now()
datetime.timedelta(-1, 86399, 999958)
>>> tnow = datetime.datetime.now()
>>> datetime.datetime.now() - tnow
datetime.timedelta(0, 4, 327859)
I would like to understand why datetime.datetime.now() - datetime.datetime.now() is producing output as -1 days, 86399 seconds whereas assigning current time to some variable and computing difference gives desired output 0 days, 4 seconds.
The results seems to be bit confusing, it would be helpful if someone could decode whats going behind
Note: I'm using Python 2.7
As per the documentation of timedelta object
If the normalized value of days lies outside the indicated range,
OverflowError is raised.
Note that normalization of negative values may be surprising at first.
For example:
>>> from datetime import timedelta
>>> d = timedelta(microseconds=-1)
>>> (d.days, d.seconds, d.microseconds)
(-1, 86399, 999999)
This is valid for python 2.7 and 3 both.
Why this is happening is simple:
a , b = datetime.datetime.now(), datetime.datetime.now()
# here datetime.now() in a will be <= b.
# That is because they will be executed separately at different CPU clock cycle.
a - b
# datetime.timedelta(-1, 86399, 999973)
b - a
# datetime.timedelta(0, 0, 27)
To get the proper time difference:
(tnow - datetime.datetime.now()).total_seconds()
# output: -1.751166
This Answer gives more information on how to use time delta safely (handle negative values) Link
You are encountering a "corner case" situation.
Every datetime.datetime.now() produces a datetime.datetime object ([Python]: https://docs.python.org/3/library/datetime.html#datetime-objects), which is the current date & time at the moment the call was made
You have 2 such calls (even if they are on the same line). Since the CPU speeds are very high nowadays, every such call takes a very small amount of time (much less than microseconds, I presume)
But, when the 1st call is at the very end of a (microsecond?) period, and the 2nd one is at the beginning of the next one, you'd get this behavior:
>>> import datetime
>>> now0 = datetime.datetime.now()
>>> now0
datetime.datetime(2018, 2, 20, 12, 23, 23, 1000)
>>> delta = datetime.timedelta(microseconds=1)
>>> now1 = now0 + delta
>>> now0 - now1
datetime.timedelta(-1, 86399, 999999)
Explanation:
Let now0 to be the result of the 1st call made to datetime.datetime.now()
Let's say that the 2nd datetime.datetime.now() call happens one microsecond later (I am reproducing the behavior using the delta object, as the times involved here are waaay too small for me to be able to to run the line at the exact time when this behavior is encountered). That is placed into now1
When subtracting them you get the negative value (in my case is -delta), since now0 happened earlier than now1 (check [Python]: timedelta Objects for more details)

Easy way to increase one minute using strftime in python [duplicate]

Given a datetime.time value in Python, is there a standard way to add an integer number of seconds to it, so that 11:34:59 + 3 = 11:35:02, for example?
These obvious ideas don't work:
>>> datetime.time(11, 34, 59) + 3
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'int'
>>> datetime.time(11, 34, 59) + datetime.timedelta(0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'
>>> datetime.time(11, 34, 59) + datetime.time(0, 0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.time'
In the end I have written functions like this:
def add_secs_to_time(timeval, secs_to_add):
secs = timeval.hour * 3600 + timeval.minute * 60 + timeval.second
secs += secs_to_add
return datetime.time(secs // 3600, (secs % 3600) // 60, secs % 60)
I can't help thinking that I'm missing an easier way to do this though.
Related
python time + timedelta equivalent
You can use full datetime variables with timedelta, and by providing a dummy date then using time to just get the time value.
For example:
import datetime
a = datetime.datetime(100,1,1,11,34,59)
b = a + datetime.timedelta(0,3) # days, seconds, then other fields.
print(a.time())
print(b.time())
results in the two values, three seconds apart:
11:34:59
11:35:02
You could also opt for the more readable
b = a + datetime.timedelta(seconds=3)
if you're so inclined.
If you're after a function that can do this, you can look into using addSecs below:
import datetime
def addSecs(tm, secs):
fulldate = datetime.datetime(100, 1, 1, tm.hour, tm.minute, tm.second)
fulldate = fulldate + datetime.timedelta(seconds=secs)
return fulldate.time()
a = datetime.datetime.now().time()
b = addSecs(a, 300)
print(a)
print(b)
This outputs:
09:11:55.775695
09:16:55
As others here have stated, you can just use full datetime objects throughout:
from datetime import datetime, date, time, timedelta
sometime = time(8,00) # 8am
later = (datetime.combine(date.today(), sometime) + timedelta(seconds=3)).time()
However, I think it's worth explaining why full datetime objects are required. Consider what would happen if I added 2 hours to 11pm. What's the correct behavior? An exception, because you can't have a time larger than 11:59pm? Should it wrap back around?
Different programmers will expect different things, so whichever result they picked would surprise a lot of people. Worse yet, programmers would write code that worked just fine when they tested it initially, and then have it break later by doing something unexpected. This is very bad, which is why you're not allowed to add timedelta objects to time objects.
One little thing, might add clarity to override the default value for seconds
>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)
You cannot simply add number to datetime because it's unclear what unit is used: seconds, hours, weeks...
There is timedelta class for manipulations with date and time. datetime minus datetime gives timedelta, datetime plus timedelta gives datetime, two datetime objects cannot be added although two timedelta can.
Create timedelta object with how many seconds you want to add and add it to datetime object:
>>> from datetime import datetime, timedelta
>>> t = datetime.now() + timedelta(seconds=3000)
>>> print(t)
datetime.datetime(2018, 1, 17, 21, 47, 13, 90244)
There is same concept in C++: std::chrono::duration.
Thanks to #Pax Diablo, #bvmou and #Arachnid for the suggestion of using full datetimes throughout. If I have to accept datetime.time objects from an external source, then this seems to be an alternative add_secs_to_time() function:
def add_secs_to_time(timeval, secs_to_add):
dummy_date = datetime.date(1, 1, 1)
full_datetime = datetime.datetime.combine(dummy_date, timeval)
added_datetime = full_datetime + datetime.timedelta(seconds=secs_to_add)
return added_datetime.time()
This verbose code can be compressed to this one-liner:
(datetime.datetime.combine(datetime.date(1, 1, 1), timeval) + datetime.timedelta(seconds=secs_to_add)).time()
but I think I'd want to wrap that up in a function for code clarity anyway.
If it's worth adding another file / dependency to your project, I've just written a tiny little class that extends datetime.time with the ability to do arithmetic. When you go past midnight, it wraps around zero. Now, "What time will it be, 24 hours from now" has a lot of corner cases, including daylight savings time, leap seconds, historical timezone changes, and so on. But sometimes you really do need the simple case, and that's what this will do.
Your example would be written:
>>> import datetime
>>> import nptime
>>> nptime.nptime(11, 34, 59) + datetime.timedelta(0, 3)
nptime(11, 35, 2)
nptime inherits from datetime.time, so any of those methods should be usable, too.
It's available from PyPi as nptime ("non-pedantic time"), or on GitHub: https://github.com/tgs/nptime
For completeness' sake, here's the way to do it with arrow (better dates and times for Python):
sometime = arrow.now()
abitlater = sometime.shift(seconds=3)
In a real world environment it's never a good idea to work solely with time, always use datetime, even better utc, to avoid conflicts like overnight, daylight saving, different timezones between user and server etc.
So I'd recommend this approach:
import datetime as dt
_now = dt.datetime.now() # or dt.datetime.now(dt.timezone.utc)
_in_5_sec = _now + dt.timedelta(seconds=5)
# get '14:39:57':
_in_5_sec.strftime('%H:%M:%S')
If you don't already have a timedelta object, another possibility would be to just initialize a new time object instead with the attributes of the old one and add values where needed:
new_time:time = time(
hour=curr_time.hour + n_hours,
minute=curr_time.minute + n_minutes,
seconds=curr_time.second + n_seconds
)
Admittedly this only works if you make a few assumptions about your values, since overflow is not handled here. But I just thought it was worth to keep this in mind
as it can save a line or two
Try adding a datetime.datetime to a datetime.timedelta. If you only want the time portion, you can call the time() method on the resultant datetime.datetime object to get it.
Old question, but I figured I'd throw in a function that handles timezones. The key parts are passing the datetime.time object's tzinfo attribute into combine, and then using timetz() instead of time() on the resulting dummy datetime. This answer partly inspired by the other answers here.
def add_timedelta_to_time(t, td):
"""Add a timedelta object to a time object using a dummy datetime.
:param t: datetime.time object.
:param td: datetime.timedelta object.
:returns: datetime.time object, representing the result of t + td.
NOTE: Using a gigantic td may result in an overflow. You've been
warned.
"""
# Create a dummy date object.
dummy_date = date(year=100, month=1, day=1)
# Combine the dummy date with the given time.
dummy_datetime = datetime.combine(date=dummy_date, time=t, tzinfo=t.tzinfo)
# Add the timedelta to the dummy datetime.
new_datetime = dummy_datetime + td
# Return the resulting time, including timezone information.
return new_datetime.timetz()
And here's a really simple test case class (using built-in unittest):
import unittest
from datetime import datetime, timezone, timedelta, time
class AddTimedeltaToTimeTestCase(unittest.TestCase):
"""Test add_timedelta_to_time."""
def test_wraps(self):
t = time(hour=23, minute=59)
td = timedelta(minutes=2)
t_expected = time(hour=0, minute=1)
t_actual = add_timedelta_to_time(t=t, td=td)
self.assertEqual(t_expected, t_actual)
def test_tz(self):
t = time(hour=4, minute=16, tzinfo=timezone.utc)
td = timedelta(hours=10, minutes=4)
t_expected = time(hour=14, minute=20, tzinfo=timezone.utc)
t_actual = add_timedelta_to_time(t=t, td=td)
self.assertEqual(t_expected, t_actual)
if __name__ == '__main__':
unittest.main()

Subtract seconds from datetime in python

I have an int variable that are actually seconds (lets call that amount of seconds X). I need to get as result current date and time (in datetime format) minus X seconds.
Example
If X is 65 and current date is 2014-06-03 15:45:00, then I need to get the result 2014-06-03 15:43:45.
Environment
I'm doing this on Python 3.3.3 and I know I could probably use the datetime module but I haven't had any success so far.
Using the datetime module indeed:
import datetime
X = 65
result = datetime.datetime.now() - datetime.timedelta(seconds=X)
You should read the documentation of this package to learn how to use it!
Consider using dateutil.relativedelta, instead of datetime.timedelta.
>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2014, 6, 3, 22, 55, 9, 680637)
>>> now - relativedelta(seconds=15)
datetime.datetime(2014, 6, 3, 22, 54, 54, 680637)
In this case of a 15 seconds delta there is no advantage over using a stdlib timedelta, but relativedelta supports larger units such as months or years, and it may handle the general case with more correctness (consider for example special handling required for leap years and periods with daylight-savings transitions).
To expand on #julienc's answer,
(in case it is helpful to someone)
If you allow X to accept positive or negatives, and, change the subtraction statement to an addition statement, then you can have a more intuitive (so you don't have to add negatives to negatives to get positives) time adjusting feature like so:
def adjustTimeBySeconds(time, delta):
return time + datetime.timedelta(seconds=delta)
time = datetime.datetime.now()
X = -65
print(adjustTimeBySeconds(time, X))
X = 65
print(adjustTimeBySeconds(time, X))

Is there any way to use a strftime-like function for dates before 1900 in Python?

I didn't realize this, but apparently Python's strftime function doesn't support dates before 1900:
>>> from datetime import datetime
>>> d = datetime(1899, 1, 1)
>>> d.strftime('%Y-%m-%d')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: year=1899 is before 1900; the datetime strftime() methods require year >= 1900
I'm sure I could hack together something myself to do this, but I figure the strftime function is there for a reason (and there also is a reason why it can't support pre-1900 dates). I need to be able to support dates before 1900. I'd just use str, but there's too much variation. In other words, it may or may not have microseconds or it may or may not have a timezone. Is there any solution to this?
If it makes a difference, I'm doing this so that I can write the data to a text file and load it into a database using Oracle SQL*Loader.
I essentially ended up doing Alex Martelli's answer. Here's a more complete implementation:
>>> from datetime import datetime
>>> d = datetime.now()
>>> d = d.replace(microsecond=0, tzinfo=None)
>>> str(d)
'2009-10-29 11:27:27'
The only difference is that str(d) is equivalent to d.isoformat(' ').
isoformat works on datetime instances w/o limitation of range:
>>> import datetime
>>> x=datetime.datetime(1865, 7, 2, 9, 30, 21)
>>> x.isoformat()
'1865-07-02T09:30:21'
If you need a different-format string it's not too hard to slice, dice and remix pieces of the string you get from isoformat, which is very consistent (YYYY-MM-DDTHH:MM:SS.mmmmmm, with the dot and following microseconds omitted if microseconds are zero).
The documentation seems pretty clear about this:
The exact range of years for which strftime() works also varies across platforms. Regardless of platform, years before 1900 cannot be used.
So there isn't going to be a solution that uses strftime(). Luckily, it's pretty straightforward to do this "by hand":
>>> "%02d-%02d-%02d %02d:%02d" % (d.year,d.month,d.day,d.hour,d.minute)
'1899-01-01 00:00'
mxDateTime can handle arbitrary dates. Python's time and datetime modules use UNIX timestamps internally, that's why they have limited range.
In [5]: mx.DateTime.DateTime(1899)
Out[5]: <mx.DateTime.DateTime object for '1899-01-01 00:00:00.00' at 154a960>
In [6]: DateTime.DateTime(1899).Format('%Y-%m-%d')
Out[6]: 1899-01-01
This is from the matplotlib source. Could provide a good starting point for rolling your own.
def strftime(self, dt, fmt):
fmt = self.illegal_s.sub(r"\1", fmt)
fmt = fmt.replace("%s", "s")
if dt.year > 1900:
return cbook.unicode_safe(dt.strftime(fmt))
year = dt.year
# For every non-leap year century, advance by
# 6 years to get into the 28-year repeat cycle
delta = 2000 - year
off = 6*(delta // 100 + delta // 400)
year = year + off
# Move to around the year 2000
year = year + ((2000 - year)//28)*28
timetuple = dt.timetuple()
s1 = time.strftime(fmt, (year,) + timetuple[1:])
sites1 = self._findall(s1, str(year))
s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
sites2 = self._findall(s2, str(year+28))
sites = []
for site in sites1:
if site in sites2:
sites.append(site)
s = s1
syear = "%4d" % (dt.year,)
for site in sites:
s = s[:site] + syear + s[site+4:]
return cbook.unicode_safe(s)
This is the "feature" of the ctime library (UTF).
Also You may have problem above 2038.

Categories