How Do I Find The Next 7am In A Timezone
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"