0

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?

drjrm3
  • 4,474
  • 10
  • 53
  • 91

5 Answers5

1

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
rafaelc
  • 57,686
  • 15
  • 58
  • 82
  • Nice. I can put in test cases for finding `unit`, but is there a way to represent `hours` as a string or does it have to be hardcoded as `hours`? – drjrm3 May 12 '15 at 18:42
  • Where? in `timedelta` argument? That is a keyword, it has to be written as so – rafaelc May 12 '15 at 18:43
  • @Laurbert515 `datetime.timedelta(**{unit: start_time})` - although you'll need to ensure that the value of `unit` matches the expected argument name, so `"hours"` rather than `"hour"` – RoadieRich May 12 '15 at 18:45
1

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'

jgritty
  • 11,660
  • 3
  • 38
  • 60
  • And then use datetime.timedelta to figure out the start time and end times in datetime format so you can compare them. http://pymotw.com/2/datetime/ – noisygecko May 12 '15 at 18:39
1

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
farhawa
  • 10,120
  • 16
  • 49
  • 91
1

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
Andy
  • 49,085
  • 60
  • 166
  • 233
  • local time may be ambiguous. Don't compare naive datetime objects that represent local time directly. [Use UTC time instead or aware datetime objects instead](http://stackoverflow.com/a/30207195/4279) – jfs May 13 '15 at 06:35
0

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
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670