Django beginner here.
In official docs:
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
Trying do reproduce it in ./manage.py shell:
In [35]: from django.conf import settings
In [36]: settings.USE_TZ
Out[36]: True
In [37]: settings.TIME_ZONE
Out[37]: 'Europe/Moscow'
In [38]: from django.utils import timezone
# UTC
In [39]: timezone.now()
Out[39]: datetime.datetime(2015, 10, 16, 9, 47, 50, 755418, tzinfo=<UTC>)
# Actual time
In [40]: timezone.datetime.now()
Out[40]: datetime.datetime(2015, 10, 16, 12, 47, 54, 554197)
# UTC
In [41]: timezone.activate("Europe/Moscow"); timezone.now()
Out[41]: datetime.datetime(2015, 10, 16, 9, 47, 59, 405269, tzinfo=<UTC>)
# Actual time
In [42]: timezone.activate("Europe/Moscow"); timezone.datetime.now()
Out[42]: datetime.datetime(2015, 10, 16, 12, 48, 3, 179085)
When I'm running timezone.now() as specified in documentation, i'm getting UTC which is wrong. When i'm running timezone.datetime.now() (what i think is just call to datetime.datetime.now(), which is using system-wide timezone) i'm getting the right thing.
Tried with different timezones, still getting plain UTC.
What am I doing wrong?
timezone.now() behaves as it should: if USE_TZ=True; it returns the current time as an aware datetime object (in UTC).
2015-10-16 09:47:50+00:00 (UTC) is the same time moment as 2015-10-16 12:47:50+03:00 (MSK). The UTC time is rendered in the templates using your current time zone (defaults to TIME_ZONE setting and therefore it is unnecessary to set it explicitly using timezone.activate() here).
You can get the value explicitly using timezone.localtime(timezone.now()) (you don't need to).
Related
I get a timestamp (via an extern API). When I try to save that, it always is two hours earlier.
I read some docs like Django Time zones, pytz and dateutil but I still don't understand how to tell Django that this specific time should not be converted to UTC because it already is UTC.
It looks like this:
t = '2020-05-29 08:47:39' # this is UTC
MyModel(
timestamp=t,
…
).save()
In the database it is stored as 2020-05-29 06:47:39. So it is shown on a template e. g. as 2020-05-29 08:47:39 whereas 2020-05-29 10:47:39 would be correct.
settings.py
TIME_ZONE = 'Europe/Berlin'
USE_TZ = True
I think I found the solution by myself: the string t must be converted to a datetime-object including the UTC timezone like this:
>>> t = '2020-05-29 08:47:39'
>>> import datetime
>>> dt = datetime.datetime.strptime(t, '%Y-%m-%d %H:%M:%S')
>>> dt
datetime.datetime(2020, 5, 29, 8, 47, 39)
>>> import pytz
>>> tz = pytz.utc.localize(dt)
>>> tz
datetime.datetime(2020, 5, 29, 8, 47, 39, tzinfo=<UTC>)
Saving this with Django, everything is perfect.
I want to change timezone in Django, so I read documentation how to do it nad here's what I've got:
#settings.py
TIME_ZONE = 'Europe/Ljubljana'
#models.py #date_time gets filled with "auto_now=True")
date_time = models.DateTimeField(auto_now=True)
UTC DST offset for given location (Europe/Ljubljana) is +2, while in my db I see timestamp of UTC. So what am I missing?
Or is this working as intended so it gets processed for each request separately (useful for people in different timezones)? But if this is the case, what's the use of setting TIME_ZONE = 'Europe/Ljubljana'?
From the documentation
When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.
so the datetime in your DB will always be stored in UTC, but will be displayed using the correct TZ in templates and forms.
To get the date in correct TZ elsewhere, use astimezone():
>>> from myapp.models import Details
>>> import pytz
>>> d = Details.objects.get(pk=1)
>>> d.added
datetime.datetime(2016, 5, 28, 18, 59, 55, 841193, tzinfo=<UTC>)
>>> localdate = d.added.astimezone(pytz.timezone('Europe/Ljubljana'))
>>> localdate
datetime.datetime(2016, 5, 28, 20, 59, 55, 841193, tzinfo=<DstTzInfo 'Europe/Ljubljana' CEST+2:00:00 DST>)
I'm trying to understand why I'm getting these results when converting time zones to UTC:
In [74]: d1 = datetime(2007, 12, 5, 6, 30,tzinfo=pytz.timezone('US/Pacific'))
In [75]: d1
Out[75]: datetime.datetime(2007, 12, 5, 6, 30, tzinfo=<DstTzInfo 'US/Pacific' LMT-1 day, **16:07:00 STD**>)
In [76]: d1.astimezone(pytz.utc)
Out[76]: datetime.datetime(2007, 12, 5, 14, 23, tzinfo=<UTC>)
Why did 6:30am become 2:23pm?
On the other hand, if I use the following approach, I get the expected result:
In [90]: d2 = datetime(2007, 12, 5, 6, 30)
In [91]: uspac = pytz.timezone('US/Pacific')
In [92]: d2_aware = uspac.localize(d2)
In [94]: d2_aware.astimezone(pytz.utc)
Out[94]: datetime.datetime(2007, 12, 5, 14, 30, tzinfo=<UTC>)
What I got is just a workaround, the simple rule is Never create datetime with timezone info by using datetime().
This sample would give you a hint for this. As you see, you could avoid the unexpected difference, once and only you make "naive" datetime (it is, datetime without timezone info) and then localize it (it is not applied when you create datetime on UTC though) :
import pytz
from datetime import datetime
# make Jan 1 on PDT -> UTC
pdt = pytz.timezone("America/Los_Angeles")
pdtnow1 = datetime(2014,1,1, tzinfo=pdt)
pdtnow2 = pdt.localize(datetime(2014,1,1))
pytz.utc.normalize(pdtnow1)
# > datetime.datetime(2014, 1, 1, 7, 53, tzinfo=<UTC>)
pytz.utc.normalize(pdtnow2)
# > datetime.datetime(2014, 1, 1, 8, 0, tzinfo=<UTC>)
# make Jan 1 on UTC -> PDT
utcnow1 = datetime(2014,1,1, tzinfo=pytz.utc)
utcnow2 = pytz.utc.localize(datetime(2014,1,1))
pdt.normalize(utcnow1)
# > datetime.datetime(2013, 12, 31, 16, 0,
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
pdt.normalize(utcnow2)
# > datetime.datetime(2013, 12, 31, 16, 0,
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
From the partial documentation:
http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones. [...] It is safe for timezones without daylight saving transitions though, such as UTC. [...] The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.
Unfortunately, creating timezone aware dates using this method doesn't work.
If you are using Django, they have a utility function, make_aware, that does this correctly.
from django.utils.timezone import make_aware
from pytz import timezone
unaware_datetime = datetime(2007, 12, 5)
local_datetime = make_aware(datetime(2007, 12, 5))
specific_datetime = make_aware(datetime(2007, 12, 5), timezone("Australia/Melbourne"))
If you're not using Django, then the source code for the make_aware function may give you inspiration.
I'm revisiting some questions about date and time to see if some of the newer libraries prove more helpful in situations like this (or not). pendulum is one that stores timezone with date and time, making it particularly valuable in situations such as this.
>>> import pendulum
>>> d1 = pendulum.datetime(2007,12,5,6,30, tzinfo='US/Pacific')
>>> d1
<Pendulum [2007-12-05T06:30:00-08:00]>
>>> d1.timezone
<Timezone [US/Pacific]>
>>> d1.astimezone(tz='UTC')
<Pendulum [2007-12-05T14:30:00+00:00]>
Lots of other sweet features too.
Print d2_aware before .astimezone and you see PST-1 (Pacific Standard Time) but in first example you have LMT-1 (Local Mean Time) - and probably it can give 7 minutes difference.
But I don't know why pytz use different timezones.
I'm trying to understand why I'm getting these results when converting time zones to UTC:
In [74]: d1 = datetime(2007, 12, 5, 6, 30,tzinfo=pytz.timezone('US/Pacific'))
In [75]: d1
Out[75]: datetime.datetime(2007, 12, 5, 6, 30, tzinfo=<DstTzInfo 'US/Pacific' LMT-1 day, **16:07:00 STD**>)
In [76]: d1.astimezone(pytz.utc)
Out[76]: datetime.datetime(2007, 12, 5, 14, 23, tzinfo=<UTC>)
Why did 6:30am become 2:23pm?
On the other hand, if I use the following approach, I get the expected result:
In [90]: d2 = datetime(2007, 12, 5, 6, 30)
In [91]: uspac = pytz.timezone('US/Pacific')
In [92]: d2_aware = uspac.localize(d2)
In [94]: d2_aware.astimezone(pytz.utc)
Out[94]: datetime.datetime(2007, 12, 5, 14, 30, tzinfo=<UTC>)
What I got is just a workaround, the simple rule is Never create datetime with timezone info by using datetime().
This sample would give you a hint for this. As you see, you could avoid the unexpected difference, once and only you make "naive" datetime (it is, datetime without timezone info) and then localize it (it is not applied when you create datetime on UTC though) :
import pytz
from datetime import datetime
# make Jan 1 on PDT -> UTC
pdt = pytz.timezone("America/Los_Angeles")
pdtnow1 = datetime(2014,1,1, tzinfo=pdt)
pdtnow2 = pdt.localize(datetime(2014,1,1))
pytz.utc.normalize(pdtnow1)
# > datetime.datetime(2014, 1, 1, 7, 53, tzinfo=<UTC>)
pytz.utc.normalize(pdtnow2)
# > datetime.datetime(2014, 1, 1, 8, 0, tzinfo=<UTC>)
# make Jan 1 on UTC -> PDT
utcnow1 = datetime(2014,1,1, tzinfo=pytz.utc)
utcnow2 = pytz.utc.localize(datetime(2014,1,1))
pdt.normalize(utcnow1)
# > datetime.datetime(2013, 12, 31, 16, 0,
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
pdt.normalize(utcnow2)
# > datetime.datetime(2013, 12, 31, 16, 0,
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
From the partial documentation:
http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones. [...] It is safe for timezones without daylight saving transitions though, such as UTC. [...] The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.
Unfortunately, creating timezone aware dates using this method doesn't work.
If you are using Django, they have a utility function, make_aware, that does this correctly.
from django.utils.timezone import make_aware
from pytz import timezone
unaware_datetime = datetime(2007, 12, 5)
local_datetime = make_aware(datetime(2007, 12, 5))
specific_datetime = make_aware(datetime(2007, 12, 5), timezone("Australia/Melbourne"))
If you're not using Django, then the source code for the make_aware function may give you inspiration.
I'm revisiting some questions about date and time to see if some of the newer libraries prove more helpful in situations like this (or not). pendulum is one that stores timezone with date and time, making it particularly valuable in situations such as this.
>>> import pendulum
>>> d1 = pendulum.datetime(2007,12,5,6,30, tzinfo='US/Pacific')
>>> d1
<Pendulum [2007-12-05T06:30:00-08:00]>
>>> d1.timezone
<Timezone [US/Pacific]>
>>> d1.astimezone(tz='UTC')
<Pendulum [2007-12-05T14:30:00+00:00]>
Lots of other sweet features too.
Print d2_aware before .astimezone and you see PST-1 (Pacific Standard Time) but in first example you have LMT-1 (Local Mean Time) - and probably it can give 7 minutes difference.
But I don't know why pytz use different timezones.
I'm currently working on the backend for a calendaring system that returns naive Python datetimes. The way the front end works is the user creates various calendar events, and the frontend returns the naive version of the event they created (for example, if the user selects October 5, 2020 from 3:00pm-4:00pm, the frontend returns datetime.datetime(2020, 10, 5, 15, 0, 0) as the start and datetime.datetime(2011, 10, 5, 16, 0, 0) as the end.
What I need to do is to take the naive datetime and convert it into UTC for storage in a database. Each user of the system has already specified their timezone preference, so the naive datetime is considered to be of the same timezone as their timezone preference. Obviously the datetimes need to be stored relative to UTC so that if users change their timezone, existing events will still render at the correct time that they scheduled them.
The frontend is outside my control, so I can't change the data that I'm receiving. The database design is also outside my control, so I can't change what data is being stored and how.
Here is the approximate approach I have taken so far:
import pytz
def convert_to_UTC(naive_datetime, user_tz_preference):
user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
utc_datetime = user_datetime.astimezone(pytz.utc)
The problem I ran into is related to Daylight Savings Time:
>>> from datetime import datetime
>>> import pytz
>>> user_tz_preference = pytz.timezone('US/Pacific')
>>> naive_datetime = datetime(2011, 10, 26, 12, 0, 0)
>>> user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
>>> user_datetime
datetime.datetime(2011, 10, 26, 12, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
>>> received_utc = user_datetime.astimezone(pytz.utc)
>>> received_utc
datetime.datetime(2011, 10, 26, 20, 0, tzinfo=<UTC>)
>>> expected_utc = datetime(2011, 10, 26, 19, 0, tzinfo=pytz.utc)
>>> expected_utc == received_utc
False
Notice that using 'replace' sets the timezone to PST instead of PDT regardless of the date, which gives it a UTC offset of 8 hours instead of the expected 7 hours DST offset, so the time ends up being saved incorrectly.
What options do I have for converting the naive datetime to the correct PDT (or other timezone-relative DST) tzinfo?
(Also, please note that not all users live in a timezone that observes DST, or may live in a timezone that switches over at different times, so in order to do a solution like a timedelta correction before saving, I would need to know if the timezone supports DST, and on which dates it switches over).
Pytz's localize function can do this: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
from datetime import datetime
import pytz
tz = pytz.timezone('US/Pacific')
naive_dt = datetime(2020, 10, 5, 15, 0, 0)
utc_dt = tz.localize(naive_dt, is_dst=None).astimezone(pytz.utc)
# -> 2020-10-05 22:00:00+00:00
With zoneinfo from Python 3.9's standard lib:
from datetime import datetime
from zoneinfo import ZoneInfo
naive_datetime = datetime(2011, 10, 26, 12, 0, 0)
user_tz_preference = ZoneInfo("America/Los_Angeles") # former US/Pacific
# it is safe to replace the tzinfo:
user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
# ...or set it directly:
user_datetime = datetime(2011, 10, 26, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
# astimezone works as before:
utc_datetime = user_datetime.astimezone(ZoneInfo("UTC"))
print(repr(user_datetime))
# datetime.datetime(2011, 10, 26, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='US/Pacific'))
print(user_datetime.isoformat())
# 2011-10-26T12:00:00-07:00
print(utc_datetime.isoformat())
# 2011-10-26T19:00:00+00:00
docs