Django - Flexible DateTimeField precision with timezone - django

I have a database model that includes a DateTimeField as below:
class Person(models.Model):
....
date_of_birth = models.DateTimeField()
date_of_birth_precision = models.CharField(max_length=3, choices=(
('D', 'Day'),
('H', 'Hour'),
('Min', 'Minute'),
('S', 'Second')
))
I have USE_TZ = True in my settings.py.
My indentation is to give the user the flexibility to input the date_of_birth with the precision he wants according to the selected date_of_birth_precision, and I have already handled the picker format according to the selected date_of_birth_precision. So, for an instance, the input could be 1992-07-28, 1992-07-28 15, 1992-07-28 15:33, or 1992-07-28 15:33:45 depending on the selected precision.
To be able to accept those different formats in my form, I have extended the accepted formats in settings.py as below:
DATETIME_INPUT_FORMATS += (
"%Y-%m-%d %H",
)
Timezone Problem Statement:
So far, everything is working fine only if I have time fractions.
Suppose that the input date is 1992-07-28 with precision Day and my local time zone is UTC+2, the value will be converted to UTC to be 1992-07-27 22:00:00 in database.
Now, when a user, for an instance, with time zone UTC+1 reads the date_of_birth, it will be translated initially from the database to 1992-07-27 23:00:00, then I will parse it to the user with Day precision to be 1992-07-27 which is not correct in my business case.
In my business case, I want the date_of_birth to be time-zone-aware only if there are time fractions, and to be saved naïve as it is and to be translated to the user as it is if there is no time fraction. How can I achieve that?

Related

How to format DurationField input as min:sec:millisec Django

I want to force users to input lap times into a Form using the format min:sec:millisec (e.g. 00:00:000). I also want to display these times in this format in a DetailView but I want to store them as milliseconds to calculate personal bests and lap differences.
I have tried to set the default DurationField value as 01:01:001 but it displays in the format HH:MM:SS.MS
Here is my model:
class SwimTime(models.Model):
swimmer =models.ForeignKey(Swimmer, on_delete=models.CASCADE)
time = models.DurationField(_('Time'), default= timedelta(minutes=1, seconds=1, milliseconds=1))
distance = models.PositiveIntegerField(_('Distance'),null = False, default=50)
strokeType = models.CharField(_('Stroke Type'),max_length=20, choices=strokeTypes, default='FC')
date = models.DateField(_('Date Recorded'),default = timezone.now)
def save(self, *args, **kwargs):
self.full_clean()
return super().save(*args, **kwargs)
If you want to have an "example" you can use help_text option for the field showing the expected input format. You can use it as any other option: default, null...
help_text="Please use the following format: YYYY-MM-DD."
Anyway, this has nothing to do with how it will be rendered in any template or even in database or browser validation.
For templates you can use the Datetime formatting. Django has not built-in formatting as it has for date and time, but there are some projects that solve that. Also, in this question there are some good examples for writing your own filters and load them in the template.
Also, reading your data I guess that null=False is not necessary in 'distance' field: by default it will be set to False. And keep in mind that null=True and blank=True have different uses.

Displaying / Testing outputs are correct. Sanity Check

I am still learning Django and slowly improving but I have a few questions, I have my whole model below:
from django.db import models
from datetime import datetime, timedelta
# Create your models here.
year_choice = [
('year1','1-Year'),
('year3','3-Year')
]
weeksinyear = 52
hours = 6.5
current_year = datetime.year
class AdminData(models.Model):
year1 = models.IntegerField()
year3 = models.IntegerField()
#property
def day_rate_year1(self):
return self.year1 / weeksinyear / hours
class Price(models.Model):
name = models.CharField(max_length=100)
contract = models.CharField(max_length=5, choices=year_choice)
start_date = models.DateField(default=datetime.now)
end_date = models.DateField(default=datetime(2021,3,31))
def __str__(self):
return self.name
The main concern for me at the moment is trying to understand if my function def day_rate_year1(self): is working correctly, if someone could point me in the right direction to understand either how I display this as a string value in a template or test in the shell to see if the value pulls through as the values for year1 and year3 can change based on user input.
I am trying to work out the day rate so I can then use the start and end dates and work out the number of days between the two to calculate a price which is then displayed to the user which can again change depending on the number of days and the contract type which is a 3 year option or 1 year option.
Let me know if you need the views or templates as well.
Thanks for the help!
if someone could point me in the right direction to understand either how I display this as a string value in a template or test in the shell to see if the value pulls through as the values for year1 and year3 can change based on user input
if you launch a shell session as below, you should see the output of your property.
python manage.py shell
Expected output:
>>> from app_name.models import AdminData
>>> test = AdminData.objects.create(year1=2010, year3=2016)
>>> print(test.day_rate_year1)
5.946745562
>>>

Filtering django DatetimeField__date not working

According to this document that was added on v1.9 we can able to query a DateTimeField by date without time.
Examples are:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
But it is not working for me:
class MilkStorage(models.Model):
....
created_at = models.DateTimeField(null=False)
Usage
from datetime import date
MilkStorage.objects.filter(created_at__date=date.today())
It returns an empty queryset <QuerySet []>.
Does this query only works on PostgreSQL? im using MySQL.
Depending on your specific requirements, this may or may not be an ideal solution. I found that __date works if you set USE_TZ = False in settings.py
I had this same problem (I couldn't even filter by __month or __day) until I disabled USE_TZ. Again, may not be ideal for your case, but it should get __date working again.
I have a visitor model. In this model I filtered data through date not time and it's working.
models.py
Visitor(models.Model):
timestamp = models.DateTimeField(_('Login Date Time'), auto_now=True)
os_info = models.CharField(_('OS Information'), max_length=30, null=True)
views.py
import datetime
visitor = Visitor.objects.filter( timestamp__lte=datetime.datetime.now().date())
print visitor
Output:
<Visitor: Windows 10>, <Visitor: Windows 10>]
Use above way to filter data from date.
If you are using a MYSQL database, you may need to do the following:
Load the time zone tables with mysql_tzinfo_to_sql https://dev.mysql.com/doc/refman/8.0/en/mysql-tzinfo-to-sql.html

convert date and time format in django

How to convert datefield of format mm/dd/yyyy to format dd/mm/yyyy and timefield of format 24hrs to 12hr AM/PM.
This is i am using in below circumstances
In database from a model i am getting values,so if the values getting from db is '0' the date and time format should be of (dd/mm/yyyy and 12hr AM/PM).
If the value from database field is '1',the format is of (mm/dd/yyyy and 24hr)
models.py
class Report(models.Model):
user = models.ForeignKey(User, null=False)
manual_date = models.DateField('Another date', null=True, blank=True)
manual_time = models.TimeField('Another time', null=True, blank=True)
class Settings(models.Model):
date_format = models.CharField('Date format', max_length=100)
time_format = models.CharField('Time format', max_length=100)
How to do.
Thanks
I think you are better off handling this converts at the template level using the tag date. For example, I see you pass the date and time variables to the template. Just pass them unconverted and later in the template wherever they appear do this:
{% if condition %}
{{date|date:"DATE_FORMAT"}}
{{value|time:"TIME_FORMAT"}}
{% endif %}
where "TIME_FORMAT" is the actual format that you want the date to be showed and condition is the condition that needs to be met.
If you absolutely need to do this within the view method, then you can use the module django.utils.dateformat like this:
from django.utils.dateformat import DateFormat, TimeFormat
formatted_date = DateFormat(date_variable)
formatted_date.format('Y-m-d') # substitute this format for whatever you need
formatted_time = TimeFormat(time_variable)
formatted_date.format('h') # substitute this format for whatever you need
Use that class wherever you want to format your date_variable and time_variable with the proper format string for them.
Also, about your views' code. You can't just return a string in the middle of a view method like you do:
if request.method == 'POST':
if user.date_format == '0':
date=datetime.datetime.strptime('%m/%d/%Y').strftime('%d/%m/%Y')
return date
That will throw an exception if the request method is POST. Besides, what date are you formatting there? You need to format your report.manual_date. I don't see that anywhere in the code. Apply what I said above to the variable you want to format depending on your settings and pass that variable to the tempalte_context when you return.
I hope it helps!

Subtract django.db.models.DateField from python's datetime.date to get age

In django, I want to get the age (in days) of an instance of a class. I tried doing that by subtracting its creation date field from today, but it does not seem to work properly. date.today() works fine, but DateField is giving me trouble. I looked at its source code and the django docs online for my version but I'm not sure how to manipulate it to perform the subtraction.
import datetime.date
from django.db import models
class MyItem(models.Model):
item_name = models.CharField(max_length = 30)
creation_date = models.DateField()
def age(self):
return date.today() - creation_date
my_first_item = MyItem(item_name = 'First', creation_date = '2005-11-01')
print my_first_item.age.days
Any input would be greatly appreciated!
Your problem is that you are trying to use a field instance outside of a model to represent a value.
models.DateField is a class which represents a database field with a type of "date". I suspect that you are looking to do one of the following:
Just do straight date math
Work with a value returned by a model
In the case of 1, you don't want to use Django's models at all. All you need and want is python's date and time handling classes. For your specific example all you need to use is a pair of date objects and you will end up with a timedelta object.
To do what you were trying to do in your example with the python standard classes, see the example below:
from datetime import date
birthday = date(year=2005, month=11, day=1)
today = date.today()
age = today - birthday
print age.days()
Here we instantiate a date with the birthdate values, we get a date with today's values, subtract them to get a timedelta, and finally print the number of days between the two dates.
In the case of 2, let's look at an example model:
class Person(models.Model):
name = models.CharField(max_length=100)
birthday = models.DateField()
Here we have a model where we've used models.CharField and models.DateField to describe a table in the database which contains a "varchar" column and a "date" column. When we fetch instances of this model using the ORM, Django handles converting whatever value the database returns to a native datatype. Now let's look at some code that figures out the age of an instance of a person:
from datetime import date
from myapp.models import Person
person = Person.objects.get(id=1)
age = date.today() - person.birthday
print age.days
Here you can see that we fetch an instance of the person model from the database and then we subtract their birthday from today. We're able to do this here, because when we access "person.birthday" Django is transforming whatever value the database returned into a python date object. This is the same type as the date object returned by "date.today()" so the "-" operator makes sense. The result of the subtraction operation is a timedelta object.