Django Sitemaps using incorrect timezone - django

To sum up the issue - django sitemaps seems to be using the incorrect timezone when processing dates which make up my urls.
1.) The data in the model:
fixture_datetime = 2016-04-03 06:15:00
2.) timezone in settings -
TIME_ZONE = 'Australia/Sydney'
postgres timezone has also been set to Australia/Sydney
3.) Value stored in Database:
"2016-04-03 06:15:00+10"
4.) The problem is the sitemap is retuning the URL as:
http://127.0.0.1:8000/sports/super-rugby/bulls/cheetahs/2016/04/02/
when it should be as follows as per the above data:
http://127.0.0.1:8000/sports/super-rugby/bulls/cheetahs/2016/04/03/
in the error page it is returning the date as:
"2016-04-03 06:15:00+00"
How can I get the sitemaps to use the correct timezone? Is there a setting i am forgetting?

Related

Django: Trying to add 'SHORT_TIME_FORMAT' somehow changes time zone to Chicargo

I required some extra date/time formats in addition to ones Django ships with, sI created these new formats:
SHORT_TIME_FORMAT
FILE_DATETIME_FORMAT
ISO8601_DATETIME_FORMAT.
So that I could use them in templates like this:
{{ value|date:"SHORT_DATE_FORMAT" }}
And In code like this:
from django.utils import formats
formats.date_format(value, `SHORT_TIME_FORMAT`)
I created the following code in utils/time/formats.py:
from collections import ChainMap
from django.utils import formats
DJANGO_DATETIME_FORMATS = {
'DATETIME_FORMAT': 'd M Y, H:i',
'DATE_FORMAT': 'j M Y',
'TIME_FORMAT': 'H:i:s',
'SHORT_DATETIME_FORMAT': 'Y-m-d H:i:s',
'SHORT_DATE_FORMAT': 'Y-m-d',
'MONTH_DAY_FORMAT': 'd M',
'YEAR_MONTH_FORMAT': 'Y M',
}
EXTRA_DATETIME_FORMATS = {
'SHORT_TIME_FORMAT': 'H:i',
'FILE_DATETIME_FORMAT': 'Y-m-d H.i.s',
'ISO8601_DATETIME_FORMAT': 'c', # Compatible with Javascript passing
}
def set_formats(globals_dict):
"""Returns a set of datetime formats for use across the whole project
Contains a union of the default formats along with the additional ones.
To add a new format, one has to perform the following two actions:
1. Add the name of the format to formats.FORMAT_SETTINGS
2. Add a variable with the same name as the variable to the settings.py
file with the value assigned to the required format.
globals_dict is the globals from the settings.py file
"""
formats.FORMAT_SETTINGS = frozenset(formats.FORMAT_SETTINGS | EXTRA_DATETIME_FORMATS.keys())
all_formats = ChainMap(DJANGO_DATETIME_FORMATS, EXTRA_DATETIME_FORMATS)
for setting, value in all_formats.items():
globals_dict[setting] = value
However when I add it to my settings.py file as so:
# settings.py
from utils.time.formats import set_formats
set_formats(globals())
My timezone changes to Chicargo, even though in settings.py I have:
TIME_ZONE = 'Africa/Johannesburg'
After research I found out that Django's default timezone is Chicargo, which is 7 Hrs behind me (I am UTC+02).
I can test it like this:
$ python manage.py shell
> from django.utils import timezone
> from django.conf import settings
> print(f"The settings.py TIME_ZONE value is: {settings.TIME_ZONE=}")
The settings.py TIME_ZONE value is: settings.TIME_ZONE='Africa/Johannesburg'
> print("Using Django local time, now is:")
Using Django local time, now is:
> print(timezone.localtime())
2022-07-27 20:54:35.293958+02:00
If I don't use set_formats, the datetime is correctly in my local time, if I add my set_formats function, then datetime is 7 Hrs off, and in the admin interface and other date/times on my website are all 7 hours off, even though settings.TIME_ZONE reports Africa/Johannesburg.
this code used to work, but at some point recently stopped working. I did change from pytz to zoneinfo, so that might be the reason, but I was a little bit sure it worked after the change.
Why is the timezone changing? How can I add a date/time format so it can be used in templates and in code with the django.utils.formats.date_format function?

Get max and min formatted date values from queryset in Django

I have a one to many relation between session and camp. Now I have to get the max and min dates of all camps combined for a particular session.
I am able to do it like this:
sess = Session.objects.last()
max_min_dates = sess.camp.aggregate(Min('start_date'), Max('end_date'))
But if I try to send this from HttpResponse then I am getting this error:
TypeError: Object of type 'date' is not JSON serializable
So I need to send the formatted date values in that. How can I modify the above code to get the same?
The default encoder for json.dumps() does not support date encoding (ie. can't convert date into str). You can use django encoder instead, it supports a few more data types see the link for more info.
Django Ref
import json
from django.core.serializers.json import DjangoJSONEncoder
json_str = json.dumps(max_min_dates, cls=DjangoJSONEncoder)

DJANGO datetime wrong on template even after timezone setup

I have a DJANGO application, and I am completely lost about times.
I am located in Budapest, Hungary, my current time for these test is: 09:26
I have my timezone correctly set in settings.py
...
TIME_ZONE = 'Europe/Budapest'
USE_I18N = True
USE_L10N = True
USE_TZ = True
...
Lets say I store a datetime object in my SQLite database, the admin page will show the correct time:
If I query that data in a view the date is suddenly wrong.
2020-10-06 07:26:41.040463+00:00
I have read solutions that I need to activate timezone in my view, but it does not work:
tzname = pytz.timezone("Europe/Budapest")
timezone.activate(tzname)
for i in MyObject.objects.all():
print(i.date)
returns
2020-10-06 07:26:41.040463+00:00
I usually fill my templates with Ajax JS calls, so I was not able to try template filters like this:
{{ value|timezone:"Europe/Budapest" }}
How can I change the time so that my JsonResponse sends the correct time to my templates?
Consider carefully the first sentence in the timezone 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.
Note that translation to local time zones only happens in templates and forms—not database queries, views, or other functions. There are many good reasons for that, one of which is that converting to a different timezone can lose information. Let's say that the datetime you got back from the database was 2:30am on October 25, 2019, Budapest time. What moment in time does that represent? You can't know, because that time occurred twice due to daylight savings time.
So the behavior you're seeing is entirely correct. If you want to convert a datetime to the local time in code use localtime():
from django.utils.timezone import localtime
local = localtime(myobject.timestamp)
When you create your models, you could set the datetime to your local timezone.
from django.utils import timezone
date = models.DateTimeField(
default=timezone.localtime(timezone.now()),
blank=True
)
timezone.localtime(timezone.now()) will give you the time based on the TIME_ZONE given in the settings.
Moment JS:
https://momentjs.com/timezone/docs/#/using-timezones/converting-to-zone/

converting time in django

hi i'm new in working with datetime objects in django
all I know now is that instead of python's datetime.datetime.now() we should use django's timezone.now(), i've also set TIMEZONE and USE_TZ=True in settings.py
but my problem is now for converting these types of time. as far as I know, even if we use timezone.now() for saving in database, django uses UTC time to store in DB. so I need a simple syntax for converting UTC time into my local time which is set in settings.py and vice versa to get local time from human and return local time.
i've also seen that django has some template tags to do that, but since i am doing this mostly for a REST API with django-rest for an android app, i need to be able to do this in python syntax.
thanks everyone, I hope I could be clear at what I mean :)
In templates, Django will automatically convert your model dates (stored as UTC) to the current time zone. The current time zone is set by settings.TIMEZONE unless you explicitly change it somewhere else. You don't even need to use special template tags. This will convert fine:
{{ MyModel.my_date }}
Outside of templates, there is a tool called localtime that you can use to do the same conversion.
from django.utils.timezone import localtime
...
local_date = localtime(MyModel.my_date)
print( str(MyModel.my_date) ) # UTF time
print( str(local_date) ) # local time
The datetime returned by localtime is time zone aware. If you ever need a time zone naive datetime, you can convert it like this:
my_date = localtime(MyModel.my_date).replace(tzinfo=None)
If, in settings.py we have the following:
from pytz import timezone
LOCAL_TZ = pytz.timezone('CST6CDT') # asume that local timezone is central, but you can use whatever is accurate for your local
Now, you can use this to convert from utc time to local
import pytz
from django.conf import settings
def to_local_dttm(utc_dttm):
return utc_dttm.astimezone(settings.LOCAL_TZ)
def to_utc_dttm(local_dttm):
return local_dttm.astimezone(pytz.utc)

Django File-based session doesn't expire

I just realized that my session doesn't expire when I use file-based session engine. Looking at Django code for file-based session, Django doesn't store any expiration information for a session, thus it's never expire unless the session file gets deleted manually.
This looks like a bug to me, as the database-backed session works fine, and I believe regardless of what session back-end developer chooses, they all should behave similarly.
Switching to database-backed session is not an option for me, as I need to store user's session in files.
Can anyone shed some lights?
Is this really a bug?
If yes, how do you suggest me to work around it?
Thanks!
So it looks like you're right. At least in django 1.4, using django.contrib.sessions.backends.file totally ignores SESSION_COOKIE_AGE. I'm not sure whether that's really a bug, or just undocumented.
If you really need this functionality, you can create your own session engine based on the file backend in contrib, but extend it with expiry functionality.
Open django/contrib/sessions/backends/file.py and add the following imports:
import datetime
from django.utils import timezone
Then, add two lines to the load method, so that it appears as below:
def load(self):
session_data = {}
try:
session_file = open(self._key_to_file(), "rb")
if (timezone.now() - datetime.datetime.fromtimestamp(os.path.getmtime(self._key_to_file()))).total_seconds() > settings.SESSION_COOKIE_AGE:
raise IOError
try:
file_data = session_file.read()
# Don't fail if there is no data in the session file.
....
This will actually compare the last modified date on the session file to expire it.
Save this file in your project somewhere and use it as your SESSION_ENGINE instead of 'django.contrib.sessions.backends.file'
You'll also need to enable SESSION_SAVE_EVERY_REQUEST in your settings if you want the session to timeout based on inactivity.
An option would be to use tmpwatch in the directory where you store the sessions
I hit similar issue on Django 3.1. In my case, my program calls the function set_expiry(value) with an integer argument (int data type) before checking session expiry.
Accoring to Django documentation, the data type of argument value to set_expiry() can be int , datetime or timedelta. However for file-based session, expiry check inside load() doesn't work properly only if int argument is passed to set_expiry() beforehand, and such problem doesn't happen to datetime and timedelta argument of set_expiry().
The simple solution (workaround?) is to avoid int argument to set_expiry(value), you can do so by subclassing django.contrib.sessions.backends.file.SessionStore and overriding set_expiry(value) (code sample below), and change parameter SESSION_ENGINE accordingly in settings.py
from datetime import timedelta
from django.contrib.sessions.backends.file import SessionStore as FileSessionStore
class SessionStore(FileSessionStore):
def set_expiry(self, value):
""" force to convert to timedelta format """
if value and isinstance(value, int):
value = timedelta(seconds=value)
super().set_expiry(value=value)
Note:
It's also OK to pass timedelta or datetime to set_expiry(value) , but you will need to handle serialization issue on datetime object.