I am trying to figure out how to make a datetime object aware through using a variable. I grab the timezone of the user and pass it through a form to be used.
I have tried both of the below methods and am not having much success
timezone_variable = "Europe/London"
new_datetime = datetime(int(date_year), int(date_month), int(date_day),
int(time_hour), int(date_minute), tzinfo=timezone_variable)
new_datetime = datetime(int(date_year), int(date_month), int(date_day),
int(time_hour), int(date_minute), tzinfo=timezone.timezone_variable)
This will then give me errors of TypeError: tzinfo argument must be None or of a tzinfo subclass, not type 'str'
The timezone will not always be known upfront, so it would not be possible to simply have the argument be as such tzinfo=timezone.utc.
Using dateutil or Python 3.9's zoneinfo:
from datetime import datetime
from dateutil.tz import gettz
# from zoneinfo import ZoneInfo # Python 3.9
date_year, date_month, date_day, time_hour, date_minute = 2020, 10, 21, 10, 21
timezone_variable = gettz("Europe/London") # ZoneInfo("Europe/London") # Python 3.9
new_datetime = datetime(int(date_year), int(date_month), int(date_day),
int(time_hour), int(date_minute), tzinfo=timezone_variable)
print(new_datetime)
# 2020-10-21 10:21:00+01:00
print(repr(new_datetime))
# datetime.datetime(2020, 10, 21, 10, 21, tzinfo=tzfile('GB-Eire'))
# with zoneinfo:
# datetime.datetime(2020, 10, 21, 10, 21, tzinfo=zoneinfo.ZoneInfo(key='Europe/London'))
Note: you can directly create a datetime object. If you use pytz (deprecated with Python 3.9), you must use the localize method of the timezone object. Otherwise, you'll end up with LMT (local mean time):
import pytz
timezone_variable = pytz.timezone("Europe/London")
# not what you want (most of the time...):
new_datetime = datetime(int(date_year), int(date_month), int(date_day),
int(time_hour), int(date_minute), tzinfo=timezone_variable)
print(repr(new_datetime))
# datetime.datetime(2020, 10, 21, 10, 21, tzinfo=<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>)
Side-note: interestingly, dateutil returns the deprecated GB-Eire time zone name for Europe/London. It is correct nevertheless, no worries.
You can create a timezone object from a string using pytz:
>>> from pytz import timezone
>>> timezone("Europe/London")
<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>
This can then be used in the datetime.datetime() constructor as the tzinfo parameter. Use the localize() function as described in the docs if needed.
I am moving my working Python into somebody else's revised Python code. I get errors. I understand that datetime.datetime cause problems, but I think I have tried the permutations. In testing, if I return() before the lines below, no runtime error. I am sorry about the formatting in the post. I have not figured that out properly.
Python version 2.7.12
import os, sys, re, datetime
from datetime import datetime
#NOTE: commenting out "from datetime import datetime" does not change the errors generated, so I think something is importing that from somewhere.
These are the 3 versions and the resulting error message at run time"
dt = dt - datetime.timedelta(hours=24.5) # decrement date a day or two
gives error "type object 'datetime.datetime' has no attribute 'timedelta'"
dt = dt - datetime.datetime.timedelta(hours=24.5) # ; also error:
gives error" type object 'datetime.datetime' has no attribute 'datetime'"
dt = dt - timedelta(hours=24.5) # ; also error:
gives error "global name 'timedelta' is not defined"
You say that commenting out from datetime import datetime does not change the errors, but this shouldn't be the case, it is the cause of the problem.
timedelta is in the datetime module. However, because you have done from datetime import datetime, the name datetime in your namespace refers to the datetime class, not the module, and can't be used to directly get a handle on the timedelta class.
Removing this line, so that datetime refers to the module, you should be able to access it as datetime.timedelta:
>>> import datetime
>>> dt = datetime.datetime.now()
>>> dt
datetime.datetime(2017, 8, 24, 15, 10, 34, 942209)
>>> dt = dt - datetime.timedelta(hours=24.5)
>>> dt
datetime.datetime(2017, 8, 23, 14, 40, 34, 942209)
Try it here: https://repl.it/KYDN/0
Alternatively, you could import both classes and refer to them directly:
>>> from datetime import datetime, timedelta
>>> dt = datetime.now()
>>> dt
datetime.datetime(2017, 8, 24, 15, 14, 46, 340878)
>>> dt = dt - timedelta(hours=24.5)
>>> dt
datetime.datetime(2017, 8, 23, 14, 44, 46, 340878)
By default when we create a datetime instance, the format includes microseconds.
>>> from datetime import datetime
>>> datetime.now()
datetime.datetime(2017, 6, 19, 16, 13, 7, 415321)
If I don't want the microseconds part in the instance then I do this,
>>> str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
>>> new_dt = datetime.strptime(str, '%Y-%m-%d %H:%M:%S')
So my question here is that is there any way where we can specify the format in which we want the datetime instance to be created rather than converting it to formatted string we want and then converting back to datetime instance.
A datetime object has no format. It is not created in any specific format, it is just an instance of a class. That instance has attributes, and those attributes have values.
If you need to have an instance that represents a time with the microsecond attribute set to 0, just use the datetime.replace() method to create a new instance from one with all the other attributes correct:
now = datetime.now()
now_without_ms = now.replace(microsecond=0)
However, if you wanted to format a datetime object for printing, then just format it without the microseconds component:
print(now.strftime('%Y-%m-%d %H:%M:%S'))
You don't have to use the microsecond attribute anywhere.
I'm trying to create a datetime.date object from integers, this is my code:
datetime.date(2011, 1, 1)
It gives me this error:
TypeError: descriptor 'date' requires a 'datetime.datetime' object but received a 'int'
If you do the following, it'll work neatly:
>>> import datetime
>>> datetime.date(2011,1,1)
datetime.date(2011, 1, 1)
However, if you do this:
from datetime import datetime
and then
datetime.date(2011,1,1)
the method you're actually calling is datetime.datetime.date(2011,1,1), which will fail:
>>> datetime.datetime.date(2011,1,1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor 'date' requires a 'datetime.datetime' object but received a 'int'
answer, based on the very generous contributions above.
The problem is that the datetime library includes a datetime class, which to the uninitiated sometimes is confusing.
To wrap up, if you do:
import datetime
datetime.date(2011, 1, 1)
you get
>>> datetime.date(2011, 1, 1)
Since you are using the date class of the datetime library. However, if you do
from datetime import datetime
datetime.date(2011, 1, 1)
you will get
>>>TypeError: descriptor 'date' requires a 'datetime.datetime' object but received a 'int'
since you are (inadvertently) using the datetime class of the datetime library, which equates to:
datetime.datetime.date(2011, 1, 1)
and the datetime class of the datetime library has no date method
Just use the proper import and you're set:
>>> from datetime import date
>>> today = date.today()
>>> today
datetime.date(2016, 3, 4)
>>> date(2016, 3, 4)
datetime.date(2016, 3, 4)
The error might be due to your import statement.
Change it from:
from datetime import datetime
To:
import datetime
>>> from datetime import datetime
>>> date = datetime(year=2011,month=1,day=1)
>>> print date
2011-01-01 00:00:00
>>>
Official documentation: datetime Objects
What I need to do
I have a timezone-unaware datetime object, to which I need to add a time zone in order to be able to compare it with other timezone-aware datetime objects. I do not want to convert my entire application to timezone unaware for this one legacy case.
What I've Tried
First, to demonstrate the problem:
Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
First, I tried astimezone:
>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>
It's not terribly surprising this failed, since it's actually trying to do a conversion. Replace seemed like a better choice (as per How do I get a value of datetime.today() in Python that is "timezone aware"?):
>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>>
But as you can see, replace seems to set the tzinfo, but not make the object aware. I'm getting ready to fall back to doctoring the input string to have a timezone before parsing it (I'm using dateutil for parsing, if that matters), but that seems incredibly kludgy.
Also, I've tried this in both Python 2.6 and Python 2.7, with the same results.
Context
I am writing a parser for some data files. There is an old format I need to support where the date string does not have a timezone indicator. I've already fixed the data source, but I still need to support the legacy data format. A one time conversion of the legacy data is not an option for various business BS reasons. While in general, I do not like the idea of hard-coding a default timezone, in this case it seems like the best option. I know with reasonable confidence that all the legacy data in question is in UTC, so I'm prepared to accept the risk of defaulting to that in this case.
In general, to make a naive datetime timezone-aware, use the localize method:
import datetime
import pytz
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)
now_aware = pytz.utc.localize(unaware)
assert aware == now_aware
For the UTC timezone, it is not really necessary to use localize since there is no daylight savings time calculation to handle:
now_aware = unaware.replace(tzinfo=pytz.UTC)
works. (.replace returns a new datetime; it does not modify unaware.)
All of these examples use an external module, but you can achieve the same result using just the datetime module, as also presented in this SO answer:
from datetime import datetime, timezone
dt = datetime.now()
dt = dt.replace(tzinfo=timezone.utc)
print(dt.isoformat())
# '2017-01-12T22:11:31+00:00'
Fewer dependencies and no pytz issues.
NOTE: If you wish to use this with python3 and python2, you can use this as well for the timezone import (hardcoded for UTC):
try:
from datetime import timezone
utc = timezone.utc
except ImportError:
#Hi there python2 user
class UTC(tzinfo):
def utcoffset(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return timedelta(0)
utc = UTC()
I wrote this Python 2 script in 2011, but never checked if it works on Python 3.
I had moved from dt_aware to dt_unaware:
dt_unaware = dt_aware.replace(tzinfo=None)
and dt_unware to dt_aware:
from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)
I use this statement in Django to convert an unaware time to an aware:
from django.utils import timezone
dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
Python 3.9 adds the zoneinfo module so now only the standard library is needed!
from zoneinfo import ZoneInfo
from datetime import datetime
unaware = datetime(2020, 10, 31, 12)
Attach a timezone:
>>> unaware.replace(tzinfo=ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 12:00:00+09:00'
Attach the system's local timezone:
>>> unaware.replace(tzinfo=ZoneInfo('localtime'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='localtime'))
>>> str(_)
'2020-10-31 12:00:00+01:00'
Subsequently it is properly converted to other timezones:
>>> unaware.replace(tzinfo=ZoneInfo('localtime')).astimezone(ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 20, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 20:00:00+09:00'
Wikipedia list of available time zones
Windows has no system time zone database, so here an extra package is needed:
pip install tzdata
There is a backport to allow use of zoneinfo in Python 3.6 to 3.8:
pip install backports.zoneinfo
Then:
from backports.zoneinfo import ZoneInfo
I agree with the previous answers, and is fine if you are ok to start in UTC. But I think it is also a common scenario for people to work with a tz aware value that has a datetime that has a non UTC local timezone.
If you were to just go by name, one would probably infer replace() will be applicable and produce the right datetime aware object. This is not the case.
the replace( tzinfo=... ) seems to be random in its behaviour. It is therefore useless. Do not use this!
localize is the correct function to use. Example:
localdatetime_aware = tz.localize(datetime_nonaware)
Or a more complete example:
import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())
gives me a timezone aware datetime value of the current local time:
datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
Use dateutil.tz.tzlocal() to get the timezone in your usage of datetime.datetime.now() and datetime.datetime.astimezone():
from datetime import datetime
from dateutil import tz
unlocalisedDatetime = datetime.now()
localisedDatetime1 = datetime.now(tz = tz.tzlocal())
localisedDatetime2 = datetime(2017, 6, 24, 12, 24, 36, tz.tzlocal())
localisedDatetime3 = unlocalisedDatetime.astimezone(tz = tz.tzlocal())
localisedDatetime4 = unlocalisedDatetime.replace(tzinfo = tz.tzlocal())
Note that datetime.astimezone will first convert your datetime object to UTC then into the timezone, which is the same as calling datetime.replace with the original timezone information being None.
This codifies #Sérgio and #unutbu's answers. It will "just work" with either a pytz.timezone object or an IANA Time Zone string.
def make_tz_aware(dt, tz='UTC', is_dst=None):
"""Add timezone information to a datetime object, only if it is naive."""
tz = dt.tzinfo or tz
try:
tz = pytz.timezone(tz)
except AttributeError:
pass
return tz.localize(dt, is_dst=is_dst)
This seems like what datetime.localize() (or .inform() or .awarify()) should do, accept both strings and timezone objects for the tz argument and default to UTC if no time zone is specified.
for those that just want to make a timezone aware datetime
import datetime
datetime.datetime(2019, 12, 7, tzinfo=datetime.timezone.utc)
for those that want a datetime with a non utc timezone starting in python 3.9 stdlib
import datetime
from zoneinfo import ZoneInfo
datetime.datetime(2019, 12, 7, tzinfo=ZoneInfo("America/Los_Angeles"))
Yet another way of having a datetime object NOT naive:
>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc)
datetime.datetime(2021, 5, 1, 22, 51, 16, 219942, tzinfo=datetime.timezone.utc)
quite new to Python and I encountered the same issue. I find this solution quite simple and for me it works fine (Python 3.6):
unaware=parser.parse("2020-05-01 0:00:00")
aware=unaware.replace(tzinfo=tz.tzlocal()).astimezone(tz.tzlocal())
Here is a simple solution to minimize changes to your code:
from datetime import datetime
import pytz
start_utc = datetime.utcnow()
print ("Time (UTC): %s" % start_utc.strftime("%d-%m-%Y %H:%M:%S"))
Time (UTC): 09-01-2021 03:49:03
tz = pytz.timezone('Africa/Cairo')
start_tz = datetime.now().astimezone(tz)
print ("Time (RSA): %s" % start_tz.strftime("%d-%m-%Y %H:%M:%S"))
Time (RSA): 09-01-2021 05:49:03
In the format of unutbu's answer; I made a utility module that handles things like this, with more intuitive syntax. Can be installed with pip.
import datetime
import saturn
unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)
now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')
Changing between timezones
import pytz
from datetime import datetime
other_tz = pytz.timezone('Europe/Madrid')
# From random aware datetime...
aware_datetime = datetime.utcnow().astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00
# 1. Change aware datetime to UTC and remove tzinfo to obtain an unaware datetime
unaware_datetime = aware_datetime.astimezone(pytz.UTC).replace(tzinfo=None)
>> 2020-05-21 06:28:26.984948
# 2. Set tzinfo to UTC directly on an unaware datetime to obtain an utc aware datetime
aware_datetime_utc = unaware_datetime.replace(tzinfo=pytz.UTC)
>> 2020-05-21 06:28:26.984948+00:00
# 3. Convert the aware utc datetime into another timezone
reconverted_aware_datetime = aware_datetime_utc.astimezone(other_tz)
>> 2020-05-21 08:28:26.984948+02:00
# Initial Aware Datetime and Reconverted Aware Datetime are equal
print(aware_datetime1 == aware_datetime2)
>> True
Above all mentioned approaches, when it is a Unix timestamp, there is a very simple solution using pandas.
import pandas as pd
unix_timestamp = 1513393355
pst_tz = pd.Timestamp(unix_timestamp, unit='s', tz='US/Pacific')
utc_tz = pd.Timestamp(unix_timestamp, unit='s', tz='UTC')