Django ReportLab Timezone - django

I'm using ReportLab in Django. I have a model with the following field:
time_stamp = models.DateTimeField(auto_now_add=True)
And my TIME_ZONE variable in settings.py is set to:
Africa/Johannesburg
I use a formset to populate this model. The time_stamp field saves correctly with the correct time zone, but when I place the time_stamp in my ReportLab pdf, the time zone is set to UTC.
For example:
time_stamp in the saved model (as str(time_stamp)[:19] is:
2015-03-04 07:57:28
But time_stamp in pdf document (as str(time_stamp)[:19] is:
2015-03-04 05:57:28
Exactly 2 hours earlier (Africa/Johannesburg is UTC + 2hours).
How can I set the time zone for ReportLab? Should it be specified in settings.py or in views.py while generating the pdf? If there is no solution, how do I add 2 hours to the time_stamp?
Some answers suggested changing auto_now_add=True with default=datetime.datetime.now(), but this creates a warning while migrating the database (Naive expression used).

I'm not a user of Reportlab but in general I don't think setting USE_TZ=False is the right approach to solve your problem. Set it back to True and instead of truncating your time stamp yourself like:
str(time_stamp)[:19]
You should try applying Django's date template filter in your template, e.g.:
{{ time_stamp|date:"SHORT_DATETIME_FORMAT" }}
It can be confusing how Django is handling timezones. But it's best practice to save timestamps in your database in UTC. You may want to refer to Django's timezone FAQ:
https://docs.djangoproject.com/en/dev/topics/i18n/timezones/#time-zones-faq

Related

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.

How to convert django default timezone to different format

Time format
"2018-12-13T05:20:06.427Z"
django providing time zone in above format when i am fetching data from database using ORM query.
In my model field is in below way.
models.DateTimeField(auto_now=True, blank=True,null=True)
How can i convert it into "24 feb 2018" like this
Apart from #Sosthenes Kwame Boame answer, you can use strftime for formatting.
import datetime
time = datetime.datetime.now().strftime('%d %b %Y')
Out[13]: '13 Dec 2018'
Instead of passing datetime module to time variable, you should pass your model field's value.
If you want to learn more about format type then you can visit the documentation page.
I am guessing you want to display this on the frontend?
You need to do this in your template:
{{ object_name.datefield_name|date:'j b Y' }}
So you call the datefield and render it with the '|date' tag with a format assigned ':'FORMAT''.
Learn more about the date tag, along with the various formats here: https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#date

Django-Postgres: how to group by DATE a datetime field with timezone enabled

I am having this problem with prostgresql and django:
I have a lot of events that were created on a certain date at a certain time which is stored in a datetime field created .
I want to have aggregations based on the date part of the created field. The simplest examples is: how many event are in each day of this month?.
The created field is timezone aware. So the result should change depending on the timezone the user is in. For example if you created 2 events at 23:30 UTC time on 2017-10-02 if you view them from UTC-1 you should see them on 3rd of October at 00:30 and the totals should add for the 3rd.
I am struggling to find a solution to this problem that works with a lot of data. So doing for each day and SQL statement is not an option. I want something that translates into:
SELECT count(*) from table GROUP BY date
Now I found a solution for the first part of the problem:
from django.db import connection
truncate_date = connection.ops.date_trunc_sql('day', 'created')
queryset = queryset.extra({'day': truncate_date})
total_list = list(queryset.values('day').annotate(amount=Count('id')).order_by('day'))
Is there a way to add to this the timezone that should be used by the date_trunc_sql function to calculate the day? Or some other function before date_trunc_sql and then chain that one.
Thanks!
You're probably looking for this: timezone aware date_trunc function
However bear in mind this might conflict with how your django is configured. https://docs.djangoproject.com/en/1.11/topics/i18n/timezones/
Django 2.2+ supports the TruncDate database function with timezones
You can now do the following to :
import pytz
east_coast = pytz.timezone('America/New_York')
queryset.annotate(created_date=TruncDay("created", tzinfo=east_coast))
.values("created_date")
.order_by("created_date")
.annotate(count=Count("created_date"))
.order_by("-created_date")

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 Templates: How to show the time differences between a Datastore time and current time?

I am working on a django- Google app engine project. A user inserts some value and the datetime field is auto filled using google app engine DateTimeProperty(auto_now_add=True) property. Now, I have to display on the template the difference of this time and the current time when the user is viewing the result.
For e.g. if the inserted time was 5:00 and the user is viewing the post at 6:00, the result should be 1 hour. Is there any builtin filter available with the Django templates to perform the same? I tried timesince as:
{{ topic.creation_date| timesince: now }}
where creation_date is a datetime field. But its not working.
Please suggest.
Thanks in advance.
Do you have a variable called "now" in your context? If not, that will fail.
However, you don't actually need one, because if you call the timesince filter without an argument, it will use the current time. So just do:
{{ topic.creation_date|timesince }}
why don't you use time.time()? And for creation date you first have to insert given row into database. That means you can read it.
from datetime import timedelta
yourmodel.save()
cdate= yourmodel.creation_date
seconds = time.time() - time.mktime(cdate.timetuple())
timediff = str(timedelta(seconds=seconds))