23

In Python, let's say I have a date of 25 December 2016. How can I create a timezone-aware datetime of noon on that date?

Bonus points if it's compatible with Django's timezone handling.

seddonym
  • 16,304
  • 6
  • 66
  • 71

2 Answers2

49

The trick is to first combine the naive time and the date into a naive datetime. This naive datetime can then be converted to an aware datetime.

The conversion can be done using the third party package pytz (using, in this case, the 'Europe/London' timezone):

import datetime
import pytz

naive_time = datetime.time(0, 30)
date = datetime.date(2016, 12, 25)
naive_datetime = datetime.datetime.combine(date, naive_time)

timezone = pytz.timezone('Europe/London')
aware_datetime = timezone.localize(naive_datetime) 

If you're doing it in Django, and want to use the current timezone (as configured in Django), you can replace the final two lines with a call to make_aware:

from django.utils import timezone

aware_datetime = timezone.make_aware(naive_datetime)
seddonym
  • 16,304
  • 6
  • 66
  • 71
  • 2
    [use `.localize()` method for timezones with a non-fixed utc offset](http://stackoverflow.com/a/36462784/4279). Or `django.utils.timezone.make_aware()`. – jfs Apr 14 '16 at 21:37
  • 1
    If datetime/time object is already created then use: `date_object.replace(tzinfo=timezone.get_current_timezone())` – d345k0 Sep 13 '17 at 15:13
  • 1
    @d345k0: `replace()` is a recipe for problems! Use `localize()` instead, as jfs said. `replace()` doesn't take the date into account when converting the timezone, so you get issue when the timezone has had transitions such as daylight savings (or other transitions... see [here](https://stackoverflow.com/questions/20804837/weird-astimezone-behavior) ) – Neal Gokli Apr 23 '19 at 22:02
  • 2
    As seen [here](https://stackoverflow.com/a/52734072/1000655), `datetime.combine()` doesn't take the date into account when dealing with the timezone, leading to incorrect results. As jfs said, use `timezone.localize()` instead! To be more specific, use combine without a timezone, and then use localize. – Neal Gokli Apr 23 '19 at 22:06
  • 3
    I should be more clear, in case someone stumbles across this: This answer is incorrect. – Neal Gokli Apr 23 '19 at 22:24
  • 1
    Thanks @NealGokli for pointing this out. I've now corrected the answer based on your comments. – seddonym Apr 24 '19 at 10:55
5

Erik's answer to this question explains why you should use datetime.combine() followed by timezone.localize(). Setting the timezone directly on the datetime.time instance will not take the date into account, which could leave you with an incorrect result (due to daylight savings or even historic transitions). datetime.combine() just isn't that smart!

>>> import pytz
>>> import datetime
>>> d = datetime.date(2016, 12, 25)
>>> t = datetime.time(12)
>>> tz = pytz.timezone('US/Pacific')
>>> tz.localize(datetime.datetime.combine(d,t))
datetime.datetime(2016, 12, 25, 12, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
Neal Gokli
  • 475
  • 7
  • 18