19

I have the following method:

# last_updated is a datetime() object, representing the last time this program ran
def time_diff(last_updated):
    day_period = last_updated.replace(day=last_updated.day + 1, 
                                      hour=1,
                                      minute=0,  
                                      second=0,
                                      microsecond=0)
    delta_time = day_period - last_updated
    hours = delta_time.seconds // 3600
    # make sure a period of 24hrs have passed before shuffling
    if hours >= 24:
        print "hello"
    else:
        print "do nothing"

I want to find out if 24 hrs have passed since last_updated, how can I do that in Python?

Georgy
  • 12,464
  • 7
  • 65
  • 73
cybertextron
  • 10,547
  • 28
  • 104
  • 208
  • 1
    what format is last updated in? – Padraic Cunningham Oct 11 '14 at 10:16
  • What is `last_updated`? Is it local time? Do you want to find out whether more than 24 hours passed or the current time on the next day is larger e.g., last_updated=`Nov 1, 7pm` and the current time is `Nov 2, 6:30pm` (it is more than 24 hours, but 6:30pm is less than 7pm) (in New York). – jfs Oct 11 '14 at 10:19
  • @PadraicCunningham I've updated the question ... last_updated is a datetime() that tells when the program was last updated. All I need to now if 24hrs have passed since last_updated (from now()) – cybertextron Oct 11 '14 at 10:32
  • @J.F.Sebastian see comment to PadraicCunningham – cybertextron Oct 11 '14 at 10:33
  • Related: [How can I subtract a day from a python date?](http://stackoverflow.com/a/25427822/4279) – jfs Oct 11 '14 at 10:37
  • What is the timezone? Can you get `last_updated` in UTC or as a Unix timestamp? Can you use 3rd party modules? Do you need to support Windows? Do you care if your code will be wrong twice a year by an hour or so? – jfs Oct 11 '14 at 10:38

2 Answers2

26

If last_updated is a naive datetime object representing the time in UTC:

from datetime import datetime, timedelta

if (datetime.utcnow() - last_updated) > timedelta(hours=24): 
    # more than 24 hours passed

If last_updated is the local time (naive (timezone-unaware) datetime object):

import time

DAY = 86400
now = time.time()
then = time.mktime(last_updated.timetuple())
if (now - then) > DAY:
    # more than 24 hours passed

If last_updated is an ambiguous time e.g., the time during an end-of-DST transition (once a year in many timezones) then there is a fifty-fifty chance that mktime() returns a wrong result (e.g., off by an hour).

time.mktime() may also fail if C time library doesn't use a historical timezone database on a given platform and the UTC offset for the local timezone was different at last_updated time compared to now. It may apply to more than a third of all timezones in the last year. Linux, OS X, the recent versions of Windows have the tz database (I don't know whether old Windows versions would work for such past dates).

Beware: it might be tempting to write datetime.now() - last_updated (similar to the UTC case) but it is guaranteed to fail on all platforms if the UTC offset was different at last_updated time (it is possible in many timezones). mktime()-based solution can utilize the tz database at least on some platforms and therefore it can handle the changes in the UTC offset for whatever reason there.

For portability, you could install the tz database. It is provided by pytz module in Python. tzlocal can return pytz timezone corresponding to the local timezone:

from datetime import datetime, timedelta
from tzlocal import get_localzone # $ pip install tzlocal

tz = get_localzone() # local timezone
then = tz.normalize(tz.localize(last_updated)) # make it timezone-aware
now = datetime.now(tz) # timezone-aware current time in the local timezone
if (now - then) > timedelta(hours=24):
    # more than 24 hours passed

It works even if the UTC offset was different in the past. But it can't (as well as time.mktime()) fix ambiguous times (tz.localize() picks is_dst=False time by default). tz.normalize() is called to adjust non-existing times e.g., those that correspond to a start-of-DST transition (it should not affect the result).

The above code assumes that last_updated is a naive datetime object (no associated timezone info). If last_updated is an aware datetime object then it is easy to convert it to UTC:

from datetime import datetime, timedelta

then_in_utc = last_updated.replace(tzinfo=None) - last_updated.utcoffset()
if (datetime.utcnow() - then_in_utc) > timedelta(hours=24):
    # more than 24 hours passed

General note: you should understand now why people recommend to work with UTC time and to use local time only for display.

alextsil
  • 504
  • 3
  • 11
  • 26
jfs
  • 399,953
  • 195
  • 994
  • 1,670
2

Just to clear our some things, because I don't think all of us make use of the time Python lib. When you use datetime, which is especially in Django a very common practice, if you do the comparison like this:

if (now - then) > DAY:

it will hugely fail. This is because you can't compare datetime.timedelta to int.

The solution to this is to convert your objects to seconds.
For example:

from datetime import datetime

then = datetime_object
now = datetime.now()

if (now - then).total_seconds() > NUMBER_OF_SECONDS:
    # do something

Hope I helped out whoever faced issues on that.
Cheers

Kostas Livieratos
  • 965
  • 14
  • 33
  • 1. notice: `timedelta(1)` in my answer 2. notice: what my answer says about `datetime.now()` and why it should not be used. 3. django corresponds to the last code example in my answer with timezone-aware datetime objects (simpler case) -- make sure `USE_TZ=True` and use `timezone.now()` instead of `datetime.now()`. – jfs Dec 08 '15 at 20:59
  • 2
    sure, your answer is 100% correct and valuable, I just wanted to point out the `total_seconds()` usage, when needed. I totally agree with the `timezone.now()` btw. – Kostas Livieratos Dec 09 '15 at 21:07