How to add timezone into a naive datetime instance in python

86,335

Solution 1

Use tz.localize(d) to localize the instance. From the documentation:

The first is to use the localize() method provided by the pytz library. This is used to localize a naive datetime (datetime with no timezone information):

>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500

If you don't use tz.localize(), but use datetime.replace(), chances are that a historical offset is used instead; tz.localize() will pick the right offset in effect for the given date. The US Eastern timezone DST start and end dates have changed over time, for example.

When you try to localize a datetime value that is ambiguous because it straddles the transition period from summer to winter time or vice-versa, the timezone will be consulted to see if the resulting datetime object should have .dst() return True or False. You can override the default for the timezone with the is_dst keyword argument for .localize():

dt = tz.localize(naive, is_dst=True)

or even switch off the choice altogether by setting is_dst=None. In that case, or in the rare cases there is no default set for a timezone, an ambiguous datetime value would lead to a AmbiguousTimeError exception being raised. The is_dst flag is only consulted for datetime values that are ambiguous and is ignored otherwise.

To go back the other way, turn a timezone-aware object back to a naive object, use .replace(tzinfo=None):

naivedt = awaredt.replace(tzinfo=None)

Solution 2

If you know that your original datetime was "measured" in the time zone you are trying to add to it, you could (but probably shouldn't) use replace rather than localize.

# d = datetime.datetime.now()
# tz = pytz.timezone('Asia/Taipei')
d = d.replace(tzinfo=tz)

I can imagine 2 times when this might make sense (the second one happened to me):

  1. Your server locale is set to the incorrect time zone and you are trying to correct a datetime instance by making it aware of this incorrect timezone (and presumably later localizing it to the "correct" time zone so the values of now() match up to other times you are comparing it to (your watch, perhaps)
  2. You want to "tag" a time instance (NOT a datetime) with a time zone (tzinfo) attribute so that attribute can be used later to form a full datetime instance.
Share:
86,335
waitingkuo
Author by

waitingkuo

Updated on July 09, 2022

Comments

  • waitingkuo
    waitingkuo almost 2 years

    I've got a datetime which has no timezone information. I'm now getting the timezone info and would like to add the timezone into the existed datetime instance, how can I do?

    d = datetime.datetime.now()
    tz = pytz.timezone('Asia/Taipei')
    

    How to add the timezone info tz into datetime a

  • waitingkuo
    waitingkuo over 11 years
    Is there any convenient way to get the naive datetime from datetime which has tzinfo?
  • Martijn Pieters
    Martijn Pieters over 11 years
    @waitingkuo: call .replace(tzinfo=None) on the datetime object. The return value is a naive datetime instance.
  • jfs
    jfs over 11 years
    is_dst parameter is worth mentioning to resolve ambiguous times or to assert that there is no DST transition at the time.
  • Martijn Pieters
    Martijn Pieters over 11 years
    @J.F.Sebastian: There, a short paragraph on is_dst added.
  • jfs
    jfs over 9 years
    also, local_dt = tz.localize(dt) may produce a non-existing time (during "sprint forward" DST transition). To fix it, call local_dt = tz.normalize(local_dt). Or use is_dst=None that prevents tz.localize() from returning ambiguous or non-existing times.
  • hobs
    hobs about 9 years
    pytz.timezone.localize(datetime.time, pyzt) will fail for timzones other than GMT/UTC (pytz.timezone.tzinfo.timedelta is nonzero). The localize method works great for datetimes but not for times (because it might have to wrap around to a new day). For a time just use new_time = old_time.replace(tzinfo=pytz.timezone(timezone_name)). This will NOT adjust the time to account for the new time zone, just presume it was already "measured" in local time. Incidentally, if you don't want to "shift" a datetime it .replace works for datetimes as well.
  • Martijn Pieters
    Martijn Pieters about 9 years
    @hobs: I'd not expect localize to work for time objects, no, because the localisation requires a date to do its job correctly. Timezone offsets make no sense for just a time component, you are missing the information for DST and historical context.
  • hobs
    hobs about 9 years
    @MartjinPieters Exactly. But if you need a tz-aware time (which is only useful with other aware times in the same time zone) you can use .replace(). I agree that doesn't normally make sense... but if you inform a time of a time zone that tz can be used later when forming a datetime.
  • jfs
    jfs about 9 years
    @hobs: it is wrong to use .replace() with a pytz timezone that may have multiple utc offsets (many timezones do). The default tzinfo object usually corresponds to LMT (solar time) that is not what you want in most cases (I think the reasoning behind the default is to help reveal the incorrect .replace() usage).
  • hobs
    hobs about 9 years
    @J.F.Sebastian Makes sense that it is 'wrong'. But it might be OK for someone in my situation (with datetime.time instances without date info yet). This happened to me processing a CSV from wunderground.com. The TZ info is available in the column header (and sometimes in the field string), but date is available elsewhere. So I just tagged the time with the tzinfo and used that tzinfo to localize with the date later. I'm sure it's wrong, a hack, but it worked for me. The downvote on my misguided answer will hopefully encourage others to think hard about how to solve this edge case properly.
  • Martijn Pieters
    Martijn Pieters about 9 years
    @hobs: why not just store the timezone and time separately and when you combine that with the date then apply the timezone?
  • hobs
    hobs about 9 years
    @MartjinPieters Agree that's probably safer from a timezone accuracy/confusion/ambiguity perspective, but adds complication and might introduce bookkeeping (indexing) errors... I prefer to create one object per CSV "cell" and pass a list of lists of objects around rather than a list of lists of 2-tuples of objects.
  • Marc
    Marc almost 4 years
    3. you've retrieved a datetime from a DB where the column is naive, but you know the TZ it was stored in, and you want to manipulate it after retrieval.
  • Marc
    Marc about 3 years
    4. you've generated a utc time using utcnow, which is tz-naive, and you need to compare it against a tx-aware time.