Django and Time Zones: New in the Trunk

Django and Time Zones: New in the Trunk

Presentation I gave during Pykonik meet-up (December 2011)
http://blog.pykonik.org/2011/11/grudniowe-spotkanie-december-meetup.html

042b05ac1200c78d8009911e302c1ee4?s=128

Tomek Paczkowski

December 07, 2011
Tweet

Transcript

  1. Django and Time Zones New in trunk Tomek Paczkowski @oinopion

    December 2011
  2. Time zones are a lie.

  3. Time zones are a lie. Sundials

  4. Time zones are a lie. Sundials XIX century – Railroad

    time
  5. Time zones are a lie. Sundials XIX century – Railroad

    time Artificial division
  6. Time zones are a lie. Sundials XIX century – Railroad

    time Artificial division Humans think in local time
  7. Time zones are a lie. Sundials XIX century – Railroad

    time Artificial division Humans think in local time Time as a point in continuum
  8. DST: Dreadful Summer Time

  9. DST: Dreadful Summer Time Promises of savings beyond comprehension ...

  10. DST: Dreadful Summer Time Promises of savings beyond comprehension ...

    ... and more sunlit leisure
  11. DST: Dreadful Summer Time Promises of savings beyond comprehension ...

    ... and more sunlit leisure Artificial division, even more political
  12. DST: Dreadful Summer Time Promises of savings beyond comprehension ...

    ... and more sunlit leisure Artificial division, even more political No reliably measured savings
  13. DST: Dreadful Summer Time Promises of savings beyond comprehension ...

    ... and more sunlit leisure Artificial division, even more political No reliably measured savings Reliably measured raise in suicides
  14. Time is hard. Let’s go shopping

  15. Time is hard. Let’s go shopping UTC = GMT

  16. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST
  17. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST (27/03/2011 2:30?)
  18. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST (27/03/2011 2:30?) CET ← CEST
  19. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST (27/03/2011 2:30?) CET ← CEST (30/10/2011 2a:30)
  20. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST (27/03/2011 2:30?) CET ← CEST (30/10/2011 2a:30) CET/CEST vs Warsaw Mean Time
  21. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST (27/03/2011 2:30?) CET ← CEST (30/10/2011 2a:30) CET/CEST vs Warsaw Mean Time (+1:24)
  22. Time is hard. Let’s go shopping UTC = GMT CET

    → CEST (27/03/2011 2:30?) CET ← CEST (30/10/2011 2a:30) CET/CEST vs Warsaw Mean Time (+1:24) Future dates
  23. Code, dammit!

  24. Code, dammit! Python and datetime

  25. Code, dammit! Python and datetime, time

  26. Code, dammit! Python and datetime, time, calendar...

  27. Code, dammit! Python and datetime, time, calendar... Abstract tzinfo class,

    but no implementations
  28. Code, dammit! Python and datetime, time, calendar... Abstract tzinfo class,

    but no implementations datetime.utcnow().tzinfo is None
  29. Code, dammit! Python and datetime, time, calendar... Abstract tzinfo class,

    but no implementations datetime.utcnow().tzinfo is None datetime(2011, 3, 27, 2, 30, tzinfo=CET) # never happend, but no error
  30. pytz is the hero

  31. pytz is the hero Olson’s database

  32. pytz is the hero Olson’s database cet = pytz.timezone(’Europe/Warsaw’)

  33. pytz is the hero Olson’s database cet = pytz.timezone(’Europe/Warsaw’) cet.localize(datetime(2011,

    10, 30, 2, 30), is_dst=True)
  34. pytz is the hero Olson’s database cet = pytz.timezone(’Europe/Warsaw’) cet.localize(datetime(2011,

    10, 30, 2, 30), is_dst=True) utc_dt.astimezone(cet)
  35. Old times

  36. Old times TIME_ZONE = "America/Chicago"

  37. Old times TIME_ZONE = "America/Chicago" Manual conversions

  38. Old times TIME_ZONE = "America/Chicago" Manual conversions https://github.com/brosner/django-timezones/

  39. Django+01:40

  40. Django+01:40 USE_TZ = True

  41. Django+01:40 USE_TZ = True django.timezone.activate(tz_name)

  42. Django+01:40 USE_TZ = True django.timezone.activate(tz_name) Done!

  43. Django+01:40 USE_TZ = True django.timezone.activate(tz_name) Done! Suggestion: use middleware

  44. Django+01:40 USE_TZ = True django.timezone.activate(tz_name) Done! Suggestion: use middleware Use

    template tags and filters to change
  45. Twitz class Profile(models.Model): user = models.OneToOneField(User) time_zone = models.CharField(max_length=200, blank=True)

    @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance)
  46. Twitz class SettingsView(UpdateView): template_name = ’accounts/settings.html’ form_class = SettingsForm def

    get_object(self, queryset=None): return self.request.user.profile def get_success_url(self): return reverse(’settings’) settings = login_required(SettingsView.as_view())
  47. Twitz {{ status.pub_date|timezone:other_user_tz }} {% timezone other_user_tz %} <strong>{% now

    "r" %}</strong> {% endtimezone %}
  48. Twitz class TimeZoneMiddleware: def process_request(self, request): user = request.user if

    user.is_authenticated(): try: timezone.activate(user.profile.time_zone) except (UnknownTimeZoneError, ValueError) as e: logger.warn("Error setting time zone: %s", e)
  49. How it works

  50. How it works Thread-locals

  51. How it works Thread-locals All dates converted on output (templates)

  52. How it works Thread-locals All dates converted on output (templates)

    All dates converted on input (forms)
  53. How it works Thread-locals All dates converted on output (templates)

    All dates converted on input (forms) Database stores everything in UTC
  54. How it works Thread-locals All dates converted on output (templates)

    All dates converted on input (forms) Database stores everything in UTC Migration: consult your database manual.
  55. How it works Thread-locals All dates converted on output (templates)

    All dates converted on input (forms) Database stores everything in UTC Migration: consult your database manual. ... PostgreSQL: no migration needed
  56. External world

  57. External world Remember to utc.localize()

  58. External world Remember to utc.localize() Watch out for invalid dates

    (is_dst=None)
  59. That’s all, folks! Questions?

  60. That’s all, folks! Questions? Thank you! https://github.com/oinopion/twitz