Parsing a Datetime String into a Django DateTimeField - django

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

Related

How can I convert the DateTimeField in Django from UTC to enduser's timezone (usually PST) when querying a Django model?

I'm querying a Django model connected to a table in my Postgres database that contains a datetime stored in UTC.
My query code looks something like this:
query_set = table_object.objects.values()
One of the columns in the query set is the datetime value in UTC.
The model looks like this:
class ops_inbox_view(models.Model):
requested_date = models.DateTimeField()
other_item = models.CharField(max_length=20)
other_item2 = models.CharField(max_length=40)
other_item3 = models.CharField(primary_key=True, max_length=10)
other_item4 = models.CharField(max_length=50)
other_item5 = models.CharField(max_length=50)
other_item6 = models.CharField(max_length=50)
I want to convert this into PST or robustly in the enduser's local time zone. My current solution is to use pandas with dt.tz_localize and dt.tz_convert after loading the query set into a dataframe but I'm trying to find a solution that is easily manageable in one location of the project file structure of the app. In my settings.py, I have TIME_ZONE set to 'US/Pacific' but because I'm using Pandas, the conversion to PST is not automatically done and will have to change many lines of code in my views.py to make the conversion with pandas.
Is there a way to not use Pandas and instead either make the field timezone aware or make the explicit conversion in the query code? Also looking for any other best practices in timezone management and display. Serving this datetimefield into an html file...
You can use F() with annotate
from datetime import timedelta
from django.db.models import DateTimeField, ExpressionWrapper, F
table_object.objects.annotate(modified_date=ExpressionWrapper(F('requested_date')+timedelta(minutes=960), output_field=DateTimeField())).filter(modified_date='2019-11-30')
Here you need to +/- minutes to convert utc datetime to your(PST) timezone and it will store converted dates into modofied_date, after that we can filter it.
Note:
You don't need to use pandas to convert timezones. Django good built-in timezone management tools.
Their timezone docs are quite good as well, so I'd suggest reading it all the way through.
The problem you're havingĀ seems very similar to one described in their docs

I can not change the date format in django drf

I have drf model which is containe DateField. That field default format is "YYYY-MM-DD" just i want to convert "DD-MM-YYYY" how can is possible.
from rest_framework import serializers
from.models import SpaUser
from djoser.serializers import UserCreateSerializer as BaseUserRegistrationSerializer
import datetime
from rest_framework.settings import api_settings
class SpaUserSerializer(serializers.ModelSerializer):
date_joined = serializers.ReadOnlyField()
birthdate = serializers.DateField(format="%d-%m-%Y", input_formats=['%d-%m-%Y',])
If it is universal, in your settings file add "DATE_INPUT_FORMATS" to REST_FRAMEWORK settings like:
REST_FRAMEWORK = {
"DATE_INPUT_FORMATS": ["%d-%m-%Y"],
...
}
for more details check http://www.django-rest-framework.org/api-guide/settings/#date-and-time-formatting
To have it correctly work, input_formats is the argument you need to assign the format needed, format is the output format
birthdate = serializers.DateField(input_formats=['%d-%m-%Y',])
or you can set the default input format in your settings
DATE_INPUT_FORMATS = [
("%d-%m-%Y"),
]
By combining all your solutions, it works
REST_FRAMEWORK = {
# "DATE_INPUT_FORMATS": "%d-%m-%Y", doesn't works
'DATE_INPUT_FORMATS': [("%d-%m-%Y"),],it works
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}
From DRF documentation regarding DateField
Format strings may either be Python strftime formats which explicitly
specify the format, or the special string 'iso-8601', which indicates
that ISO 8601 style dates should be used. (eg '2013-01-29')
So in your case format should be
format="%d-%m-%Y"
Although this thread is quite old, I found it when trying to solve a similar problem. Although the responses didn't give me the entire answer, they did push me to simply read DRF's quite helpful documentation on the topic:
https://www.django-rest-framework.org/api-guide/settings/#date-and-time-formatting
In my case, I am building a events calendar using Vuetify's v-calendar, Vue JS, and DRF. The v-calendar requires the format YYYY-MM-DD HH:MM, and prefers a string. I could simply store the string in my DB, but I preferred to store the datetime fields in native Python formatting for server-side processing should I ever need them. So, in my settings.py file, I added the following to my REST_FRAMEWORK setting:
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M',],
'DATETIME_FORMAT': '%Y-%m-%d %H:%M',
The first one takes a list (hence the brackets and comma), the second just takes a string. Then, in my serializer, I use:
start = serializers.DateTimeField()
On the front end, I take in the dates as the string objects, pass them to the calendar for display, and it works. When POSTing data, I simply pass them back in that same string format, and the DRF serializer encodes them as native Python for storage.
We have to differentiate between all these:
DATE_INPUT_FORMATS
To specify the date format for the input in POST request API.
DATETIME_FORMAT
To specify the DateTime format for serialized data like with GET request.
DATE_FORMAT
To specify the Date format for serialized data like with GET request.
And this is an example of how to use these settings in the settings.py file.
REST_FRAMEWORK = {
'DATE_INPUT_FORMATS': ["%Y-%m-%d %H", ],
'DATETIME_FORMAT': '%Y-%m-%d %H',
'DATE_FORMAT': '%Y-%m-%d %H'
}
Note: You should use these settings just if you want your serializer to have the same data or DateTime format.
But in case you have changed in some field you can just change the format for that field and here is an example of how to do like this:
class YourSerializer(serializers.ModelSerializer):
...
def to_representation(self, instance):
representation = super(YourSerializer, self).to_representation(instance)
representation['created_at'] = instance.created_at.strftime('%Y-%m-%d %H)
return representation
Or in a similar way
class YourSerializer(serializers.ModelSerializer):
created_at = serializers.DateTimeField(format='%Y-%m-%d %H')
In settings.py include:
REST_FRAMEWORK = {
'DATE_FORMAT': '%d-%m-%Y'
}

Freezegun always causes RuntimeWarning of receiving naive datetime

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

Change timezone of timezone-aware object in django

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)

Django JSONField dumping/loading

I'm using JSONField in some of my Django models and would like to migrate this data from Oracle to Postgres.
So far I haven't had any luck keeping this JSON data intact when using Django's dumpdata and loaddata commands, the data is transformed into string representations of the JSON. I've yet to find a good solution to this... Ideas?
I ended up solving this problem by overriding Django's included JSON serializer, specifically the handle_field method, in a custom serializer file called custom_json_serializer.py. By doing this I can ensure that specific JSONFields stay as is, without being converted to string.
On the chance anyone else runs into this issue, these are the steps I took. I had to add this custom serializer to the settings.py file:
SERIALIZATION_MODULES = {
'custom_json': 'myapp.utils.custom_json_serializer',
}
and then call it when serializing the data from Django:
python manage.py dumpdata mymodel --format=custom_json --indent=2 --traceback > mymodel_data.json
The custom serializer looks like:
from django.core.serializers.json import Serializer as JSONSerializer
from django.utils.encoding import is_protected_type
# JSONFields that are normally incorrectly serialized as strings
json_fields = ['problem_field1', 'problem_field2']
class Serializer(JSONSerializer):
"""
A fix on JSONSerializer in order to prevent stringifying JSONField data.
"""
def handle_field(self, obj, field):
value = field._get_val_from_obj(obj)
# Protected types (i.e., primitives like None, numbers, dates,
# and Decimals) are passed through as is. All other values are
# converted to string first.
if is_protected_type(value) or field.name in json_fields:
self._current[field.name] = value
else:
self._current[field.name] = field.value_to_string(obj)
The really strange part is that before this fix some JSONFields were serializing just fine, while others were not. That is why I took the approach of specifying the fields to be handled. Now all data is serializing correctly.
I haven't used the JSONField before, but what I do is:
import json
data_structure = json.loads(myData)
Maybe that will work for what you need as well. There's likely a better way to deal with this.
EDIT: If you end up using the package json - only then is the following solution applicable.
If you are using Python 2.6 and above you can use:
import json
otherwise, you can use the simplejson that is bundled with django.utils (for Python < 2.6).
from django.utils import simplejson as json
That way you can continue to use the same package name, and take your code to Google App Engine as it supports Python 2.5.2 at the moment.