problem with timezone and datetime libs in Django 2 and python 3 - django

I'm a bit confused by the daylight savings handling. I have working on web notifier application in Django 2.0.6 framework and python 3.6. this application provide scheduling notification. for example users schedule message for some hours later or a few days later. this website is available in only one time zone.
I have use this settings in Django:
...
TIME_ZONE = 'Europe/London'
USE_I18N = True
USE_L10N = True
USE_TZ = True
...
when I run this code in python manage.py shell:
>>> from django.utils import timezone
>>> from datetime import datetime
>>> timezone.now()
>>> datetime.now()
django timezone.now() return to me datetime with tzinfo UTC and datetime.now() return to me correct time information with correct time zone info. when I made USE_TZ=False in django setting to find out timezone.now() in django return correct time zone information like datetime.now(). in Django time zone document said: ''This is handy if your users live in more than one time zone and you want to display datetime information according to each user’s wall clock.good practice to store data in UTC in your database. The main reason is Daylight Saving Time (DST). Many countries have a system of DST, where clocks are moved forward in spring and backward in autumn. If you’re working in local time, you’re likely to encounter errors twice a year, when the transitions happen''
Depending on my project type I'm looking for that what is the best practice for time zone config in my Django settings project and solve that problem Django talk about that in time documentation. Please help me
Thanks.

You should enable USE_TZ. If you look carefully, you'll see that datetime.now() does not specify the timezone (naive time), whereas timezone.now() does (timezone-aware time). With USE_TZ=True, all datetime objects are transformed to the absolute UTC time and timezone aware, so that there's no ambiguity about the actual moment in time.
timezone.now() should give you 14:28 with tzinfo=UTC when in London it's 15:28 (now, in April):
>>> timezone.now()
datetime.datetime(2020, 4, 6, 14, 41, 13, 296983, tzinfo=<UTC>)
>>> datetime.now()
datetime.datetime(2020, 4, 6, 15, 42, 3, 320929) # no tzinfo
Imagine your user sets an alarm for October 25th, 2020, 2:30am. Now this is ambiguous, since this time will occur twice in London: Once during DST and an hour later after we've gone back to winter time and moved our clocks from 3am to 2am. But if you change this to UTC, this won't be ambiguous, there's only one 2020/10/25 2:30 UTC (when Europe/London is in winter time when GMT = UTC). So it either has to be 1:30 or 2:30 (UTC).
So definitely if you're making an app where there's scheduling, use USE_TZ otherwise you'll have issues around DST.

Related

Saving objects to postgresql with DJango in UTC instead of local timezone?

I have a column in my table called "signed_up_date", which I am using the auto_now_add=True parameter with the DateTimeField() function.
I want this function to save timestamps to the PostgreSQL DB in UTC instead of my local time (EST) which it is currently doing.
Barebones version of the model for quick ref below:
class TutorTest(models.Model):
signed_up_on = models.DateTimeField(auto_now_add=True)
Settings.py
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
I thought based on the docs, if I have Time_Zone set to UTC and UTC_TZ set to True, then the default behavior of Django would be to save as UTC.
I know when I check postgresql timezone I get:
SHOW TIMEZONE;
TimeZone
------------
US/Eastern
(1 row)
I just tried a new model with timezone.now() and it looks like in the db shell I get:
datetime.datetime(2022, 9, 21, 14, 36, 16, 670726, tzinfo=datetime.timezone.utc)
But the db saved it as:
2022-09-21 10:35:26.633116-04
I guess it's the DB in this situation? Confused lol
*** EDIT ***
I think I figured it out. The database takes a UTC time and converts to EST, but when I pull the time out django reads it as UTC. Which works for me lol.
The timestamp which is actually saved in the database would be in UTC, but if you are using some sort of tool such as DBeaver (for example) to look into and query the datbase, then that tool would convert the timestamps as per what is put in its configuration and show those converted timestamps to you. But the underlying data in the database is still in UTC.
Hope this helps.

How to get queryset in django?

I am trying to send an email to those orders that is created 5 minutes before the datetime.now(). I try to filter the orders but it is not working, it is not giving me any queryset. How to do this? I am sharing my code.
def my_email():
now = datetime.now() - timedelta(minutes=5) # 11:55
now = now.replace(tzinfo=pytz.utc)
print(now)
order = Order.objects.filter(createdAt__gt = now)
print(order)
for o in order:
print(o._id)
You should not replace the timezone with UTC. now() will obtain the current datetime for a given timezone. If you replace that with UTC, then the result can be several hours ahead or behind the current time.
It might also be better to make use of a Now() expression [Django-doc], which will use the clock of the database, we thus can filter with:
from django.db.models.functions import Now
from datetime import timedelta
order = Order.objects.filter(createdAt__gt=Now()-timedelta(minutes=5))
If you plan to run this to send emails for orders the last five minutes, this will however be quite error-prone: since a scheduled task can always have a few milliseconds of delay, it thus means that certain items can, by the time the query is running, be considered too old. You probably might want to use a BooleanField that indicates if an email has been sent.

Django. How to correctly save time & timezones

I have this code that behaves in a rather strange way and opens the question, how should I deal with timezones? So, first I have a datetime object I build from the info a user posts:
time_zone = request.POST.get("time_zone")
date_start = request.POST.get("date_start")
time_day = request.POST.get("time_day")
time_zone_obj = pytz.timezone("Etc/" + time_zone) # GMT + 2 in this example
date_start = datetime.strptime(date_start, "%d/%m/%Y")
date_start = date_start.replace(tzinfo=time_zone_obj)
time_day = datetime.strptime(time_day, "%I:%M %p")
date_start = date_start.replace(hour=time_day.hour, minute=time_day.minute)
...
event.date_start = date_start
event.save()
print("event.date_start.hour:%s" % event.date_start.hour)
print("event.date_start.tzinfo:%s" % event.date_start.tzinfo)
print("is_aware(event.date_start:%s)" % is_aware(event.date_start))
return redirect("event_detail", event_id=event.id)
This prints event.date_start.hour:6, event.date_start.tzinfo:Etc/GMT+2 and is_aware:True. Then, inmediatlty after saving the object and printing the hour, it redirects to the event_detail view, very simple:
def event_detail(request, event_id):
event = get_object_or_404(Event, id=event_id)
print("event.date_start.hour:%s" % event.date_start.hour)
print("event.date_start.tzinfo:%s" % event.date_start.tzinfo)
...
And it prints event.date_start.hour:8 and event.date_start.tzinfo:UTC. (it has replaced the tz info with UTC) I don't understand why. I am saving the object with a clear tz_info. Plz note that I printed the hour after I saved the object and then after I retrieved it in the other view. It has a difference of two hours that must have something to do with the timezone the user selected (GMT + 2). Why is this? Which is the best way to save this data?
The user submits "6:00 AM" + "GMT+2" in the form and then later when I want to show the time in the event detail html ({{ event.date_start|date:"h:i A" }}) it displays "8:00 AM".
I assume you're using PostgreSQL to save the timezone aware timestamp.
It's important to understand that (contrary to the name and popular belief) PostgreSQL doesn't save the timezone of the timezone aware timestamp. It's just a way to tell PostgreSQL that the value is not in some local time, but is timezone aware.
PostgreSQL then converts it to UTC and stores as such. If the original timezone is important, you need to store it separately.
More info on the topic: https://www.postgresqltutorial.com/postgresql-timestamp/
The best way to store this data is a separate column (usually called timezone). I use https://pypi.org/project/django-timezone-field/
Then either activate timezone (https://docs.djangoproject.com/en/3.1/ref/utils/#django.utils.timezone.activate) or use localtime (https://docs.djangoproject.com/en/3.1/ref/utils/#django.utils.timezone.localtime) util function.
As per the Django docs,
"When support for time zones is enabled, Django stores DateTime information in UTC in the database. It’s still good practice to store data in UTC in your database. The main reason is the Daylight Saving Time (DST). "
So saving DateTime in UTC format in the database as expected.
Now, going ahead with your requirement. In order to display the time back in the timezone which was used for saving you need to add a column in the DB to store the timezone info.
While retrieving the DateTime, convert it into the required timezone back using the tzinfo stored in DB.
This is the correct way of doing. Hope this helps you understand better.

Django template tag, python timezone aware dates different results

I have :
TIME_ZONE = 'Europe/Paris'
USE_L10N = True
USE_TZ = True
in my settings.py file. I am living in Istanbul/Turkey and there is one hour difference between Paris and Istanbul.
In the admin side when selecting a date, django correctly shows 1 hour difference. And using template tag i am getting the datetime i have set in the admin.
But when i pass the datetime via python using beginning_date.strftime("%H:%M") python substracts 1 hour from the value that was set via admin which is not true.
How can i solve this?
Use the Django template defaultfilters to format your dates in Python code.
from django.template.defaultfilters import date as _date
_date(datetime_object, "%H:%M")
And, maybe related: Django cannot reliably use alternate time zones in a Windows. See documentation.
I don't think Turkey has anything to do with it.
My guess is that the one-hour difference you're seeing is between the Paris timezone—which is being used, by default, to interpret and display dates—and UTC—which is being used to store the datetime, and which is the timezone of the datetime returned from the database.
If that's correct, then you can just use django.utils.timezone.localtime to convert the datetime to the current time zone (which by default will be TIME_ZONE):
localtime(beginning_date).strftime("%H:%M")

Django timezone confusion

The timezone for my users is fixed to PST.
I can't make timezone.now() output the correct time. In settings.py I have the timezone set to US/Pacific and USE_TZ=False. Am I doing something wrong? The timezone.now() is 9 hours ahead.
Also, if I want to adjust the timezones some time later, is there a nice way to do it? Django's documentation seems kinda messy in this regard.
USE_TZ = False tells django to ignore timezone information. Switch it to USE_TZ = True and you should be fine.
With this in place, changing timezones in future should be as simple as updating the timezone setting as the timezone is saved with the date when USE_TZ = True
You should really have USE_TZ=True and the timezone set to US/Pacific. Then all the dates will be stored as UTC and converted to PDT on the front-end. This makes it easier if/when you have to change timezones later since all of the datetimes are stored as UTC rather than a naive PDT.