drjrm3
drjrm3

Reputation: 4728

Compare times with python

I have data in a file with dates marked, for example, '2015.05.05-11:46', and want to read these lines and then see if they fulfill certain conditions. For example, as input to the function, I may have

get_times('hour', -3.0, -1.2)

which has function defintion:

get_times(unit, start_time, end_time):

which means I want to return all strings that are from -3.0 hours in the past to -1.2 hours in the past. I can get the current time with now = datetime.datetime.now(). Assuming I read in time1 = '2015.05.05-11:46', how do I compare that to now and find out if it is within start_time and end_time units from now?

Upvotes: 0

Views: 4224

Answers (5)

jfs
jfs

Reputation: 414865

To compare time from the file, you should convert it to UTC time (POSIX timestamp) or an aware datetime object (local time + utc offset).

start_time, end_time = get_times('hour', -3, -1.2)
if start_time <= utc_time < end_time:
   # utc_time is in between

You should not use start_time <= naive_local_time < end_time. Convert input time to UTC or create an aware datetime objects instead.

If local times in your input file are consecutive then you could use that fact to disambiguate the timestamps if necessary, see Parsing of Ordered Timestamps in Local Time (to UTC) While Observing Daylight Saving Time.

More explanation and solutions that use time.mktime(), pytz, aware datetime objects are in: Find if 24 hrs have passed between datetimes - Python.

Why you should not use datetime.now()

datetime.now() returns local time as a naive datetime object may be ambiguous e.g., during a DST transition:

>>> from datetime import datetime
>>> import pytz
>>> tz = pytz.timezone('America/New_York')
>>> tz.localize(datetime(2015,11,1,1,30), is_dst=None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/pytz/tzinfo.py", line 349, in localize
    raise AmbiguousTimeError(dt)
pytz.exceptions.AmbiguousTimeError: 2015-11-01 01:30:00
>>> tz.localize(datetime(2015,11,1,1,30), is_dst=True).astimezone(pytz.utc)
datetime.datetime(2015, 11, 1, 5, 30, tzinfo=<UTC>)
>>> tz.localize(datetime(2015,11,1,1,30), is_dst=False).astimezone(pytz.utc)
datetime.datetime(2015, 11, 1, 6, 30, tzinfo=<UTC>)

Note: if you remove UTC offset then the same local time may correspond to different UTC time. datetime.utcnow() is unambiguous (except perhaps during a leap second such as 2015-06-30T23:59:60Z).

How to implement get_times('hour', -3.0, -1.2)

Use UTC time or aware datetime objects:

#!/usr/bin/env python
from datetime import datetime, timedelta

def get_times(unit, relative_start, relative_end):
    relative_start, relative_end = [timedelta(**{unit+'s': v})
                                    for v in [relative_start, relative_end]]
    now = datetime.utcnow() # or datetime.now(timezone.utc).astimezone()
    return now + relative_start, now + relative_end

Upvotes: 0

farhawa
farhawa

Reputation: 10417

hope this is what you are looking for:

import datetime
import time


def get_times(unit, start_time, end_time):
    now = datetime.datetime.now().timetuple()
    matched_dates = []
    for date in your_file:
        converted_date = time.strptime(date,"%Y.%m.%d-%H:%M")
        if converted_date.tm_hour > (now.tm_hour + start_time) and converted_date.tm_hour < (now.tm_hour + end_time):
            matched_dates.append(date)
    return matched_dates

Upvotes: 1

Andy
Andy

Reputation: 50630

A couple things need to be done in your situation. First, you need to convert your datetime strings to datetime objects for easy comparison. We do this via strptime:

input_datetime = datetime.datetime.strptime(i, '%Y.%m.%d-%H:%M')

We also need the function to return start and end times based on your input. If you can make a slight modification and utilize hours instead of hour, we can do this without setting up a large if/elif block.

def get_times(unit, start_time, end_time):
    now = datetime.datetime.now()
    start_kwarg = {unit: start_time}
    end_kwarg = {unit: end_time}
    time_start = now + datetime.timedelta(**start_kwarg)
    time_end = now + datetime.timedelta(**end_kwarg)
    return time_start, time_end

This takes your unit and creates a dictionary that is passed as a keyword argument to timedelta. Since hours is one of the arguments it accepts, we can utilize the keyword instead of mapping hour to hours. Then we return start and end time.

Finally, we just need to compare that the input time is between start and end:

start < input_datetime < end

A final script could look like this:

import datetime

def get_times(unit, start_time, end_time):
    now = datetime.datetime.now()
    start_kwarg = {unit: start_time}
    end_kwarg = {unit: end_time}
    time_start = now + datetime.timedelta(**start_kwarg)
    time_end = now + datetime.timedelta(**end_kwarg)
    return time_start, time_end

start, end = get_times('hours', -3.0, -1.2)

input_times = [
    '2015.05.12-11:46',
    '2014.05.12-11:46',
    '2016.05.12-11:46',
    '2015.04.12-11:46',
    '2015.05.05-11:46'
    ]

for i in input_times:
    input_datetime = datetime.datetime.strptime(i, '%Y.%m.%d-%H:%M')
    print "{} => {}".format(input_datetime, start < input_datetime < end)

Output would look like this (if run at 12:46pm on 2015-05-12):

2015-05-12 11:46:00 => True
2014-05-12 11:46:00 => False
2016-05-12 11:46:00 => False
2015-04-12 11:46:00 => False
2015-05-05 11:46:00 => False

Upvotes: 1

jgritty
jgritty

Reputation: 11935

Use datetime.strptime to convert your string '2015.05.05-11:46'

then = datetime.datetime.strptime('2015.05.05-11:46', "%Y.%m.%d-%H:%M")

now = datetime.datetime.now()

Then use datetime.timedelta to compare times.

tdelta = now - then

if datetime.timedelta(hours=1.2) < tdelta < datetime.timedelta(hours=3.0):
    print "In range"

For writing your function, you'll probably want to stick to the units that are in datetime.timedelta, unless you have a good reason not to.

class datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]])

So, 'days', 'seconds', 'microseconds', 'milliseconds', 'minutes', 'hours', 'weeks'

Upvotes: 1

rafaelc
rafaelc

Reputation: 59284

You can use < and > operators normally.
But for that to work, you have to make all data be datetime type.

For instance:

time_str = "2015.05.05-11:46"
reference_time = datetime.datetime.strptime(time_str, "%Y.%m.%d-%H:%M")

start_time = reference_time - datetime.timedelta(hours=3)
end_time = reference_time - datetime.timedelta(hours=1.2)
now = datetime.datetime.now()

if end_time <= now <= start_time:
    print 'It is in between'

You can also pass arguments to timedelta function using a dictionary:

>>> a = datetime.timedelta(hours=3, minutes=10)
>>> args = {'hours': 3, 'minutes': 10}
>>> b = datetime.timedelta(**args)
>>> a == b
True

Upvotes: 1

Related Questions