Simply save time as UTC with Django - python

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.

Related

Timezone not working properly in Django

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>)

Confused by django timezone support

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).

Django: activate() not showing effect

I have the following line in python manager.py shell:
>>> import pytz
>>> from django.utils import timezone
>>> zone = "Asia/Kolkata"
>>> timezone.activate(pytz.timezone(zone))
>>> timezone.now()
datetime.datetime(2014, 12, 17, 1, 52, 0, 411937, tzinfo=<UTC>)
But the output which I get is still using UTC. Should not it be converted into "Asia/Kolkata"?
UPDATE
If i use commands suggested by dazedconfused below:
zone = "Asia/Kolkata"
if zone:
timezone.activate(pytz.timezone(zone))
else:
timezone.deactivate()
utc_date = datetime.utcnow()
aware_date = timezone.make_aware(utc_date, timezone.utc)
l_time = timezone.localtime(aware_date, timezone.get_current_timezone())
And now when i try to save it on my database(Mongodb on Mongolab) it gets saved as UTC
I have a DateTimeField in my database.
Although when i save it as a simple string it gets saved in current timezone that is "Asia/kolkata" Output as string: 2014-12-17 11:01:53.028852+05:30
It actually successfully sets the current time zone to "Asia/Kolkata"
You can verify by:
...
>>> timezone.get_current_timezone_name()
'Asia/Kolkata'
From the django documentation:
now():
Returns a datetime that represents the current point in time. Exactly
what’s returned depends on the value of USE_TZ:
If USE_TZ is False, this will be a naive datetime (i.e. a datetime
without an associated timezone) that represents the current time in
the system’s local timezone.
If USE_TZ is True, this will be an aware
datetime representing the current time in UTC. Note that now() will
always return times in UTC regardless of the value of TIME_ZONE; you
can use localtime() to convert to a time in the current time zone.
So, if your system's local timezone is 'Asia/Kolkata', you can set USE_TZ to False in your settings.py and the timezone.now() will return what you want.
Or, you'll have to use localtime() to convert the timezone to yours (continue from your shell results):
...
>>> import datetime
>>> utc_date = datetime.datetime.utcnow()
>>> aware_date = timezone.make_aware(utc_date, timezone.utc)
>>> timezone.localtime(aware_date, timezone.get_current_timezone())
datetime.datetime(2014, 12, 17, 8, 0, 36, 598113, tzinfo=<DstTzInfo 'Asia/Kolkata' IST+5:30:00 STD>)
Lastly, here's the documentation of the make_aware() function

In python how do I convert a datetime in a specific local time (not my local) to UTC

I'm pulling data from a London based service and they are giving me date&time info in London local time.So UTC in winter and BST(UTC+1) in summer.
Internally we use UTC for everything, in Python how do I convert the London stuff to UTC in a way that will account for daylight savings?
I appreciate that some times around the DST rollover are ambiguous, that's acceptable as long as it works the rest of the year.
For completeness, I'm getting the following info from them:
dt="2012-10-12T19:30:00"
lcnid="LDN"
locale="en-gb"
You need to use a timezone object; these don't come with Python itself as the data changes too often. The pytz library is easily installed though.
Example conversion:
>>> import pytz
>>> import datetime
>>> bst = pytz.timezone('Europe/London')
>>> dt = datetime.datetime.strptime('2012-10-12T19:30:00', '%Y-%m-%dT%H:%M:%S')
>>> dt
datetime.datetime(2012, 10, 12, 19, 30)
>>> bst.localize(dt)
datetime.datetime(2012, 10, 12, 19, 30, tzinfo=<DstTzInfo 'Europe/London' BST+1:00:00 DST>)
>>> bst.localize(dt).astimezone(pytz.utc)
datetime.datetime(2012, 10, 12, 18, 30, tzinfo=<UTC>)
import pytz
utc = pytz.utc
print(utc.localize(datetime.datetime(2012,10,12,19,30,00)))

Python date iso8601 format with timezone designator

I'm sending some dates from server that has it's time in gmt-6 format, but when i convert them to isoformat i don't get the tz designator at the end.
I'm currently setting the date like this:
date.isoformat()
but I'm getting this string: 2012-09-27T11:25:04 without the tz designator.
how can I do this?
You're not getting the timezone designator because the datetime is not aware (ie, it doesn't have a tzinfo):
>>> import pytz
>>> from datetime import datetime
>>> datetime.now().isoformat()
'2012-09-27T14:24:13.595373'
>>> tz = pytz.timezone("America/Toronto")
>>> aware_dt = tz.localize(datetime.now())
>>> datetime.datetime(2012, 9, 27, 14, 25, 8, 881440, tzinfo=<DstTzInfo 'America/Toronto' EDT-1 day, 20:00:00 DST>)
>>> aware_dt.isoformat()
'2012-09-27T14:25:08.881440-04:00'
In the past, when I've had to deal with an unaware datetime which I know to represent a time in a particular timezone, I've simply appended the timezone:
>>> datetime.now().isoformat() + "-04:00"
'2012-09-27T14:25:08.881440-04:00'
Or combine the approaches with:
>>> datetime.now().isoformat() + datetime.now(pytz.timezone("America/Toronto")).isoformat()[26:]
'2012-09-27T14:25:08.881440-04:00'
It is much easier to deal with dates with a specialized module such as arrow or delorean
>>> import arrow
>>> arrow.now().isoformat()
'2020-11-25T08:10:39.672624+01:00'

Categories