django 1.4 - não é possível comparar datetimes ingênuo e ciente de offset

86

Estou migrando um aplicativo do Django 1.2 para 1.4.

Eu tenho um objeto de tarefa diária que contém uma hora do dia em que a tarefa deve ser concluída:

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']

Para verificar se uma tarefa ainda precisa ser concluída hoje, tenho o seguinte código:

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

Isso funcionou bem em 1.2, mas em 1.4 recebo o erro:

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

devido à linha

if timeDue<now and timeDue>dailyTask.last_completed

e ambas as cláusulas de comparação geram esse erro.

Eu tentei tornar o fuso horário timeDue ciente adicionando pytz.UTC como um argumento, mas isso ainda gera o mesmo erro.

Eu li alguns dos documentos sobre fusos horários, mas estou confuso se preciso apenas tornar o fuso horário timeDue ciente ou se preciso fazer uma alteração fundamental no meu banco de dados e nos dados existentes.

meepmeep
fonte

Respostas:

170

Verifique o documento completo para obter informações detalhadas.

Normalmente, use django.utils.timezone.nowpara fazer uma data e hora atual ciente de deslocamento

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

E django.utils.timezone.make_awarepara fazer uma data e hora com reconhecimento de deslocamento

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

Você pode então comparar os dois datetimes com reconhecimento de deslocamento sem problemas.

Além disso, você pode converter datetime de reconhecimento de deslocamento em datetime ingênuo de deslocamento removendo as informações de fuso horário e, em seguida, pode ser comparado com normal datetime.datetime.now(), em utc.

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

USE_TZé True'por padrão' (na verdade é Falsepor padrão, mas o settings.pyarquivo gerado por django-admin.py startprojectdefini-lo como True), então se seu banco de dados suporta horários com reconhecimento de fuso horário, os valores dos campos de modelo relacionados ao tempo seriam cientes do fuso horário. você pode desativá-lo definindo USE_TZ=False(ou simplesmente removendo USE_TZ=True) nas configurações.

okm
fonte
4
Django não armazena tempos de reconhecimento para TimeField, apenas para DateTimeField. É realmente irritante, pois o objeto datetime.time do python oferece suporte a TZINFO, assim como os objetos datetime.datetime. Eu me pergunto se eles iriam consertar no próximo lançamento. Btw eu testei no servidor de banco de dados postres 9.1.
tejinderss
@tejinderss: datetime.timeestá errado. Não faz sentido armazenar o 'Asia/Shanghai'fuso horário se você não souber a data (o deslocamento utc pode ser diferente para o mesmo horário, mas em datas diferentes).
jfs
@okm: make_aware(datetime.now(), get_default_timezone())falha se for get_default_timezone()diferente do seu fuso horário local (deveria ser, mas não é totalmente confiável). Em timezone.now()vez disso, use (reconhece o fuso horário se USE_TZestiver True).
jfs