I'm working with a testcase where an organization can be suspended.
Currently I'm using freezegun to freeze a firm time, which is a datetime.datetime object with tzinfo=pytz.UTC.
In the test below you'll see a print of self.fake_datetime which returns a tzaware datetime: 2000-01-01 00:00:00+00:00.
When the test runs, I keep getting the famous RuntimeWarning:
/usr/local/lib/python2.7/dist-packages/django/db/models/fields/init.py:1447: RuntimeWarning: DateTimeField Organization.suspended received a naive datetime (2000-01-01 00:00:00) while time zone support is active.
RuntimeWarning)
import datetime
import pytz
from freezegun import freeze_time
# crop
class OrganizationSuspendTestCase(TestCase):
def setUp(self):
self.organization = OrganizationFactory()
self.fake_datetime = datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=pytz.UTC)
print self.fake_datetime
def test_suspend_organization(self):
"""
Case: An organization is being suspended with the suspend service.
Expected: The organization is updated with the suspending datetime and the reason.
"""
with freeze_time(self.fake_datetime):
mc_organization.services.organization.suspend(organization=self.organization, reason="Did not pay the bill")
self.assertEqual(self.organization.suspended, datetime.datetime(2000, 1, 1, 0, 0, 0))
I've been playing around with the freezegun timezone examples without any success to remove the runtime warning.
Any suggestions on how this should be resolved properly? I'd like to keep using Freezegun for this without a RuntimeWarning. Suppressing is an option, but I'd prefer not to.
update -- Solution based on the answer of xyres
Service was saving the datetime timezone unaware. Old situation commented and new situation is the actual code. I was thinking to much about mocking and assumed that the datetime saved in the service would be mocked with the timezone aware datetime object from the testcase by freezegun - which is not the case.
def suspend(organization, reason):
"""
Suspend an organization with the given reason.
:param mc_organization.models.Organization organization: The organization to suspend.
:param string reason: The reason of the suspending.
:return: None
"""
# organization.suspended = datetime.datetime.now() (Old sitation)
organization.suspended = timezone.now() # timezone aware situation.
organization.suspended_reason = reason
organization.save()
Seems like you're trying to save an object with a timezone-naive datetime. To get rid of this warning, just use timezone-aware datetime everywhere in your application.
Instead of managing timezone yourself manually using pytz, you can use Django's timezone module found at django.utils.timezone . It has some shortcut methods that you can use to convert naive datetime to aware datetime.
An advantage of using this is that if you ever change the timezone settings in your settings file, it will automatically pick the new timezone, whereas with pytz you'll have to manually update the new timezone everywhere.
from django.utils import timezone
fake_datetime = timezone.make_aware(timezone.datetime(2000, 1, 1, 0, 0, 0))
Related
I am trying to fetch data based on DateTime Field! I wrote the following script for the same:
import datetime as dt
from dasboard.models import Visionsystem
from django.utils.timezone import make_aware
min_dt = dt.datetime.combine(dt.date.today(), dt.time(7, 15))
max_dt = dt.datetime.combine(dt.date.today(), dt.time(15, 44))
# Changing format for Django
min_dt_aware = make_aware(min_dt)
max_dt_aware = make_aware(max_dt)
# Fetching all the data between 7:15:00 to 15:44:00 for current day
l1 = Visionsystem.objects.filter(start_datetime__range=(min_dt_aware, max_dt_aware))
print(l1) yields empty list and Checking str(l1.query) gives:
'SELECT `visionsystem`.`id`, `visionsystem`.`Start_Datetime` ... FROM `visionsystem` WHERE
`visionsystem`.`Start_Datetime` BETWEEN 2019-10-16 01:45:00 AND 2019-10-16 10:14:00'
Desired query would be:
'SELECT `visionsystem`.`id`, `visionsystem`.`Start_Datetime` ... FROM `visionsystem` WHERE
`visionsystem`.`Start_Datetime` BETWEEN 2019-10-16 07:15:00 AND 2019-10-16 15:44:00'
I don't understand why Django-ORM is querying with a different time then what's specified ?
My timezone settings ( in settings.py ):
TIME_ZONE = 'Asia/Kolkata'
USE_I18N = True
USE_L10N = True
USE_TZ = True
How do I resolve this, to fetch the required data from 7:15:00 to 15:44:00, for current day ? NOTE: I am using MySQL Database!!
models.py file:
from django.db import models
class Visionsystem(models.Model):
start_datetime = models.DateTimeField(db_column='Start_Datetime', blank=True, null=True)
class Meta:
managed = False
db_table = 'visionsystem'
MySql doesn't supports timezones, It stores datetime in UTC, while your django
application timezone is Asia/Kolkata (UTC +5:30).
Here django automatically converts your application timezone to UTC before querying to MySQL database. This makes sense as when you save data using django application, it converts datetime to UTC time, so 07:15:00 Kolkata time would be stored as 01:45:00 in UTC.
Solution:
You can store MySql data in UTC timezone, If existing data is in Kolkata timezone, run update query by subtracting -5:30 and save all new data in UTC timezone.
OR
If your application doesn't need to support multiple timezone, you can change application timezone to UTC. This is quick solution but not a good idea as timezone information is truncated.
In short
Django saves store UTC in database.
If you don't mention a timezone, the timezone you mentioned in settings.py is taken (in your case, Asia/Kolkata is taken, so make_aware function assumes, you are entering a datetime with timezone Asia/Kolkata. But as mentioned in step 1., it is saved as UTC. Both time points to same time - that is, time described by both UTC and Asia/Kolkata is same).
In addition to both 1 and 2, django convert your timezone_aware datetime (ie. min_dt_aware and max_dt_aware) to UTC, to make the correctness of your query during query execution.
Here is the detailed explanation.
Time zones overview
When support for time zones is enabled, Django stores date-time information in UTC in the database, uses time-zone-aware date-time objects internally, and translates them to the end user’s time zone in templates and forms.
Value is shown as UTC, which is printed in SQL query:
import pytz
utc = pytz.UTC
# Print the utc value of min_dt_aware and max_dt_aware
print(min_dt_aware.astimezone(utc), max_dt_aware.astimezone(utc))
# So, as you can see, these utc values are which you see in SQL queries.
You can use timezone.now(), it worked for me. For this first you need to import timezone,from django.utils import timezone https://docs.djangoproject.com/en/3.1/topics/i18n/timezones/
In my models, I have a DateTimeField say
class ReturnEvent(models.Model):
book_title = models.CharField()
return_time = models.DateTimeField()
When I retrieve the return_time to be printed, for example:
return_event = ReturnEvent.objects.get(id=1)
print(return_event.return_time.strftime("%H:%M"))
I am seeing the datetime unaware time like:
2016-06-18 08:18:00+00:00
However, I like to see the local time using strftime.
2016-06-18 10:18:00+02:00
Is there a quick solution for this?
If you have USE_TZ = True in your settings, Django stores all the datetime objects in the database as UTC and convert them to your TIME_ZONE=XYZ from settings.py on the fly when rendering in the templates.
That is why, when retrieved from the database, datetime object is timezone aware but it has UTC as its timezone (hence +00:00 in 2016-06-18 08:18:00+00:00). As, you are converting the time to str yourself (and not rendering in the template) so Django does not convert it to your TIME_ZONE setting. You need to convert it yourself to your desired TimeZone.
If you want to convert it to the TimeZone from your TIME_ZONE setting, you can do
from django.utils import timezone
to_tz = timezone.get_default_timezone()
print return_event.return_time.astimezone(to_tz).strftime("%H:%M")
I have a timezone aware object:
date = timezone.now()
and then I am getting the user's timezone using some middleware, so I have it as a string, like:
tz = "America/New_York"
This is probably a really simple question but I have not been able to find the answer anywhere: How do I change the timezone of the date object to the user's timezone? I tried date.localize(tz) but that gave me an error. What am I doing wrong.
You need to convert the string to a timezone object, then use astimezone()
import pytz
timezone = pytz.timezone(tz)
your_date_with_usertimezone = date.astimezone(timezone)
I suppose time zone management was added to Django 1.4, so the problem is quite new.
I used a simple model
class Sample(models.Model):
...
date_generated = models.DateTimeField(auto_now_add = True)
When I try to retrieve a newly created record its fails.
min_datetime = datetime.now() - timedelta(seconds = 300)
sample = Sample.objects.get(date_generated__gte = min_datetime)
and the server issues a warning.
DateTimeField received a naive DateTime (2012-06-29 15:02:15.074000) while time zone support is active.
I figured out two solutions to that problem.
Disable time zone management in settings.py
USE_TZ = False
but this is not always desirable.
2. changing
date_generated = models.DateTimeField(auto_now_add = True)
to
date_generated = models.DateTimeField(default=datetime.now())
is the solution that keeps time zone management working
Use timezone utils of django
from django.utils import timezone
date_generated = models.DateTimeField(default=timezone.now)
The problem is on your end: datetime.now() is not TZ aware, so you're the one feeding in a naive TZ. See the Django docs on this issue. The reason it works when setting default=datetime.now is that you're forcing the value to a naive datetime, so when you later compare it with another naive datetime, there's no problem.
You need to get "now" the following way:
import datetime
from django.utils.timezone import utc
now = datetime.datetime.utcnow().replace(tzinfo=utc)
Be careful setting a DateTimeField default value of datetime.now(), as that will compute a single value when Apache/nginx loads Django (or when you start the development server), and all subsequent records will receive that value.
Always use auto_now_add for that reason.
I have a Django app with a model that contains a field of type DateTimeField.
I am pulling data from the web in the format of 2008-04-10 11:47:58-05.
I believe that the last 3 characters in this example are the timezone.
How can I preserve that data in the DateTimeField, and is there an easy conversion between the two? Setting the DateTimeField to simply contain a string of the above format throws a ValidationError.
Thank you!
You can also use Django's implementation. I would in fact prefer it and only use something else, if Django's parser cannot handle the format.
For example:
>>> from django.utils.dateparse import parse_datetime
>>> parse_datetime('2016-10-03T19:00:00')
datetime.datetime(2016, 10, 3, 19, 0)
>>> parse_datetime('2016-10-03T19:00:00+0200')
datetime.datetime(2016, 10, 3, 19, 0, tzinfo=<django.utils.timezone.FixedOffset object at 0x8072546d8>)
To have it converted to the right timezone when none is known, use make_aware from django.utils.timezone.
So ultimately, your parser utility would be:
from django.utils.dateparse import parse_datetime
from django.utils.timezone import is_aware, make_aware
def get_aware_datetime(date_str):
ret = parse_datetime(date_str)
if not is_aware(ret):
ret = make_aware(ret)
return ret
You can use
import dateutil.parser
dateutil.parser.parse('2008-04-10 11:47:58-05')
Which returns a datetime (that can be assigned to the DateTimeField).
I've been using this:
from django.utils.timezone import get_current_timezone
from datetime import datetime
tz = get_current_timezone()
dt = tz.localize(datetime.strptime(str_date, '%m/%d/%Y'))
String format of Django DateTimeField is "%Y-%m-%dT%H:%M:%S.%fZ".
Hence, conversion between eachother can be done using strptime() or strptime() using this format.
eg. for string formatted value (2016-10-03T19:00:00.999Z), it can be converted
to Django datetime object as :
from datetime import datetime
datetime_str = '2016-10-03T19:00:00.999Z'
datetime_object = datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S.%fZ")
If you're using Django Forms, you can specify input_formats to your DateField. See the DateField documentation
If you are wanting to parse arbitrary date information, you could use something like parsedatetime and implement a method that Django calls to do the parsing before it hits the validators. (See this SO answer for a good summary of how validations work and when to insert them)
To make standard format:
from django.utils.dateparse import parse_datetime
formatted_datetime = parse_datetime(YOUR_STRING_DATETIME).strftime('%Y-%m-%d %H:%M:%S')
print(f"formatted_datetime: {formatted_datetime}")
You will see something like this:
2022-02-09 12:58:52
Be successful