Skip to content Skip to sidebar Skip to footer

How Do I Find The Next 7am In A Timezone

current_datetime = datetime.now(tz) next_hour = datetime(current_datetime.year, current_datetime.month, current_datetime.day, 7, 0, 0, 0, tz) timedelta_until_next_hour = next_hour

Solution 1:

Finding the next 7am

You can do this pretty easily with python-dateutil's relativedelta module:

from dateutil.relativedelta import relativedelta
defnext_7am(dt):
    relative_days = (dt.hour >= 7)
    absolute_kwargs = dict(hour=7, minute=0, second=0, microsecond=0)
    return dt + relativedelta(days=relative_days, **absolute_kwargs)

The way it works is that relativedelta takes absolute arguments (denoted by being in the singular, e.g. month, year, day) and relative arguments (denoted by being in the plural, e.g. months, years, days). If you add a relativedelta object to a datetime, it will replace absolute values in the datetime, then add the relative values, so what I've done above is specify that relative_days should be 1 if it's already 7am, otherwise it should be 0, and the absolute arguments say "replace the time with 7 AM". Add that to your datetime and it will give you the next 7am.

Dealing with time zones

The next step depends on what you are using for your time zone. If you are using a dateutil time zone, then you can just use the function defined above:

dt_next_7am = next_7am(dt)

If you are using a pytz timezone, you should strip it off and do the calculation as a naive date-time, then re-localize the time zone, as below:

dt_next_7am = tz.localize(next_7am(dt.replace(tzinfo=None)))

If you want to get the absolute number of hours between those two times, you should do the arithmetic in UTC:

time_between = dt_next_7am.astimezone(tz=UTC) - dt.astimezone(tz=UTC)

Where UTC has been defined as either dateutil.tz.tzutc() or pytz.UTC or equivalent.

Examples across a DST transition

Here is an example using dateutil (with the result in the comment):

from datetime import datetime
from dateutil.tz import gettz, tzutc

LA = gettz('America/Los_Angeles')
dt = datetime(2011, 11, 5, 12, 30, tzinfo=LA)
dt7 = next_7am(dt)

print(dt7.astimezone(tzutc()) - dt.astimezone(tzutc()))  # 19:30:00

And an example showing the wrong and right way to do this with pytz:

from datetime import datetime
import pytz
LA = pytz.timezone('America/Los_Angeles')
UTC = pytz.UTC

dt = LA.localize(datetime(2011, 11, 5, 12, 30))
dt7_bad = next_7am(dt)      # pytz won't like this
dt7_good = LA.localize(next_7am(dt.replace(tzinfo=None)))

dt_utc = dt.astimezone(pytz.UTC)
print(dt7_bad.astimezone(pytz.UTC) - dt_utc)   # 18:30:00 (Wrong)print(dt7_good.astimezone(pytz.UTC) - dt_utc)  # 19:30:00 (Right)

Ambiguous / Non-existent 7 AM

If you are dealing with certain dates in certain zones, specifically those that would result in an ambiguous time are on the following list (as of April 2016):

1901-12-1307:00:00 (/Pacific/Fakaofo)
1901-12-1407:00:00 (/Asia/Kamchatka)
1901-12-1407:00:00 (/Asia/Ust-Nera)
1901-12-1407:00:00 (/Pacific/Bougainville)
1901-12-1407:00:00 (/Pacific/Kosrae)
1901-12-1407:00:00 (/Pacific/Majuro)
1917-03-2507:00:00 (/Antarctica/Macquarie)
1918-03-3107:00:00 (/EST5EDT)
1919-03-3107:00:00 (/Antarctica/Macquarie)
1952-01-1307:00:00 (/Antarctica/DumontDUrville)
1954-02-1307:00:00 (/Antarctica/Mawson)
1957-01-1307:00:00 (/Antarctica/Davis)
1969-01-0107:00:00 (/Antarctica/Casey)
1969-02-0107:00:00 (/Antarctica/Davis)
1969-09-2907:00:00 (/Kwajalein)
1969-09-2907:00:00 (/Pacific/Kwajalein)
1979-09-3007:00:00 (/Pacific/Enderbury)
1979-09-3007:00:00 (/Pacific/Kiritimati)
2009-10-1807:00:00 (/Antarctica/Casey)
2011-09-2307:00:00 (/Pacific/Apia)
2011-10-2807:00:00 (/Antarctica/Casey)

Then the resulting 7AM value will be either ambiguous or non-existent. If you want to handle these edge cases, see this answer. It is probably worth noting that after PEP495 has been implemented, dealing with ambiguous times will probably be handled slightly differently.

An alternative implementation using python-dateutil's rrule module for generating recurrence rules and approach with pytz zones is below (note that this will work with non-pytz zones, but it will not resolve ambiguious/non-existent times properly):

from datetime import datetime
from dateutil import rrule
import pytz

defnext_real_7am(dt):
    tzi = dt.tzinfo

    dt_naive = dt.replace(tzinfo=None)
    rr = rrule.rrule(freq=rrule.DAILY, byhour=7, dtstart=dt_naive)

    for ndt in rr:
        localize = getattr(tzi, 'localize', None)
        if tzi isnotNoneand localize isnotNone:
            try:
                ndt = localize(ndt, is_dst=None)
            except pytz.AmbiguousTimeError:
                returnmin([localize(ndt, is_dst=True),
                            localize(ndt, is_dst=False)])
            except pytz.NonExistentTimeError:
                continueelse:
            ndt = ndt.replace(tzinfo=tzi)

        return ndt

KWA = pytz.timezone('Pacific/Kwajalein')

dtstart = KWA.localize(datetime(1969, 9, 29, 18))
dt7 = next_real_7am(dtstart)

print(dt7.tzname())         # Should be MHT, before the transition

dtstart = KWA.localize(datetime(1993, 8, 19, 18))  # There was no 8/20 in this zone
dt7 = next_real_7am(dtstart)
print(dt7)                  # Should be 1993-8-21 07:00:00

Solution 2:

I'm trying to find the next time it's 7am for a local timezone and return the number of seconds until that.

Find dt7 using the same code as for dt6 (replace time(6) with time(7)).

Then the number of seconds until that is (dt7 - now).total_seconds().

See the bullet points that explain when other solutions may fail.

Post a Comment for "How Do I Find The Next 7am In A Timezone"