django 1.4 - impossible de comparer les datetimes naïfs et sensibles au décalage

85

Je suis en train de migrer une application de django 1.2 vers 1.4.

J'ai un objet de tâche quotidienne qui contient une heure de la journée à laquelle la tâche doit être terminée:

class DailyTask(models.Model):
    time = models.TimeField()
    last_completed = models.DateTimeField()
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=1000)
    weekends = models.BooleanField()

    def __unicode__(self):
        return '%s' % (self.name)

    class Meta:
        db_table = u'dailytask'
        ordering = ['name']

Afin de vérifier si une tâche doit encore être terminée aujourd'hui, j'ai le code suivant:

def getDueDailyTasks():
    dueDailyTasks=[]
    now = datetime.datetime.now()
    try:
        dailyTasks = DailyTask.objects.all()
    except dailyTask.DoesNotExist:
        return None
    for dailyTask in dailyTasks:
        timeDue = datetime.datetime(now.year,now.month,now.day,dailyTask.time.hour,dailyTask.time.minute,dailyTask.time.second)
        if timeDue<now and timeDue>dailyTask.last_completed:
            if dailyTask.weekends==False and now.weekday()>4:
                pass
            else:
                dueDailyTasks.append({'id':dailyTask.id,
                            'due':timeDue,
                             'name': dailyTask.name,
                             'description':dailyTask.description})
    return dueDailyTasks

Cela a bien fonctionné sous 1.2, mais sous 1.4 j'obtiens l'erreur:

can't compare offset-naive and offset-aware datetimes

en raison de la ligne

if timeDue<now and timeDue>dailyTask.last_completed

et les deux clauses de comparaison génèrent cette erreur.

J'ai essayé de rendre le fuseau horaire timeDue conscient en ajoutant pytz.UTC comme argument, mais cela soulève toujours la même erreur.

J'ai lu certains documents sur les fuseaux horaires, mais je ne sais pas si j'ai juste besoin de prendre en compte le fuseau horaire timeDue, ou si je dois apporter une modification fondamentale à ma base de données et aux données existantes.

Meep meep
la source

Réponses:

168

Consultez le document détaillé pour obtenir des informations détaillées.

Normalement, utilisez django.utils.timezone.nowpour créer une date-heure actuelle prenant en compte le décalage

>>> from django.utils import timezone
>>> timezone.now()
datetime.datetime(2012, 5, 18, 13, 0, 49, 803031, tzinfo=<UTC>)

Et django.utils.timezone.make_awarepour créer un datetime prenant en charge le décalage

>>> timezone.make_aware(datetime.datetime.now(), timezone.get_default_timezone())
datetime.datetime(2012, 5, 18, 21, 5, 53, 266396, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)

Vous pouvez alors comparer les deux datetimes prenant en charge le décalage sans problème.

De plus, vous pouvez convertir une date-heure décalée en une date-heure décalée en supprimant les informations de fuseau horaire, puis elle pourrait être comparée à la normale datetime.datetime.now(), sous utc.

>>> t = timezone.now() # offset-awared datetime
>>> t.astimezone(timezone.utc).replace(tzinfo=None)
datetime.datetime(2012, 5, 18, 13, 11, 30, 705324)

USE_TZest True«par défaut» (en fait, c'est Falsepar défaut, mais le settings.pyfichier généré en le django-admin.py startprojectdéfinissant sur True), alors si votre base de données prend en charge les heures prenant en charge le fuseau horaire, les valeurs des champs de modèle liés au temps seraient sensibles au fuseau horaire. vous pouvez le désactiver en définissant USE_TZ=False(ou simplement en le supprimant USE_TZ=True) dans les paramètres.

okm
la source
4
Django ne stocke pas les heures conscientes pour TimeField, il ne le fait que pour DateTimeField. C'est vraiment ennuyeux, car l'objet python datetime.time prend en charge TZINFO tout comme les objets datetime.datetime. Je me demande qu'ils le répareraient dans la prochaine version. Btw je l'ai testé sur le serveur de base de données postres 9.1.
tejinderss
@tejinderss: datetime.timec'est faux. Il est inutile de stocker le 'Asia/Shanghai'fuseau horaire si vous ne connaissez pas la date (le décalage utc peut être différent pour la même heure mais à des dates différentes).
jfs
@okm: make_aware(datetime.now(), get_default_timezone())échoue si get_default_timezone()diffère de votre fuseau horaire local (il devrait l'être mais il n'est pas entièrement fiable). Utilisez simplement à la timezone.now()place (il est sensible au fuseau horaire si USE_TZc'est le cas True).
jfs