How do I get the UTC time of "midnight" for a given timezone?

77,934

Solution 1

@hop's answer is wrong on the day of transition from Daylight Saving Time (DST) e.g., Apr 1, 2012. To fix it tz.localize() could be used:

tz = pytz.timezone("Australia/Melbourne")
today = datetime.now(tz).date()
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)
utc_dt = midnight.astimezone(pytz.utc)        

The same with comments:

#!/usr/bin/env python
from datetime import datetime, time
import pytz # pip instal pytz

tz = pytz.timezone("Australia/Melbourne") # choose timezone

# 1. get correct date for the midnight using given timezone.
today = datetime.now(tz).date()

# 2. get midnight in the correct timezone (taking into account DST)
#NOTE: tzinfo=None and tz.localize()
# assert that there is no dst transition at midnight (`is_dst=None`)
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)

# 3. convert to UTC (no need to call `utc.normalize()` due to UTC has no 
#    DST transitions)
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
print midnight.astimezone(pytz.utc).strftime(fmt)

Solution 2

This is more straightforward with dateutil.tz than pytz:

>>>import datetime
>>>import dateutil.tz
>>>midnight=(datetime.datetime
             .now(dateutil.tz.gettz('Australia/Melbourne'))
             .replace(hour=0, minute=0, second=0, microsecond=0)
             .astimezone(dateutil.tz.tzutc()))
>>>print(midnight)
2019-04-26 14:00:00+00:00

The tzinfo documentation recommends dateutil.tz since Python 3.6. The tzinfo objects from dateutil.tz have no problems with anomalies like DST without requiring the localize functionality of pytz. Using the example from user3850:

>>> now = (datetime.datetime(2012, 4, 1, 5,  
...         tzinfo = dateutil.tz.gettz('Australia/Melbourne'))) 
>>> print(now.replace(hour = 0).astimezone(dateutil.tz.tzutc()))
2012-03-31 13:00:00+00:00
Share:
77,934
Tom
Author by

Tom

I'm mostly a Python & JavaScript developer at the moment but I've done a lot of things...

Updated on March 28, 2020

Comments

  • Tom
    Tom about 4 years

    The best I can come up with for now is this monstrosity:

    >>> datetime.utcnow() \
    ...   .replace(tzinfo=pytz.UTC) \
    ...   .astimezone(pytz.timezone("Australia/Melbourne")) \
    ...   .replace(hour=0,minute=0,second=0,microsecond=0) \
    ...   .astimezone(pytz.UTC) \
    ...   .replace(tzinfo=None)
    datetime.datetime(2008, 12, 16, 13, 0)
    

    I.e., in English, get the current time (in UTC), convert it to some other timezone, set the time to midnight, then convert back to UTC.

    I'm not just using now() or localtime() as that would use the server's timezone, not the user's timezone.

    I can't help feeling I'm missing something, any ideas?

  • Tom
    Tom over 15 years
    Aside from not wanting to use the TZ variable to control this, that doesn't actually tell me how to find midnight, just the current time.
  • Tom
    Tom over 15 years
    I think you're on the right track, but how do you know what date to start with? i.e. a few hours ago, it was the 17th here in Melbourne, while it was still the 16th in UTC.
  • Admin
    Admin over 15 years
    adding/substracting tz differences by hand might have issues around the switch from and to DST
  • Admin
    Admin almost 12 years
    I'm a bit confused. The DST switch happened at 3 a.m., so midnight on that day should still be at 14:00 UTC, not 13:00. no?
  • jfs
    jfs almost 12 years
    @hop: convert 2012 Mar 31 13:00 UTC to Melbourne timezone and see for yourself (it is still +11 timezone (DST), not +10 (standard))
  • mik
    mik about 6 years
    time() is the same as time(0, 0), but shorter