Find the closest hour - python

I have a list with these items:
hours = ['19:30', '20:10', '20:30', '21:00', '22:00']
Assuming that now it's 20:18, how can I get the '20:10' item from list? I want to use this to find the current running show in a TV Guide.

>>> import datetime
>>> hours = ['19:30', '20:10', '20:30', '21:00', '22:00']
>>> now = datetime.datetime.strptime("20:18", "%H:%M")
>>> min(hours, key=lambda t: abs(now - datetime.datetime.strptime(t, "%H:%M")))
'20:10'

easy but dirty way
max(t for t in sorted(hours) if t<=now)

I'm not a Python programmer, but I'd use the following algorithm:
Convert everything to "minutes after midnight", e.g. hours = [1170 (= 19*60+30), 1210, ...], currenttime = 1218 (= 20*60+18).
Then just loop thorugh hours and find the last entry which is smaller than currenttime.

You can use functions in the time module; time.strptime() allows you to parse a string into a time-tuple, then time.mktime() converts this to seconds. You can then simply compare all items in seconds, and find the smallest difference.

#katrielalex & Tim
import itertools
[x for x in itertools.takewhile( lambda t: now > datetime.datetime.strptime(t, "%H:%M"), hours )][-1]

import bisect
# you can use the time module like katrielalex answer which a standard library
# in python, but sadly for me i become an addict to dateutil :)
from dateutil import parser
hour_to_get = parser.parse('20:18')
hours = ['19:30', '20:10', '20:30', '21:00', '22:00']
hours = map(parser.parse, hours) # Convert to datetime.
hours.sort() # In case the list of hours isn't sorted.
index = bisect.bisect(hours, hour_to_get)
if index in (0, len(hours) - 1):
print "there is no show running at the moment"
else:
print "running show started at %s " % hours[index-1]
Hope this can help you :)

Related

Add time values in Python

I have a list of times that are in following format:
Hour:Minue:Second.Microseconds
File looks like this:
0:06:50.137529
0:08:55.439963
0:06:19.179093
0:07:16.680906
0:31:55.778010
0:16:56.940836
Is there a Python function or set of commands that will let me add all of these values together?
I initially "build" these values with the following code:
optimize_times = []
starting_time=(datetime.now())
ending_time=(datetime.now())
optimize_times.append(str(ending_time-starting_time))
You can use datetime.timedelta from the standard library:
from datetime import timedelta
L = ['0:06:50.137529', '0:08:55.439963', '0:06:19.179093',
'0:07:16.680906', '0:31:55.778010', '0:16:56.940836']
def str_to_td(x):
hrs, mins, sec_micro = x.split(':')
secs, msecs = map(int, sec_micro.split('.'))
return timedelta(hours=int(hrs), minutes=int(mins), seconds=secs, microseconds=msecs)
res = sum(map(str_to_td, L), timedelta())
# datetime.timedelta(0, 4694, 156337)
Note the output of this is a timedelta object. If this isn't the format your desire, you'll need to convert back to a string with additional logic.

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 convert datetime to integer in python

How can I convert YYYY-MM-DD hh:mm:ss format to integer in python?
for example 2014-02-12 20:51:14 -> to integer.
I only know how to convert hh:mm:ss but not yyyy-mm-dd hh:mm:ss
def time_to_num(time_str):
hh, mm , ss = map(int, time_str.split(':'))
return ss + 60*(mm + 60*hh)
It depends on what the integer is supposed to encode. You could convert the date to a number of milliseconds from some previous time. People often do this affixed to 12:00 am January 1 1970, or 1900, etc., and measure time as an integer number of milliseconds from that point. The datetime module (or others like it) will have functions that do this for you: for example, you can use int(datetime.datetime.utcnow().timestamp()).
If you want to semantically encode the year, month, and day, one way to do it is to multiply those components by order-of-magnitude values large enough to juxtapose them within the integer digits:
2012-06-13 --> 20120613 = 10,000 * (2012) + 100 * (6) + 1*(13)
def to_integer(dt_time):
return 10000*dt_time.year + 100*dt_time.month + dt_time.day
E.g.
In [1]: import datetime
In [2]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:def to_integer(dt_time):
: return 10000*dt_time.year + 100*dt_time.month + dt_time.day
: # Or take the appropriate chars from a string date representation.
:--
In [3]: to_integer(datetime.date(2012, 6, 13))
Out[3]: 20120613
If you also want minutes and seconds, then just include further orders of magnitude as needed to display the digits.
I've encountered this second method very often in legacy systems, especially systems that pull date-based data out of legacy SQL databases.
It is very bad. You end up writing a lot of hacky code for aligning dates, computing month or day offsets as they would appear in the integer format (e.g. resetting the month back to 1 as you pass December, then incrementing the year value), and boiler plate for converting to and from the integer format all over.
Unless such a convention lives in a deep, low-level, and thoroughly tested section of the API you're working on, such that everyone who ever consumes the data really can count on this integer representation and all of its helper functions, then you end up with lots of people re-writing basic date-handling routines all over the place.
It's generally much better to leave the value in a date context, like datetime.date, for as long as you possibly can, so that the operations upon it are expressed in a natural, date-based context, and not some lone developer's personal hack into an integer.
I think I have a shortcut for that:
# Importing datetime.
from datetime import datetime
# Creating a datetime object so we can test.
a = datetime.now()
# Converting a to string in the desired format (YYYYMMDD) using strftime
# and then to int.
a = int(a.strftime('%Y%m%d'))
This in an example that can be used for example to feed a database key, I sometimes use instead of using AUTOINCREMENT options.
import datetime
dt = datetime.datetime.now()
seq = int(dt.strftime("%Y%m%d%H%M%S"))
The other answers focused on a human-readable representation with int(mydate.strftime("%Y%m%d%H%M%S")). But this makes you lose a lot, including normal integer semantics and arithmetics, therefore I would prefer something like bash date's "seconds since the epoch (1970-01-01 UTC)".
As a reference, you could use the following bash command to get 1392234674 as a result:
date +%s --date="2014-02-12 20:51:14"
As ely hinted in the accepted answer, just a plain number representation is unmistakeable and by far easier to handle and parse, especially programmatically. Plus conversion from and to human-readable is an easy oneliner both ways.
To do the same thing in python, you can use datetime.timestamp() as djvg commented. For other methods you can consider the edit history.
Here is a simple date -> second conversion tool:
def time_to_int(dateobj):
total = int(dateobj.strftime('%S'))
total += int(dateobj.strftime('%M')) * 60
total += int(dateobj.strftime('%H')) * 60 * 60
total += (int(dateobj.strftime('%j')) - 1) * 60 * 60 * 24
total += (int(dateobj.strftime('%Y')) - 1970) * 60 * 60 * 24 * 365
return total
(Effectively a UNIX timestamp calculator)
Example use:
from datetime import datetime
x = datetime(1970, 1, 1)
time_to_int(x)
Output: 0
x = datetime(2021, 12, 31)
time_to_int(x)
Output: 1639785600
x = datetime(2022, 1, 1)
time_to_int(x)
Output: 1639872000
x = datetime(2022, 1, 2)
time_to_int(x)
Output: 1639958400
When converting datetime to integers one must keep in mind the tens, hundreds and thousands.... like
"2018-11-03" must be like 20181103 in int
for that you have to
2018*10000 + 100* 11 + 3
Similarly another example,
"2018-11-03 10:02:05" must be like 20181103100205 in int
Explanatory Code
dt = datetime(2018,11,3,10,2,5)
print (dt)
#print (dt.timestamp()) # unix representation ... not useful when converting to int
print (dt.strftime("%Y-%m-%d"))
print (dt.year*10000 + dt.month* 100 + dt.day)
print (int(dt.strftime("%Y%m%d")))
print (dt.strftime("%Y-%m-%d %H:%M:%S"))
print (dt.year*10000000000 + dt.month* 100000000 +dt.day * 1000000 + dt.hour*10000 + dt.minute*100 + dt.second)
print (int(dt.strftime("%Y%m%d%H%M%S")))
General Function
To avoid that doing manually use below function
def datetime_to_int(dt):
return int(dt.strftime("%Y%m%d%H%M%S"))
df.Date = df.Date.str.replace('-', '').astype(int)

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))

How to find two close events (that have a datetime component) in python

I have an object that represents an event, and I have many types of those objects.
I want to find events that are close in time in python.
The nicest way I could find to do it is:
joined_events = [[event_a.time, event_a.description, event_b.time, event_b.description] for event_a in events_a for event_b in events_b if abs(event_a.time - event_b.time) < datetime.timedelta(hours = 1)]
But I hoped there was a better method of doing it.
Beauty is in the eye of the beholder, but hopefully the is a little less ugly to you :)
import datetime
from itertools import product
delta = datetime.timedelta(hours=1)
joined_events = [
[a.time, a.description, b.time, b.description]
for a, b in product(events_a, events_b) if abs(a.time - b.time) < delta
]
I couldn't find a better solution so I wrote one:
https://github.com/tomirendo/Grouper
Its main purpose is to group object by their relation and replace the itertools.groupby function.
import datetime,random,Grouper
groups_of_dates = Grouper.groupby(list_of_dates,relation= Grouper.time_delta(hours = 3),as_iterable=False)
#Get a list of lists, grouping
#consecutive events that are less than 3 hours apart.
To solve the problem I had, You can do this:
results = [[a,b]
for a,b in itertools.product(events_a ,events_b)
if Grouper.time_delta(attr="time",hours = 3)(a,b)]
For a more general solution, you can use the AND,OR and difference methods:
groups_of_dates = Grouper.groupby(list_of_dates,
relation= Grouper.AND(Grouper.time_delta(hours = 3),
Grouper.difference(3,attr="loc")),
as_iterable=False)

Categories

Resources