python function to find difference between 2 datetime in python - python

I want to write a function that can get an input like "2022-09-12 13:18:36,270" and then calculate difference between input and now.
def compute_target_time(date_time):
reach_time = datetime.datetime.utcnow() - datetime.timedelta(hours=int(date_time))
return reach_time
but it doesn't work. can you help me?

def compute_target_time(date_time):
reach_time = datetime.datetime.utcnow() - datetime.datetime.strptime(date_time, '%Y-%m-%d %H:%M:%S,%f')
return reach_time

Related

How to get ceiling of seconds in a timedelta object [duplicate]

Currently I am logging stuff and I am using my own formatter with a custom formatTime():
def formatTime(self, _record, _datefmt):
t = datetime.datetime.now()
return t.strftime('%Y-%m-%d %H:%M:%S.%f')
My issue is that the microseconds, %f, are six digits. Is there anyway to spit out less digits, like the first three digits of the microseconds?
The simplest way would be to use slicing to just chop off the last three digits of the microseconds:
def format_time():
t = datetime.datetime.now()
s = t.strftime('%Y-%m-%d %H:%M:%S.%f')
return s[:-3]
I strongly recommend just chopping. I once wrote some logging code that rounded the timestamps rather than chopping, and I found it actually kind of confusing when the rounding changed the last digit. There was timed code that stopped running at a certain timestamp yet there were log events with that timestamp due to the rounding. Simpler and more predictable to just chop.
If you want to actually round the number rather than just chopping, it's a little more work but not horrible:
def format_time():
t = datetime.datetime.now()
s = t.strftime('%Y-%m-%d %H:%M:%S.%f')
head = s[:-7] # everything up to the '.'
tail = s[-7:] # the '.' and the 6 digits after it
f = float(tail)
temp = "{:.03f}".format(f) # for Python 2.x: temp = "%.3f" % f
new_tail = temp[1:] # temp[0] is always '0'; get rid of it
return head + new_tail
Obviously you can simplify the above with fewer variables; I just wanted it to be very easy to follow.
As of Python 3.6 the language has this feature built in:
def format_time():
t = datetime.datetime.now()
s = t.isoformat(timespec='milliseconds')
return s
This method should always return a timestamp that looks exactly like this (with or without the timezone depending on whether the input dt object contains one):
2016-08-05T18:18:54.776+0000
It takes a datetime object as input (which you can produce with datetime.datetime.now()). To get the time zone like in my example output you'll need to import pytz and pass datetime.datetime.now(pytz.utc).
import pytz, datetime
time_format(datetime.datetime.now(pytz.utc))
def time_format(dt):
return "%s:%.3f%s" % (
dt.strftime('%Y-%m-%dT%H:%M'),
float("%.3f" % (dt.second + dt.microsecond / 1e6)),
dt.strftime('%z')
)
I noticed that some of the other methods above would omit the trailing zero if there was one (e.g. 0.870 became 0.87) and this was causing problems for the parser I was feeding these timestamps into. This method does not have that problem.
An easy solution that should work in all cases:
def format_time():
t = datetime.datetime.now()
if t.microsecond % 1000 >= 500: # check if there will be rounding up
t = t + datetime.timedelta(milliseconds=1) # manually round up
return t.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
Basically you do manual rounding on the date object itself first, then you can safely trim the microseconds.
Edit: As some pointed out in the comments below, the rounding of this solution (and the one above) introduces problems when the microsecond value reaches 999500, as 999.5 is rounded to 1000 (overflow).
Short of reimplementing strftime to support the format we want (the potential overflow caused by the rounding would need to be propagated up to seconds, then minutes, etc.), it is much simpler to just truncate to the first 3 digits as outlined in the accepted answer, or using something like:
'{:03}'.format(int(999999/1000))
-- Original answer preserved below --
In my case, I was trying to format a datestamp with milliseconds formatted as 'ddd'. The solution I ended up using to get milliseconds was to use the microsecond attribute of the datetime object, divide it by 1000.0, pad it with zeros if necessary, and round it with format. It looks like this:
'{:03.0f}'.format(datetime.now().microsecond / 1000.0)
# Produces: '033', '499', etc.
You can subtract the current datetime from the microseconds.
d = datetime.datetime.now()
current_time = d - datetime.timedelta(microseconds=d.microsecond)
This will turn 2021-05-14 16:11:21.916229 into 2021-05-14 16:11:21
This method allows flexible precision and will consume the entire microsecond value if you specify too great a precision.
def formatTime(self, _record, _datefmt, precision=3):
dt = datetime.datetime.now()
us = str(dt.microsecond)
f = us[:precision] if len(us) > precision else us
return "%d-%d-%d %d:%d:%d.%d" % (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, int(f))
This method implements rounding to 3 decimal places:
import datetime
from decimal import *
def formatTime(self, _record, _datefmt, precision='0.001'):
dt = datetime.datetime.now()
seconds = float("%d.%d" % (dt.second, dt.microsecond))
return "%d-%d-%d %d:%d:%s" % (dt.year, dt.month, dt.day, dt.hour, dt.minute,
float(Decimal(seconds).quantize(Decimal(precision), rounding=ROUND_HALF_UP)))
I avoided using the strftime method purposely because I would prefer not to modify a fully serialized datetime object without revalidating it. This way also shows the date internals in case you want to modify it further.
In the rounding example, note that the precision is string-based for the Decimal module.
Here is my solution using regexp:
import re
# Capture 6 digits after dot in a group.
regexp = re.compile(r'\.(\d{6})')
def to_splunk_iso(dt):
"""Converts the datetime object to Splunk isoformat string."""
# 6-digits string.
microseconds = regexp.search(dt.isoformat()).group(1)
return regexp.sub('.%d' % round(float(microseconds) / 1000), dt.isoformat())
Fixing the proposed solution based on Pablojim Comments:
from datetime import datetime
dt = datetime.now()
dt_round_microsec = round(dt.microsecond/1000) #number of zeroes to round
dt = dt.replace(microsecond=dt_round_microsec)
If once want to get the day of the week (i.e, 'Sunday)' along with the result, then by slicing '[:-3]' will not work. At that time you may go with,
dt = datetime.datetime.now()
print("{}.{:03d} {}".format(dt.strftime('%Y-%m-%d %I:%M:%S'), dt.microsecond//1000, dt.strftime("%A")))
#Output: '2019-05-05 03:11:22.211 Sunday'
%H - for 24 Hour format
%I - for 12 Hour format
Thanks,
Adding my two cents here as this method will allow you to write your microsecond format as you would a float in c-style. It takes advantage that they both use %f.
import datetime
import re
def format_datetime(date, format):
"""Format a ``datetime`` object with microsecond precision.
Pass your microsecond as you would format a c-string float.
e.g "%.3f"
Args:
date (datetime.datetime): You input ``datetime`` obj.
format (str): Your strftime format string.
Returns:
str: Your formatted datetime string.
"""
# We need to check if formatted_str contains "%.xf" (x = a number)
float_format = r"(%\.\d+f)"
has_float_format = re.search(float_format, format)
if has_float_format:
# make microseconds be decimal place. Might be a better way to do this
microseconds = date.microsecond
while int(microseconds): # quit once it's 0
microseconds /= 10
ms_str = has_float_format.group(1) % microseconds
format = re.sub(float_format, ms_str[2:], format)
return date.strftime(format)
print(datetime.datetime.now(), "%H:%M:%S.%.3f")
# '17:58:54.424'

How to add zero to a time

I have a function that receives a time, in a string, and another number to add to the time:
def get_hours(hm):
return int(hm.split(':')[0])
def get_minutes(hm):
return int(hm.split(':')[1])
def add_minutes(hm, incr):
"""Increment the given time by the given amount of minutes.
Requires:
- hm str with a time represented as HH:MM;
- incr int with the number of minutes.
Ensures: str with a time represented as HH:MM, the result of incrementing hm by incr minutes.
"""
if get_minutes(hm)+incr>=60:
if get_hours(hm)==23:
return '00:'+str((get_minutes(hm)+incr)-60)
else:
return str(get_hours(hm)+1)+':'+str((get_minutes(hm)+incr)-60)
else:
return str(get_hours(hm))+':'+str((get_minutes(hm)+incr))
The problem is when the time is 16:05 or 04:06, it doesn't display the zeros, so it's just 16:5 and 4:6. How can i make this work?
Python has a function on the string class that may help you here;
"3".zfill(2) # this will output 03
"20".zfill(2) # this will output 20
Simply cast your ints to a string, and call .zfill(2) prior to printing any results.
Generally, when working with time you want to use the built-in modules.
import time
time.strftime('%H:%M')
'00:23'
Here is an example on how you can work with datetime:
import datetime
def add_minutes(t, incr):
return t + datetime.timedelta(minutes=incr)
# Create a datetime object
t = datetime.datetime.now() # 2017-12-05 01:29...
# Add minutes
t2 = add_minutes(t,5)
# Whenever you need to output use stftime:
print(t2.strftime("%H:%M"))
Prints:
01:34
If you aren't allowed to use standard datetime library then you could at worst modify add_minutes so that:
your_return_string = hours + ':' + minutes
if get_minutes() < 10:
your_return_string = hours + ':' + '0' + minutes
if get_hours() < 10:
your_return_string = '0' + your_return_string
Another solution may be
"%.2i:%.2i" % (get_hours(hm), get_minutes(hm))

Invalid format string in Python

I get the invalid format string error when I try to run the below code (last line), not sure where I am missing the point:
import datetime
DAYS = 2
SINCE = datetime.datetime.now() - datetime.timedelta(days=DAYS)
params = "?fields=feed.since(" + SINCE.strftime("%s") + ").limit(1),name,updated_time&"
Any suggestions would be much appreciated !!
You have to use "%S" because "%s" is not defined in the method you called : https://docs.python.org/2/library/datetime.html#datetime.strftime
import datetime
DAYS = 2
SINCE = datetime.datetime.now() - datetime.timedelta(days=DAYS)
params = "?fields=feed.since(" + SINCE.strftime("%S") + ").limit(1),name,updated_time&"
You should add what format you need for your application.
It really depends on which format suits you, but if you need timestamp use:
int(time.mktime(SINCE.timetuple()))
it works fine for me (Python 2.7). If it is part of a query and it is failing on that part, maybe you can use another date format like:
params = "?fields=feed.since(" + SINCE.strftime("%Y-%m-%d %H:%M:%S") + ").limit(1),name,updated_time&"
Please note that capital "S", will give you the seconds of that datetime object.

An elegant one-line solution to extract data from Nested Tuples from divmod

assuming the following code:
from datetime import datetime
start_time = datetime.now()
end_time = datetime.now()
delta = end_time - start_time
delta_ms = delta.microseconds
what is the most elegant way to simplify the conversion of the timedelta object to mins,secs and millisecs?
The one-liner I have below, requires two calculations of divmod(delta_ms,1000), is there any way, keeping it to one line, that it only needs to be calculated once?
mins,secs,ms = divmod(divmod(delta_ms,1000),60) + (divmod(delta_ms,1000)[1],)
I also have the following, which is two lines of code but only calculates divmod(delta_ms,1000) once:
unwrap = lambda x: divmod(x[0],60) + (x[1],)
mins,secs,ms = unwrap(divmod(delta_ms,1000))
First of all, you missed a [0] in your second divmod :) You might want to change that:
mins,secs,ms = divmod(divmod(delta_ms,1000)[0],60) + (divmod(delta_ms,1000)[1],)
Answering your question, if you are looking for elegant based on amount of lines it takes up, than your above solution would the most so. However, you are running divmod an extra, unnecessary time, which can be solved in one-line (much less elegant, uses ;):
var1, var2 = divmod(delta_ms,1000);mins,secs,ms = divmod(var1,60) + (var2,)
Or two lines:
var1, var2 = divmod(delta_ms,1000)
mins,secs,ms = divmod(var1,60) + (var2,)

How to get real three digits of microseconds in Python?

I'm trying to increase the time.
I want to get an hour format like this: 13:30:45,123 (in Java: "HH:mm:ss,SSS"), but Python displays 13:30:45,123456 ("%H:%M:%S,%f")(microseconds of 6 digits).
I read on the web and found possible solutions like:
from datetime import datetime
hour = datetime.utcnow().strftime('%H:%M:%S,%f')[:-3]
print(hour)
The output is: 04:33:16,123
But it's a bad solution, because if the hour is for example: 01:49:56,020706, the output is: 01:49:56,020, that the right should be: 01:49:56,021 (rounded).
The real purpose is that if I increase the milliseconds, even reaching rounds the seconds.
Example: (I want to increase 500 microseconds)
If the Input: 00:01:48,557, the Output should be: 00:01:49,057
The code of the program in Java (working good) is:
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss,SSS");
System.out.print("Input the time: ");
t1 = in.next();
Date d = df.parse(t1);
Calendar cal = Calendar.getInstance();
cal.setTime(d);
cal.add(Calendar.MILLISECOND, 500);//here increase the milliseconds (microseconds)
t2 = df.format(cal.getTime());
System.out.print("The Output (+500): "+t2);
I don't know if exists in Python something like SimpleDateFormat (in Java).
As to addition, you can add 500ms to your datetime object, using a timedelta object:
from datetime import datetime, timedelta
t1 = datetime.utcnow()
t2 = t1 + timedelta(milliseconds=500)
So as long as you're working with datetime objects instead of strings, you can easily do all the time-operations you'd like.
So we're left with the question of how to format the time when you want to display it.
As you pointed out, the [:-3]-trick seems to be the common solution, and seems to me it should work fine. If you really care about rounding correctly to the closest round millisecond, you can use the following "rounding trick":
You must have seen this trick in the past, for floats:
def round(x):
return int(x + 0.5)
The same idea (i.e. adding 0.5) can also be applied to datetimes:
def format_dt(t):
tr = t + timedelta(milliseconds=0.5)
return tr.strftime('%H:%M:%S,%f')[:-3]
You can round of digits using decimal
from decimal import Decimal
ts = datetime.utcnow()
sec = Decimal(ts.strftime('%S.%f'))
print ts.strftime('%H:%M:')+str(round(sec, 3))

Categories

Resources