2

Given a datetime object, how do I round it up to the next occurrence of 8AM PST?

jfs
  • 399,953
  • 195
  • 994
  • 1,670
camomilk
  • 763
  • 1
  • 7
  • 15
  • 1
    Make a new datetime, use input from the current datetime year, month, day and set hours, mins, seconds as 8, 0, 0 respectively – Ian Feb 24 '16 at 18:16
  • 2
    @Ian: This would always be today's 8AM. To always round up, you need to add another day on top unless it is before 8AM. – jsfan Feb 24 '16 at 18:19
  • related: [Rounding up to nearest 30 minutes in python](http://stackoverflow.com/q/32723150/4279) – jfs Feb 25 '16 at 12:46

3 Answers3

3

Just test whether the time is before or after 8, then add a day if it's after and construct a new datetime.

import datetime

def round_datetime(dt):
    t = datetime.time(8)
    # If time is after 8am, add a day.
    if dt.time() > datetime.time(8):
        dt += datetime.timedelta(days=1)
    return datetime.datetime.combine(dt, t)
Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
  • 1
    Thanks, I didn't realize you could split up the date and time like that. – camomilk Feb 24 '16 at 19:49
  • 1
    if the input is a timezone-aware datetime object then it is more complex (your code is ok if the input is a naive datetime object). – jfs Feb 25 '16 at 12:41
3

If the result is a timezone-aware datetime object in a timezone with a non-fixed UTC offset then you can't just call .replace() or .combine() -- it may create a datetime with a wrong UTC offset. The issue is similar to How do I get the UTC time of "midnight" for a given timezone? (00:00 is used instead of 08:00).

Assuming 8AM always exists and unambiguous in PST:

from datetime import datetime, time as datetime_time, timedelta
import pytz # $ pip install pytz

def next_8am_in_pst(aware_dt, tz=pytz.timezone('America/Los_Angeles')):
    pst_aware_dt = tz.normalize(aware_dt.astimezone(tz)) # convert to PST
    naive_dt = round_up_to_8am(pst_aware_dt.replace(tzinfo=None))
    return tz.localize(naive_dt, is_dst=None)

def round_up_to_8am(dt):
    rounded = datetime.combine(dt, datetime_time(8))
    return rounded + timedelta(rounded < dt)

Example:

>>> str(next_8am_in_pst(datetime.now(pytz.utc))) 
'2016-02-25 08:00:00-08:00'
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
0

I made an answer based on some ideas from the comments:

def nextDay(d):
    pstTime = d.astimezone(pytz.timezone('US/Pacific'))
    pstTime = pstTime.replace(hour=8, minute=0, second=0, microsecond=0)

    if pstTime < d:
        pstTime += datetime.timedelta(days=1)

    return pstTime
camomilk
  • 763
  • 1
  • 7
  • 15