django 1.4 - nie można porównać czasów naiwnych i uwzględniających offset

85

Jestem w trakcie migracji aplikacji z django 1.2 do 1.4.

Mam obiekt zadania dziennego zawierający porę dnia, w którym zadanie powinno zostać ukończone:

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

Aby sprawdzić, czy zadanie nadal wymaga wykonania dzisiaj, mam następujący kod:

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

To działało dobrze pod 1.2, ale pod 1.4 dostaję błąd:

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

ze względu na linię

if timeDue<now and timeDue>dailyTask.last_completed

i obie klauzule porównania zgłaszają ten błąd.

Próbowałem uświadomić strefę czasową timeDue, dodając pytz.UTC jako argument, ale nadal powoduje to ten sam błąd.

Przeczytałem niektóre dokumenty dotyczące stref czasowych, ale nie wiem, czy muszę po prostu uświadomić strefę czasową timeDue, czy też muszę dokonać fundamentalnej zmiany w mojej bazie danych i istniejących danych.

meepmeep
źródło

Odpowiedzi:

168

Sprawdź dokładny dokument, aby uzyskać szczegółowe informacje.

Zwykle służy django.utils.timezone.nowdo tworzenia bieżącej daty i godziny uwzględniającej przesunięcie

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

I django.utils.timezone.make_awareaby utworzyć datę i godzinę uwzględniającą przesunięcie

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

Następnie można porównać oba czasy danych uwzględniające przesunięcie bez problemów.

Co więcej, możesz przekonwertować datetime.datetime.now()datę i godzinę z uwzględnieniem przesunięcia na datę i godzinę naiwną z przesunięciem, usuwając informacje o strefie czasowej, a następnie można ją porównać w / normal , w ramach utc.

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

USE_TZjest True`` domyślnie '' (w rzeczywistości jest to Falseustawienie domyślne, ale settings.pyplik wygenerowany przez django-admin.py startprojectustawienie go na True), to jeśli twoja baza danych obsługuje czasy z uwzględnieniem strefy czasowej, wartości pól modelu związanych z czasem będą uwzględniać strefę czasową. możesz go wyłączyć, ustawiając USE_TZ=False(lub po prostu usuwając USE_TZ=True) w ustawieniach.

okm
źródło
4
Django nie przechowuje świadomych czasów dla TimeField, robi to tylko dla DateTimeField. Jest to naprawdę denerwujące, ponieważ obiekt datetime.time w Pythonie obsługuje TZINFO tak samo, jak obiekty datetime.datetime. Zastanawiam się, że naprawią to w następnej wersji. Btw przetestowałem to na serwerze bazy danych Postres 9.1.
tejinderss
@tejinderss: datetime.timejest źle. Nie ma sensu zapisywać 'Asia/Shanghai'strefy czasowej, jeśli nie znasz daty (przesunięcie utc może być różne dla tego samego czasu, ale dla różnych dat).
jfs
@okm: make_aware(datetime.now(), get_default_timezone())kończy się niepowodzeniem, jeśli get_default_timezone()różni się od lokalnej strefy czasowej (powinno być, ale nie jest w pełni wiarygodna). Po prostu użyj timezone.now()zamiast tego (jest świadomy strefy czasowej, jeśli USE_TZjest True).
jfs