timezones in python has couple of minutes shift - python

from pytz import timezone
import datetime
utc = datetime.timezone.utc
tz = timezone("Europe/London")
now = datetime.datetime.now(utc)
date = now.replace(tzinfo=tz)
date_utc = date.astimezone(utc)
print(now.isoformat())
print(date.isoformat())
print(date_utc.isoformat())
Might print:
2021-12-01T21:30:09.170108+00:00
2021-12-01T21:30:09.170108-00:01 <---- why does it say -00:01 here?
2021-12-01T21:31:09.170108+00:00
Why on earth is there a one minute shift?
Am I missing some fundamental knowledge on time zones?

As per the documentation from PYTZ
Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones.
You can only use localize() or astimezone() to attach PYTZ timezones to datetime objects

Related

Why are these Python datetime conversions affected by local time (UTC offset and DST)?

I'm using Python 3.10.4 on Linux trying to convert naive datetime objects that represent UTC to a local time (UTC+1+DST).
What puzzles me is that converting a naive datetime to UTC seems to take the local timezone into consideration despite its tzinfo is None (as it should) - whereas going the other way, i.e. attempting to convert a datetime that is naive or aware (set to UTC) to the local timezone, does not seem to take DST into account.
That only works as expected when using zoneinfo (new in 3.9) instead of timezone.
import datetime
print("naive as UTC")
# naive local time UTC+1 (DST +1)
# -> UTC 22-07-07 06:02:34
naive = datetime.datetime(2022,7,7,8,2,34)
print( naive )
utc = naive.astimezone(datetime.timezone.utc)
print(utc)
print("UTC as local")
utc = datetime.datetime(2022,7,7,8,2,34,tzinfo=datetime.timezone.utc)
print( utc )
tz = datetime.timezone( datetime.timedelta(hours=1) )
local = naive.astimezone(tz)
print(local, local.dst() )
print("UTC as local with zoneinfo")
import zoneinfo
local = utc.astimezone(zoneinfo.ZoneInfo("Europe/Berlin"))
print(local, local.dst() )
Output
naive as UTC
2022-07-07 08:02:34
2022-07-07 06:02:34+00:00
UTC as local
2022-07-07 08:02:34+00:00
2022-07-07 07:02:34+01:00 None
UTC as local with zoneinfo
2022-07-07 10:02:34+02:00 1:00:00
On the other hand, the code for "UTC as local" does seem to handle DST but not the UTC offset:
UTC as local
2022-07-07 08:02:34+00:00
2022-07-07 07:02:34+01:00 None
UTC as local
2022-01-07 08:02:34+00:00
2022-01-07 08:02:34+01:00 None
While I get expected results using zoneinfo, I'd like to understand what is happening with the other "conversions" and why the system clock affects them (running the code on https://www.python.org/shell/ gives different results).
When you create a datetime object like this:
naive = datetime.datetime(2022,7,7,8,2,34)
You create a naive object (that is not aware of the timezone). Following the documentation:
Whether a naive object represents Coordinated Universal Time (UTC), local time, or
time in some other timezone is purely up to the program, just like it
is up to the program whether a particular number represents metres,
miles, or mass.
I think you expect that this object will represent UTC time, but it's not true. It's simply unaware of the timezone so the timezone could be interpreted anyhow.
Let's look at source code of the astimezone method:
def astimezone(self, tz=None):
....
mytz = self.tzinfo
if mytz is None:
mytz = self._local_timezone()
myoffset = mytz.utcoffset(self)
else:
....
# Convert from UTC to tz's local time.
return tz.fromutc(utc)
You see that when self.tzinfo is None then it interprets this as a local timezone rather than UTC
The other examples that you provided are aware of the timezone.
To make a naive datetime object aware, do not use .astimezone(), as that assumes your system's local time for your naive object:
If self is naive, it is presumed to represent time in the system timezone.
Use .replace(tzinfo=...) instead.

Python datetime utcnow does not set timezone?

Found this issues working with timestamp conversions where the .timestamp() seem to compensate for time offset even though I created the datetime object with utcnow() method. So what is the reason for the utcnow() method if it actually fails when converting to timestamp later on?
def test_timestamp_issues():
now = datetime.now()
utc_now = datetime.utcnow()
utc_now_with_tz = datetime.now(timezone.utc)
print(f"Now: {now} -- As Timestamp: {now.timestamp()}")
print(f"Utc Now: {utc_now} -- As TimeStamp: {utc_now.timestamp()} "
f"with builtin: {datetime.timestamp(utc_now)}, with setting timezone hard: "
f"{utc_now.replace(tzinfo=timezone.utc).timestamp()}")
print(f"Utc Now: {utc_now_with_tz} -- As TimeStamp: {utc_now_with_tz.timestamp()} "
f"with builtin: {datetime.timestamp(utc_now_with_tz)}, with setting timezone hard: "
f"{utc_now_with_tz.replace(tzinfo=timezone.utc).timestamp()}")
Output:
Now: 2022-04-25 09:12:16.881608 -- As Timestamp: 1650870736.881608
Utc Now: 2022-04-25 07:12:16.881613 -- As TimeStamp: 1650863536.881613 with builtin: 1650863536.881613, with setting timezone hard: 1650870736.881613
Utc Now: 2022-04-25 07:12:16.881753+00:00 -- As TimeStamp: 1650870736.881753 with builtin: 1650870736.881753, with setting timezone hard: 1650870736.881753
The expected timestamp here should be 1650870736.881608 but for some reason converting the utcnow() gives 2 hours backward again.
So for some reason I get another 2 hours withdrawal when taking timestamp of a utcnow() created datetime object. I am currently running a macbook m1 pro, with Timezone Norway (+1, +2 atm with daylight), and python 3.9.12
So can this be the arm architecture messing up things or is it just not recommended to use the utcnow() and then convert to timestamp?
The answer to your question is managing timezones (TZ). When you create a datetime instance with now() or utcnow(), it does not store info about TZ. And, according to python docs for utcnow() (bolding by author):
Return the current UTC date and time, with tzinfo None.
This is like now(), but returns the current UTC date and time, as a naive datetime object. An aware current UTC datetime can be obtained by calling datetime.now(timezone.utc). See also now().
Warning: Because naive datetime objects are treated by many datetime methods as local times, it is preferred to use aware datetimes to represent times in UTC. As such, the recommended way to create an object representing the current time in UTC is by calling datetime.now(timezone.utc).
This means that, although you received time for UTC TZ with utcnow(), since no TZ info is stored (naive datetime object), python thinks of it as a local time. And, when converting it to timestamp, will add/subtract time difference once again. Proof for this weird behaviour may be found in python docs for timestamp() method:
Naive datetime instances are assumed to represent local time
The reason actually is the one, you stated in the title of the question, not the ARM architecture

How do I construct a UTC `datetime` object in Python?

I am using the datetime.datetime class from the Python standard library. I wish to construct an instance of this class with the UTC timezone. To do so, I gather that I need to pass as the tzinfo argument to the datetime constructor some instance of the tzinfo class.
The documentation for the tzinfo class says that:
tzinfo is an abstract base class, meaning that this class should not be instantiated directly. You need to derive a concrete subclass, and (at least) supply implementations of the standard tzinfo methods needed by the datetime methods you use. The datetime module does not supply any concrete subclasses of tzinfo.
Now I'm stumped. All I want to do is represent "UTC". I should be able to do that using approximately three characters, like this
import timezones
...
t = datetime(2015, 2, 1, 15, 16, 17, 345, timezones.UTC)
In short, I'm not going to do what the documentation tells me to do. So what's my alternative?
There are fixed-offset timezones in the stdlib since Python 3.2:
from datetime import datetime, timezone
t = datetime(2015, 2, 1, 15, 16, 17, 345, tzinfo=timezone.utc)
Constructor is :
datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0)
Docs link.
Though it is easy to implement utc timezone on earlier versions:
from datetime import tzinfo, timedelta, datetime
ZERO = timedelta(0)
class UTCtzinfo(tzinfo):
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTCtzinfo()
t = datetime(2015, 2, 1, 15, 16, 17, 345, tzinfo=utc)
I used a lot in pytz and very satisfied from this module.
pytz
pytz brings the Olson tz database into Python. This library allows
accurate and cross platform timezone calculations using Python 2.4
or higher. It also solves the issue of ambiguous times at the end
of daylight saving time, which you can read more about in the Python
Library Reference (datetime.tzinfo).
Also I would recommend for reading: Understanding DateTime, tzinfo, timedelta & TimeZone Conversions in python

Generating current time in a chosen time zone

I'm trying to perform a, to me, simple task of generating a current date/time combo at a speficic time zone. All I see is suggestions to use pytz even though datetime includes the tzinfo class to deal with timezones. However, if I try to use tzinfo, it does not work:
>>> from datetime import datetime, tzinfo
>>> d = datetime.now(tzinfo.tzname("EDT"))
TypeError: descriptor 'tzname' requires a 'datetime.tzinfo' object but received a 'str'
The docs say you can use a time zone name like "EDT" or "GMT". What's wrong with this?
The function tzinfo.tzname does the opposite of what you think it does.
It takes a datetime object and returns a string indicating the time zone.

"import datetime" v.s. "from datetime import datetime"

I have a script that needs to execute the following at different lines in the script:
today_date = datetime.date.today()
date_time = datetime.strp(date_time_string, '%Y-%m-%d %H:%M')
In my import statements I have the following:
from datetime import datetime
import datetime
I get the following error:
AttributeError: 'module' object has no attribute 'strp'
If I change the order of the import statements to:
import datetime
from datetime import datetime
I get the following error:
AttributeError: 'method_descriptor' object has no attribute 'today'
If I again change the import statement to:
import datetime
I get the following error:
AttributeError: 'module' object has no attribute 'strp'
What is going on here and how do I get both to work?
Your trouble is that you have some code that is expecting datetime to be a reference to the datetime module and other code that is expecting datetime to be a reference to the datetime class. Obviously, it can't be both.
When you do:
from datetime import datetime
import datetime
You are first setting datetime to be a reference to the class, then immediately setting it to be a reference to the module. When you do it the other way around, it instead ends up being a reference to the class. Last assignment "wins."
You need to rename one of these references. For example:
import datetime as dt
from datetime import datetime
Then you can change references in the form datetime.xxxx that refer to the module to dt.xxxx.
Or else just import datetime and change all references to use the module name. In other words, if something just says datetime(...) you need to change that reference to datetime.datetime.
Python has a fair bit of this kind of thing in its library, unfortunately. If they followed their own naming guidelines in PEP 8, the datetime class would be named Datetime and there'd be no problem using both datetime to mean the module and Datetime to mean the class.
You cannot use both statements; the datetime module contains a datetime type. The local name datetime in your own module can only refer to one or the other.
Use only import datetime, then make sure that you always use datetime.datetime to refer to the contained type:
import datetime
today_date = datetime.date.today()
date_time = datetime.datetime.strptime(date_time_string, '%Y-%m-%d %H:%M')
Now datetime is the module, and you refer to the contained types via that.
Alternatively, import all types you need from the module:
from datetime import date, datetime
today_date = date.today()
date_time = datetime.strptime(date_time_string, '%Y-%m-%d %H:%M')
Here datetime is the type from the module. date is another type, from the same module.
datetime is a module which contains a type that is also called datetime. You appear to want to use both, but you're trying to use the same name to refer to both. The type and the module are two different things and you can't refer to both of them with the name datetime in your program.
If you need to use anything from the module besides the datetime type (as you apparently do), then you need to import the module with import datetime. You can then refer to the "date" type as datetime.date and the datetime type as datetime.datetime.
You could also do this:
from datetime import datetime, date
today_date = date.today()
date_time = datetime.strp(date_time_string, '%Y-%m-%d %H:%M')
Here you import only the names you need (the datetime and date types) and import them directly so you don't need to refer to the module itself at all.
Ultimately you have to decide what names from the module you need to use, and how best to use them. If you are only using one or two things from the module (e.g., just the date and datetime types), it may be okay to import those names directly. If you're using many things, it's probably better to import the module and access the things inside it using dot syntax, to avoid cluttering your global namespace with date-specific names.
Note also that, if you do import the module name itself, you can shorten the name to ease typing:
import datetime as dt
today_date = dt.date.today()
date_time = dt.datetime.strp(date_time_string, '%Y-%m-%d %H:%M')
The difference between from datetime import datetime and normal import datetime is that , you are dealing with a module at one time and a class at other.
The strptime function only exists in the datetime class so you have to import the class with the module otherwise you have to specify datetime twice when calling this function.
The thing here is that , the class name and the module name has been given the same name so it creates a bit of confusuion.
try this:
import datetime
from datetime import datetime as dt
today_date = datetime.date.today()
date_time = dt.strptime(date_time_string, '%Y-%m-%d %H:%M')
strp() doesn't exist. I think you mean strptime.
from datetime import datetime,timedelta
today=datetime.today()
print("Todays Date:",today)
yesterday=today-datetime,timedelta(days=1)
print("Yesterday date:",yesterday)
tommorrow=today+datetime.timedelta(days=1)
print("Tommorrow Date:",tommorrow)

Categories