Django: Timezone

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

Hi,

gibt es eine Möglichkeit Zeiten von Django so zu ändern, dass eine vorher definierte Zeitzone für die Anzeige genutzt wird (oder die des Browser OS, wenn das übertragen werden sollte)? In settings.TIME_ZONE kann man zwar die Zeitzone einstellen, zu der Django automatisch sämtliche Zeitangaben konvertiert, aber das bringt mir nicht viel, da die Seite in verschiedenen Zeitzonen arbeiten soll und jeweils die lokale Zeit angezeigt werden soll (speichere sämtliche Zeitangaben in GMT).

Was ich nun nun bräuchte wäre evtl. ein (session-abhängiger) Filter, der zur richtigen Zeitzone konvertiert...

Irgendwelche Ideen?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Zeitzonen in Python erfordern erstmal ein neues Paket: pytz. Dummerweise kann Python in der Standardinstallation nicht mit Zeitzonen umgehen. Dann würde ich einfach in einem UserProfile den Benutzer seine Lieblingszeitzone einstellen lassen und diese dann über den in der Session gemerkten User zu berücksichtigen. Vielleicht hilft dir ja auch http://www.djangosnippets.org/snippets/183/ oder http://code.google.com/p/django-timezones/

Stefan
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

Vielen Dank für den Hinweis. Den Filter konnte ich auch schon implementieren, aber so recht gefällt mir die Lösung noch nicht, da jedes mal das User Objekt im Template mitgegeben werden muss.

Code: Alles auswählen

from django import template

def user_timezone(value, user):
  # timezone conversion
  pass

register = template.Library()
register.filter('user_timezone', user_timezone)
Das kann zwar noch vereinfacht werden, damit man nicht jedes Mal bei der HttpResponse den user explizit mitgeben muss:

Code: Alles auswählen

# settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
    'mysite.processor_file_name.user'
)

# mysite/processor_file_name.py
def user(request):    
  return {'user':request.user }
aber trotzdem scheint mir das Übergeben des Users im Template überflüssig. Gibt es vielleicht die Möglichkeit den User, der das soeben generierte Template abfragt innerhalb der Filter-Funktion zu ermitteln?

Vielen Dank.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Hm. Du könntest einen Hack zusammenschrauben, aber toll wäre das auch nicht:

Code: Alles auswählen

# models
class YourModel:
    @property
    def usertime(self):
        return (self.timestamp, self.user)
Dann passiert zwar im Prinzip das Selbe, allerdings musst du nur noch `article.usertime|user_timezone` machen. Optional könntest du den Filter gleich in die Methode `usertime` einbauen, dann ist alles was du schreibst `article.usertime`. Oder so.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Vielleicht ist es mit einem Custom-Tag einfacher? Z.B. {% usertime article.create_at %}. Der Tag hätte Zugriff auf den Kontext und könnte sich dort den per Request-Prozessor (dafür gibt es übrigens schon einen fertigen) in den Kontext eingetragenen User herausholen.

Stefan
tarak
User
Beiträge: 2
Registriert: Montag 9. Februar 2009, 16:15

Snippet 183 und django-timezones find ich auch nicht so gut. Vor allem wenn man sich das Alter des Snippets anschaut. An "Django-Timezones" wird zwar aktuell gebaut (siehe SVN-Repo), gefällt mir aber auch nicht so gut. Ich benutze "Django-Timezones" allerdings wegen des TimeZoneField. Bin zu faul das mit PyTZ selber zu machen.

Das gesamte User-Objekt mit in den Context zu packen finde ich, ehrlich gesagt, fragwürdig. Das schließt, soweit ich weiß, alle Relationen ein. Mit Kanonen auf Spatzen geschossen... Das Wort "Sicherheit" sei hier auch mal kurz erwähnt, nebenbei. Und vor allem braucht man nicht immer den User. Zudem: Spart DB-Abfragen...

Per Middleware kann man ja z.B. die Sprache nach den Einstellungen des Users einstellen... Da würde ich auch gleich die Zeitzone irgendwo hin packen. Allerdings unabhängig vom "User", in die Session meiner Meinung nach. Oder gleich in den Request, der ist ja immer verfügbar. Ob das so klug ist frag ich mich allerdings gerade auch.... Babeldjango macht allerdings genau das per Middleware und bringt auch Filter mit, um die Zeit ins rischdische Format zu formatieren. Locale = de-de, Datum = 01.12.2345

Bezüglich User-Einstellungen und Middleware würd ich nen Blick auf Pinax werfen. Im SVN unter so genannten "lokalen Applikationen" (glaub ich) findet man in einer App namens misc (oder woanders) eine LocaleMiddleware die bei jedem eingeloggtem User die Sprache einstellt...

Dann stellt sich da noch eine Frage: Wenn der User Zeiten oder Datum mit Zeitangaben (DATETIME oder TIME) in Formularen macht, denkt er doch bestimmt, dies innerhalb seiner Zeit-Zone zu machen. Was passiert dann bei der Verarbeitung? Will man in jeder View die Zeit von Hand anfassen und in die richtige Zeitzone konvertieren? Heiden-Arbeit bei größeren Projekten, oder?

Das zu lösen finde ich viel spannender!!!

Ich bin ja dafür, das Django das "unter der Haube" löst... Diskussion läuft da schon seit längerem. Dann hätte sich das Thema im Forum hier eh von selbst erledigt.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

tarak hat geschrieben: Das gesamte User-Objekt mit in den Context zu packen finde ich, ehrlich gesagt, fragwürdig. Das schließt, soweit ich weiß, alle Relationen ein. Mit Kanonen auf Spatzen geschossen... Das Wort "Sicherheit" sei hier auch mal kurz erwähnt, nebenbei. Und vor allem braucht man nicht immer den User. Zudem: Spart DB-Abfragen...
Das user object ist schon im Context! (zumindest so lang du http://docs.djangoproject.com/en/dev/re ... rs-request enablest). Zugriff hast du darauf über request.user. Django macht so oder so jeden request eine Db Abfrage für den User, und nein relations werden erst geladen wenn du auf sie zugreifst, ala request.user.groups...
Und nein Sicherheit braucht man hier nicht zu erwähnen...
Dann stellt sich da noch eine Frage: Wenn der User Zeiten oder Datum mit Zeitangaben (DATETIME oder TIME) in Formularen macht, denkt er doch bestimmt, dies innerhalb seiner Zeit-Zone zu machen. Was passiert dann bei der Verarbeitung? Will man in jeder View die Zeit von Hand anfassen und in die richtige Zeitzone konvertieren? Heiden-Arbeit bei größeren Projekten, oder?
Nein ist es normalerweise nicht, man schreibt sich 2,3 Funktionen und konvertiert dann einfach, im Grunde könnte schon das Feld im Formular diese Aufgabe übernehmen (def clean, anyone?). Und selbst bei Modelforms kannst du die Felder überschreiben, also ist das fast keine Arbeit.
Ich bin ja dafür, das Django das "unter der Haube" löst... Diskussion läuft da schon seit längerem. Dann hätte sich das Thema im Forum hier eh von selbst erledigt.
Wo? Link bitte.
tarak
User
Beiträge: 2
Registriert: Montag 9. Februar 2009, 16:15

Wo? Link bitte.
Gibt keinen bestimmten Link... Hab mir mit den Zeitzonen die selben Gedanken gemacht wie Lunas und herum gesucht. Mehrere Tage lang Mailinglisten gelesen, gegoogelt, etc... Ist mein "Gesamt-Eindruck" von der Problematik. Die Django-Entwickler sind sich des Problems bewusst, meiner Erinnerung nach war die Aussage da: Design decision needed (kann mich auch irren)...
Django macht so oder so jeden request eine Db Abfrage für den User
Kann man abstellen. Will jetzt hier nicht theoretisch werden. Ich bleib dabei, will mich nicht auf request.user verlassen. Zeitzonen gehen auch ohne eingeloggten user...

Danke für das klären meiner Frage wegen relations, Apollo13!!! Hat mir Arbeit gespart. (Ich fauler Hund, nimm's mir nicht übel...)

Sicher kann man da 2,3 Funktionen schreiben, reduziert das ganze auf wenige Zeilen in den Views oder in Forms, die wohl trotzdem alle geändert werden müssten, wenn sich die Funktion ändert, bsp.-weise ein Argument mehr, oder? Sowas ist absolut machbar, will ich nicht bestreiten, aber auch anfällig. Ich weiss dann immer, wo ich vor Monaten was gemacht hab, was ich mittlerweile schon wieder vergessen hab. Eigentlich benutz ich ja Django, um genau von sowas wegzukommen... By the way, I know "def clean", man...

Noch was: Django-Timezones bringt zwar ein TimeZoneField mit, aber das ist nur in Englisch. Wer sich das anschaut, der kann sich Gedanken über eine Übersetzung ins Deutsche machen.

Und Django-Timezones vom SVN ist z.Zt. strange (kaputt?), siehe

http://code.google.com/p/django-timezon ... tail?id=14

Comment 2 is von mir..
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das mit der Zeitzone interessiert mich nun auch ein wenig... Denn die Zeiten sind auf meiner Webseite falsch ;)
Bei meinem Blog nutzte ich teilweise den template Filter "timesince"...

Aber wie kann man es richtig machen?

Eine Möglichkeit wäre es doch, alles in UTC zu speichern und beim Client per JS bzw. jQuery die zeiten zu korrigieren...

Dumm ist allerdings, das django von sich auf kein UTC verwendet, siehe auch:
http://www.python-forum.de/topic-13534.html
http://www.python-forum.de/topic-19956.html

EDIT: Ach, das Problem an "timesince" ist natürlich, das es quasi nicht cacheable ist.

Ich Frage mich warum django z.B. beim DateTimeField mit "auto_now" bzw. "auto_now_add" ein datetime.now() benutzt und kein datetime.utcnow()... Weiß jemand warum???
EDIT2: Hab es mal nachgefragt: http://groups.google.com/group/django-d ... ef33c88bf3

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Geht dieser Template Filter auch einfacher (ein großer Teil ist von contrib/syndication/feeds.py):

Code: Alles auswählen

from datetime import datetime, timedelta

def _get_offset():
    now = datetime.now()
    utcnow = datetime.utcnow()

    # Must always subtract smaller time from larger time here.
    if utcnow > now:
        sign = -1
        tzDifference = (utcnow - now)
    else:
        sign = 1
        tzDifference = (now - utcnow)

    # Round the timezone offset to the nearest half hour.
    tzOffsetMinutes = sign * ((tzDifference.seconds / 60 + 15) / 30) * 30
    tzOffset = timedelta(minutes=tzOffsetMinutes)
    return tzOffset

_TZ_OFFSET = _get_offset()

def to_utc(value, arg=None):
    value = value - _TZ_OFFSET
    return value

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Offensichtlich ist django in der Sache nicht gut durchdacht. Sehr lesenswert: http://code.djangoproject.com/ticket/10587

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

jens hat geschrieben:Eine Möglichkeit wäre es doch, alles in UTC zu speichern und beim Client per JS bzw. jQuery die zeiten zu korrigieren...
UN*X macht sowas schon seit 30 Jahren aus dem Grund, dass man sich anderenfalls schwer ins Knie schiessen kann. Genauso wie man Preise immer netto speichert gehören Uhrzeiten immer in UTC abgespeichert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

hab mir ein kleines "timezone info plugin" für PyLucid geschrieben: http://trac.pylucid.net/changeset/2436

Normalerweise sehen die Ausgaben so ähnlich aus:

Code: Alles auswählen

      server information
      settings.TIME_ZONE
          America/Chicago
      TZ from os.environ
          America/Chicago
      datetime now
          26. Nov. 2009, 10:56
      datetime UTC now
          26. Nov. 2009, 16:56
      datetime to UTC (calculated with PyLucid template filter 'to_utc')
          26. Nov. 2009, 16:56
      UTC offset
          -1 day, 18:00:00 

      Client information via JavaScript
      Current time from JavaScript
          Do 26 Nov 2009 17:53:30 CET
      JavaScript timezone offset
          -1 
Wenn man aber settings.TIME_ZONE="" macht, dann sieht das so aus:

Code: Alles auswählen

      server information
      settings.TIME_ZONE
      TZ from os.environ
      datetime now
          26. Nov. 2009, 16:48
      datetime UTC now
          26. Nov. 2009, 16:48
      datetime to UTC (calculated with PyLucid template filter 'to_utc')
          26. Nov. 2009, 16:48
      UTC offset
          0:00:00 

      Client information via JavaScript
      Current time from JavaScript
          Do 26 Nov 2009 17:48:54 CET
      JavaScript timezone offset
          -1 
Speichert django also Zeiten in UTC, wenn man TIME_ZONE="" setzt?
So sieht es zumindest aus, wenn man datetime.now() und datetime.utcnow() vergleicht.

Wenn das so ist, dann wäre das doch jetzt schon eine gute Lösung, oder nicht???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab mich dazu entschieden settings.TIME_ZONE auf "Europe/London" zu stellen, was Greenwich Mean Time bzw. GMT-0 ist. Das ist expliziter als einfach ein Leeren String zu übergeben ;)

Django sollte das in den Voreinstellung auch machen. Ist IMHO besser als "America/Chicago".

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Keine gute Idee, da Europe/London auch Daylight Saving Times beinhalten kann: http://www.timezoneconverter.com/cgi-bi ... ope/London
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Oh, stimmt.

Dann doch besser leer lassen? Oder eins davon nehmen:
  • * GMT GMT+0 GMT-0 GMT0 Greenwich Etc/GMT Etc/GMT+0 Etc/GMT-0 Etc/GMT0 Etc/Greenwich
    * MET
    * CET
von: http://www.postgresql.org/docs/8.1/stat ... -SET-TABLE

EDIT: MET und CET (Mitteleuropäische Zeit) fällt schon mal raus, weil es UTC +1h ist: http://de.wikipedia.org/wiki/Mitteleuro ... ische_Zeit

Man könnte aber auch einfach TIME_ZONE = "UTC" nehmen und gut ist ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten