I'm developing a Django app for logging dives and each dive has a datetime and a timezone in it. I'm using the django-timezone-field app for the timezone.
class Dive(models.Model):
...
date_time_in = models.DateTimeField(default=timezone.now)
timezone = TimeZoneField(default=timezone.get_current_timezone_name())
So the user is able to enter a datetime string ("2016-07-11 14:00") and select a timezone ("Asia/Bangkok" - UTC+0700), I then set the timezone of the datetime to the one given in my view like this:
def log_dive(request):
if request.method == 'POST':
form = DiveForm(request.POST)
if form.is_valid():
dive = form.save(commit=False)
date = dive.date_time_in
date = date.replace(tzinfo=None)
dive.date_time_in = dive.timezone.localize(date)
dive.save()
The database then stores the datetime as UTC in the database (SELECT statement gives it in my local timezone):
# SELECT date_time_in, timezone FROM divelog_dive ORDER BY number DESC;
date_time_in | timezone
------------------------+------------------
2014-07-11 17:00:00+10 | Asia/Bangkok
Now there are two things I'm struggling with:
1) I want to display the dates in the given timezone, however I can't seem to stop it defaulting to the TIME_ZONE setting.
2) If the user edits the record, the time displayed in the edit field should be the one they originally entered (14:00), instead it's showing it in the current timezone (17:00).
Check your timezone setting in settings.py
Do you have USE_TZ = true in your settings file? If you created your app using the djangoadmin-startproject command, it is set by default.
Also, I struggled with timezones at my last job but found that using pytz really helped. Have you tried that package yet?
EDIT: Ok man I may be way off, but since noone else has answered and I feel the timezone struggle, here is something I noticed...
You are replacing the date object with tz_info=None, but wouldn't you want to instead replace that with the timezone from the database? So you would get that timezone and do a replace using the valid format (tzinfo=blah...)?
Like I said I may be way off but if that helps there you go.
Sorry, I don't think I explained my problem very well. I finally figured this out, so I'll answer my own question.
1) turned out to be easy, Django have a template tag for displaying times in a given zone:
{{ dive.date_time_in|timezone:dive.timezone|date:"Y-m-d H:i e" }}
For 2), I came across [1] which lead me to this solution: In the view, after getting the object from the database, I use astimezone(...) to convert the date value (which the DB stores as UTC) into the given timezone. I then use replace(tzinfo=None) to make it naive and then it displays correctly on my form.
def edit_dive(request, dive_id=None):
dive = None
if dive_id != None:
dive = get_object_or_404(Dive, pk=dive_id)
local_date = dive.date_time_in.astimezone(timezone(str(dive.timezone)))
dive.date_time_in = local_date.replace(tzinfo=None)
[1] http://www.saltycrane.com/blog/2009/05/converting-time-zones-datetime-objects-python/
Related
I am trying to read a date of format DD-MM-YYYY format from HTML and Compare it with the datetime field named as widget_created_at in my model.
if data["field"] == "widget_created_at":
date = datetime.strptime(data["data"], "%d-%m-%Y").date()
if data["option"] == "before":
filter_query = Q(widget_created_at__lt=date)
Then whenever this query is used for filtering in the below code
blogs = blogs.filter(filter_query)
RunTime warning for naive datetime appears. I tried every solution i found from here and google but the Error is still there. Please tell me how to avoid it.
Thanks
The date you are creating for your filter has no timezone.
Have you looked at pytz to "localize" the filter date? This would allow you to add a timezone to the filter date?
I'm using django 1.4.1 with mysql and timezones enabled. I did a dump data to yaml, modified some fields to create some test data, and am trying to load it back in. however, Django keeps complaining about naive datetimes even though a tz is specified
specifically, my loaddata has:
fields: {created_date: !!timestamp '2012-09-15 22:17:44+00:00', ...
but loaddata gives the error:
RuntimeWarning: DateTimeField received a naive datetime (2012-09-15 22:17:44) while time zone support is active.
This doesn't make much sense to me, seeing as its:
a UTC timestamp
the same exact format Django exported using dumpdata
is there some way i can tell django this is a UTC date?
The problem stems from PyYAML. When loaddata hands off the datetime to PyYAML, it takes the aware datetime, adjusts the time to UTC, and then returns a naive datetime, which generates the warning.
There is a Django ticket, as well as a PyYAML ticket concerning the issue. Both go into far greater detail concerning the unexpected behavior above. Judging by the comments in the tickets, this issue seems unlikely to be resolved anytime soon.
Is you set TIME_ZONE = 'UTC' in settings.py of your project, you will load in the correct time, but you will still get warnings. Should your timezone be set to anything else, Django will treat the datetime as local, and adjust it to UTC, which is probably undesired.
The best way to avoid this is to use JSON as a serialization format.
Hope that helps.
From the docs...
When serializing an aware datetime, the UTC offset is included, like
this:
"2011-09-01T13:20:30+03:00"
For a naive datetime, it obviously isn't:
"2011-09-01T13:20:30"
...so instead of...
created_date: !!timestamp '2012-09-15 22:17:44+00:00'
...either of...
created_date: '2012-09-15T22:17:44+00:00'
...or...
created_date: '2012-09-15T22:17:44Z'
...will work.
You can copy django/core/serializers/pyyaml.py to your project dir,
and replace following code (maybe 78-79 lines in the case of ver.1.9.9)
for obj in PythonDeserializer(yaml.load(stream, Loader=SafeLoader), **options):
yield obj
to
output = yaml.load(stream, Loader=SafeLoader)
for a_model in output:
for key, value in a_model.items():
if key == 'fields':
for vkey, vvalue in value.items():
if isinstance(vvalue, datetime.datetime):
value[vkey] = vvalue.replace(tzinfo=pytz.utc)
for obj in PythonDeserializer(output, **options):
yield obj
of course pytz already installed and
import pytz
is needed.
This code will convert all naive datetime values to UTC aware.
To override default serializer, add SERIALIZATION_MODULES in settings.py:
SERIALIZATION_MODULES = {'yaml': 'yourproj.pyyaml'}
I hope this monkey patch works fine.
I wanted to continue to use YAML instead of JSON fixtures so I could have comments in the data. The workaround from here fixed the problem for me: https://code.djangoproject.com/ticket/18867
Namely, manually changing the YAML fixture so it:
Doesn't use the !!timestamp YAML tag
Encloses the timestamp values in quotes
Includes timezone information in the timestamp value
...and apparently that triggers Django's timestamp parsing logic instead of the broken PyYAML logic.
I have a model with a datetime field:
class MyModel(models.Model):
created = models.DateTimeField(auto_now = True)
I want to get all the records created today.
I tried:
MyModel.objects.all().filter(created = timezone.now())
and
MyModel.objects.all().filter(created = timezone.now().date())
But always got an empty set. What is the correct way in Django to do this?
EDIT:
It looks strange, but a record, created today (06.04.2012 23:09:44) has date (2012-04-07 04:09:44) in the database. When I'm trying to edit it in the admin panel it looks correct (06.04.2012 23:09:44). Does Django handle it somehow?
Since somewhere in 2015:
YourModel.objects.filter(some_datetime__date=some_date)
i.e. __date after the datetime field.
https://code.djangoproject.com/ticket/9596
There may be a more proper solution, but a quick workup suggests that this would work:
from datetime import timedelta
start_date = timezone.now().date()
end_date = start_date + timedelta( days=1 )
Entry.objects.filter(created__range=(start_date, end_date))
I'm assuming timezone is a datetime-like object.
The important thing is that you're storing an exact time, down to the millisecond, and you're comparing it to something that only has accuracy to the day. Rather than toss the hours, minutes, and seconds, django/python defaults them to 0. So if your record is createed at 2011-4-6T06:34:14am, then it compares 2011-4-6T:06:34:14am to 2011-4-6T00:00:00, not 2011-4-6 (from created date) to 2011-4-6 ( from timezone.now().date() ). Helpful?
Try this
from datetime import datetime
now=datetime.now()
YourModel.objects.filter(datetime_published=datetime(now.year, now.month, now.day))
I am creating a form in Django. I have to put a input type field, which only stores the timezone in database(which will be chosen by a user from a drop Down list at form). I am not able to find out any approach to create this timezone model and how it will return the local time according to saved timezone. I choose following field but it stores also minute hours and second also
timestamp = models.DateTimeField()
Drop down list should be in form of :
...
GMT +5:30
GMT +6:00...and so on.
I think the above answers are all correct, but I leave mine here as an simple example...
class UserProfile(models.Model):
import pytz
TIMEZONES = tuple(zip(pytz.all_timezones, pytz.all_timezones))
# ...
timezone = models.CharField(max_length=32, choices=TIMEZONES,
default='UTC')
# ...
Neither django nor python provide a set of timezones for you to use.
For that, you will need an additional module like pytz. You can get a list of all timezones like this:
>>> import pytz
>>> pytz.all_timezones ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara',
'Africa/Asmera'....
You can their store the timezone name in a CharField.
By the way, choosing a timezone by "GMT +6:00" is not a good idea. For example, EST is usually 5 hours behind GMT, but for 2 weeks around daylight savings time changes, the offset is different. Also, at some times of year someone in Queensland and someone in New South Wales both have the same GMT offset, but because NSW has DST and Queensland doesn't, for half the year their GMT offsets are different. The only safe way to list timezones is to list the actual geographic timezones.
django-timezone-field is an app that handles this nicely.
The way I do this is using pytz valid timezone names. I adjusted my list to reflect only the ones I need, i.e.
TIMEZONES = (
'Canada/Atlantic',
'Canada/Central',
'Canada/Eastern',
'Canada/Mountain',
'Canada/Pacific',
)
I then have a location class which sets the timezone as a Char Field as such:
class Location(models.Model):
....
time_zone = models.CharField(max_length=100, blank=True, null=True, choices=TIMEZONES) # 64 min
....
Notice I set blank & null to True to make the field optional. Take a look at django-timezone-field fields.py for further ideas.
To use this in my code with pytz, I import timezone:
from pytz import timezone
import datetime
from locations.models import Location # my object that has the time_zone field
loc = Location.objects.get(pk=1) #get existing location or your object that has time_zone field
utc = pytz.utc
some_utc_date = datetime.datetime(2002, 10, 27, 6, 0, 0).replace(tzinfo=utc) #tz aware
some_date.astimezone(timezone(loc.time_zone))
Replace datetime.datetime(2002, 10, 27, 6, 0, 0) with the datetime field that corresponds to your location or specific object that has the time_zone field. In my case I store all my date fields in UTC format in a MongoDB collection. When I retrieve the data and want to create human readable output, I use the above method to show the date in the output. You can also create a custom tag to handle this in templates. See pytz doc for more details.
If you are migrating from pytz to zoneinfo:
try:
import zoneinfo
except ImportError:
from backports import zoneinfo
class UserProfile(models.Model):
TIMEZONE_CHOICES = ((x, x) for x in sorted(zoneinfo.available_timezones(), key=str.lower))
timezone = models.CharField("Timezone", choices=TIMEZONE_CHOICES, max_length=250, default='Etc/GMT+2')
I'm having a bit of trouble with django's DateField model field. Shouldn't it be able to accept fiveDaysLater as a valid date object? When I try to add fiveDaysLater into the database, I get an error saying cannot add null value to date. However, the second I change the date field to a regular CharField, the fiveDaysLater value is added to the database with no problem. fyi if I print fiveDaysLater, I get 2011-09-28
My view:
def myView():
now = datetime.date.today()
fiveDaysLater = now + datetime.timedelta(days=5)
newDate = Speech(date = fiveDaysLater)
newDate.save()
My model
class Speech(models.Model):
date = models.DateField()
"However, the second I change the date field to a regular CharField..." Just a suspicion but if you made this change in your code, make sure to delete and recreated the Speech table using syncdb, otherwise, sqlite will not be aware of this change. (or you could change the datatype using sqlite exporer for firefox or something like that...)