Karen: Thank you so much for your reply.
On Sep 23, 7:01 am, Karen Tracey <[email protected]> wrote: > What you haven't shared is the code for DateTimeFieldInTZ. It's going to be > rather hard to figure out what is going wrong without seeing that code. ... > If you show us what is in the DateTimeFieldInTZ implementation it's much > more likely that someone will be able to help. Ah, right you are. Here's the custom field in question. ===== datetimefieldintz.py: # classes used by this class from django.db import models from django.conf import settings # from django.db.backends.util import typecast_timestamp import datetime from dateutil.tz import tzutc, tzstr, tzlocal _utc = tzutc() class DateTimeFieldInTZ(models.DateTimeField): """DateTimeFieldInTZ: replacement for models.DateTimeField, but stores UTC and returns tzinfo Use this class exactly as you would Django's models.DateTimeField. However, instead of returning naive datetime values, it returns datetime values with time zone info (the tzinfo attribute) set, based on Django's settings.TIME_ZONE value. You can set settings.TIME_ZONE to None, and DateTimeField will return naive datetime values. If you store a value then read it back, you should get a datetime value equal to the same UTC time as the original, but perhaps with a different timezone. Also it stores all datetime values in the database in UTC, converting as necessary from whatever tzinfo is in the datetime values it receives. It treats incoming naive datetimes as being in the settings.TIME_ZONE (or if that is None, in the Django server's time zone). The approach is to store all datetimes in backend database as naive datetimes, but with time converted to UTC. Django's settings.TIME_ZONE value becomes a sitewide timezone directive; all timestamps returned from the database are labelled with this time zone. When a site doesn't care about time zones, it's OK to set the settings.TIME_ZONE value to None. In this case, the class uses the timezone of the server hosting Django as the starting point for conversion to UTC. Here is some background to this approach. Some databases (e.g. MySQL) do not have a mechanism for storing the timezone info in a timestamp value. Others (e.g. Postgres) have a database setting for timezone. Only naive datetimes will work in all these backends. However, we want to have time zones in datetime values in the application code. We need some indication of what time zone to fill in; the settings.TIME_ZONE value is a handy indicator. """ def __init__(self, *args, **kwargs): super(DateTimeFieldInTZ, self).__init__(*args, **kwargs) # store tzinfo structs from the runtime environment, for later reference self._server_tz = tzlocal() self._settings_tz = None if settings.TIME_ZONE: self._settings_tz = tzstr(settings.TIME_ZONE) def to_python(self, value, *args, **kwargs): """DateTimeFieldInTZ.to_python(value): convert value to a Python datetime with the proper time zone value: a datetime value as returned by the database, in database- specific format return a datetime value which is in the settings.tz time zone. """ # don't impose tzinfo on datetime.date types skip_tz = isinstance(value, datetime.date) # let the superclass do the hard work utc_dt = super(DateTimeFieldInTZ, self).__init__(value, *args, **kwargs) # if we don't need to impose a time zone, return if utc_dt is None or skip_tz: return utc_dt # impose the timezone assert isinstance(utc_dt, datetime) utc_dt = utc_dt.replace(tzinfo=_utc) # now convert utc_dt to target timezone and store as dt if self._settings_tz == _utc: dt = utc_dt elif self._settings_tz: # Cast it to the specified timezone dt = utc_dt.astimezone(self._settings_tz) else: # Cast to server local time then make it naive dt = utc_dt.astimezone(self._server_tz).replace(tzinfo=None) return dt def get_db_prep_value(self, value, *args, **kwargs): """DateTimeFieldInTZ.get_db_prep_value(value): Casts dates into the format expected by the backend """ print " @@@ get_db_prep_value( %s(%s) ) begins. " % (type(value), value) # convert value which are datetime to be in UTC, and then make them naiive if False and isinstance(value, datetime.datetime): if value.tzinfo is None: # Treat as settings or server local time, and cast to UTC if self._settings_tz is not None: value = value.replace(tzinfo=self._settings_tz) else: value = value.replace(tzinfo=self._server_tz) # value now has a timezone. Move value to UTC if not there already if value.tzinfo != _utc: value = value.astimezone(_utc) # value is now in UTC. Make it naive, for backend's benefit value = value.replace(tzinfo=None) # let the superclass do the hard work r = super(DateTimeFieldInTZ, self).get_db_prep_value(value, *args, **kwargs) print " @@@ get_db_prep_value( %s ) got %s back from superclass " % (value, r) return r def value_to_string(self, obj, *args, **kwargs): """DateTimeFieldInTZ.value_to_string: returns same string as for standard DateTimeField, but with timezone Timezone is represented as a UTC offset in minutes, of form "+0000" or "-0000". Naive datetime values get no UTC offset. """ value = Field._get_val_from_obj(obj) s = super(DateTimeFieldInTZ, self).value_to_string(obj, *args, **kwargs) # Improve s if we can if False and s and isinstance(value, datetime.datetime) and value.tzinfo is not None: s += unicode(value.strftime(" %z")) return s # def formfield(self, **kwargs): # pass # same as superclass def __unicode__(self): """DateTimeFieldInTZ.__unicode__(): returns a Unicode string with the value of the datetime """ return unicode(repr(self)) ===== I wrote this class while going over the definition of models.DateTimeField in django/db/models/fields/__init__.py:526-583. One interesting observation about the diagnostic print statement in get_db_prep_value(). It prints out, @@@ get_db_prep_value( <type 'datetime.datetime'>(2009-06-30 23:59:45+00:00) ) begins. @@@ get_db_prep_value( 2009-06-30 23:59:45+00:00 ) got None back from superclass Making it look like models.DateTimeField.get_db_prep_value() is choking on <type 'datetime.datetime'>(2009-06-30 23:59:45+00:00) (with a utc tzinfo). Here's the entire stack trace of the failure resulting from my S1.save (): ===== ====================================================================== ERROR: test_partial_readwrite (twang.workshop.tests.PartialTwStatus) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/jdlh/workspace/Twanguages/src/twang/workshop/tests.py", line 85, in test_partial_readwrite S1.save() File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/models/base.py", line 410, in save self.save_base(force_insert=force_insert, force_update=force_update) File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/models/base.py", line 495, in save_base result = manager._insert(values, return_id=update_pk) File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/models/manager.py", line 177, in _insert return insert_query(self.model, values, **kwargs) File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/models/query.py", line 1087, in insert_query return query.execute_sql(return_id) File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/models/sql/subqueries.py", line 320, in execute_sql cursor = super(InsertQuery, self).execute_sql(None) File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/models/sql/query.py", line 2369, in execute_sql cursor.execute(sql, params) File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/db/backends/sqlite3/base.py", line 193, in execute return Database.Cursor.execute(self, query, params) IntegrityError: workshop_twstatus.created_at may not be NULL ===== Here's the entire traceback of the first failure in the Django built- in tests: ===== ====================================================================== FAIL: Doctest: django.contrib.auth.tests.__test__.BASIC_TESTS ---------------------------------------------------------------------- Traceback (most recent call last): File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/test/_doctest.py", line 2180, in runTest raise self.failureException(self.format_failure(new.getvalue())) AssertionError: Failed doctest test for django.contrib.auth.tests.__test__.BASIC_TESTS File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/ lib/python2.6/site-packages/django/contrib/auth/tests/__init__.py", line unknown line number, in BASIC_TESTS ---------------------------------------------------------------------- File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/ python2.6/site-packages/django/contrib/auth/tests/__init__.py", line ?, in django.contrib.auth.tests.__test__.BASIC_TESTS Failed example: call_command("createsuperuser", interactive=False, username="joe", email="[email protected]") Exception raised: Traceback (most recent call last): File "/opt/local/Library/Frameworks/Python.framework/Versions/ 2.6/lib/python2.6/site-packages/django/test/_doctest.py", line 1267, in __run compileflags, 1) in test.globs File "<doctest django.contrib.auth.tests.__test__.BASIC_TESTS[27] >", line 1, in <module> call_command("createsuperuser", interactive=False, username="joe", email="[email protected]") File "/opt/local/Library/Frameworks/Python.framework/Versions/ 2.6/lib/python2.6/site-packages/django/core/management/__init__.py", line 166, in call_command return klass.execute(*args, **defaults) File "/opt/local/Library/Frameworks/Python.framework/Versions/ 2.6/lib/python2.6/site-packages/django/core/management/base.py", line 221, in execute self.validate() File "/opt/local/Library/Frameworks/Python.framework/Versions/ 2.6/lib/python2.6/site-packages/django/core/management/base.py", line 249, in validate num_errors = get_validation_errors(s, app) File "/opt/local/Library/Frameworks/Python.framework/Versions/ 2.6/lib/python2.6/site-packages/django/core/management/validation.py", line 38, in get_validation_errors if f.name.endswith('_'): AttributeError: 'NoneType' object has no attribute 'endswith' ---------------------------------------------------------------------- ===== Diagnosis is made a little harder, because my Eclipse + PyDev setup cannot step all the way through to these errors. I get a different failure in the debugger, and it happens in the midst of stepping to the next line, so I don't see the point of error. Thanks for any light anyone can shed! I appreciate the help. — Jim DeLaHunt, Vancouver, Canada. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/django-users?hl=en -~----------~----~----~----~------~----~------~--~---

