r5 - 09 Sep 2009 - 16:50:59 - GrantBaillieYou are here: OSAF >  Journal Web  >  ContributorNotes > GrantBaillieNotes > GrantBaillie20050809
I've been going through Chandler, making various parts of its workings timezone-aware. Here are some of the issues I've run into. For some background, have a look at the 0.6 Chandler Timezone spec.

UI Issues

  • The default timezone dropdown is too big: Mimi has suggested it move to a menu (under File, probably).
  • In week view, if you're not running on a massively wide display, we'll clip the timezone when displaying text like "3:00 PM PST".

Localization of timezone names

PyICU currently has localizations for strings of the form "PST" or "Pacific Standard Time". If, as the UI spec says, we want to display timezones as "US/Pacific", we will have to create localizable strings for these. (This is the approach taken by Apple iCal, for example).

Equivalent timezones

When running on Linux, I've noticed that my default timezone appears as "PDT", while it's (the technically more correct) "US/Pacific" on the Mac. It would be good to map this correctly, so there aren't duplications in the timezone drop-downs.

"Floating" timezones

At one point, I had put in a "floating" entry into the detail view timezone dropdown. For the most part, this worked, although it was causing weird UI errors when switching to/from floating in recurring events. So, for now, I've disabled this.

Unsafe datetime comparisons

Python's datetime API doesn't allow you to compare naïve and non-naïve datetimes (it raises a TypeError if you do so). Note that "compare" here covers the usual builtin operators like ==, <, <=, >, but also builtin functions like min, max and cmp.

Currently, all datetime objects are naïve in Chandler: This includes instances from imported events, as well as objects used internally (e.g. to figure out where to display events in the calendar, or when the event reminder dialog should be displayed). Once events with timezone information are present, then, Chandler has a dangerous mixture of naïve and non-naïve datetimes, and we need a strategy for porting over code that does comparisons.

Let's say we're trying to compare naif, a naïve datetime with sophisticate, a non-naïve instance. There are a couple of possibilities (using <= as an example):

  1. naif <= sophisticate.replace(tzinfo=None)
  2. naif.replace(tzinfo=PyICU.ICUtzinfo.getDefault()) <= sophisticate

It really depends on the situation as to which is correct. For instance, if the calendar UI is trying to determine whether or an event with a floating timezone overlaps another (non-floating) event, 2. above is correct, since "floating" events are always interpreted as occurring in the user's current time zone. However, there are cases where the first comparison is what we want.

For now, for minimizing code changes (or making them more obvious), I've added API on the Calendar module that uses the regular Python comparison API if it's safe, and falls back to 1. otherwise. Where now you have

>>> naive <= sophisticate

you would use the API via:

>>> Calendar.datetimeOp(naive, '<=', sophisticate)

This is somewhat of a stopgap measure, partly because all these usages have to be reviewed to check whether #2. should be used instead.

An abundance of naïveté

There are cases in our code where calculations are performed incorrectly when datetimes have differing timezones. For example, the calendar UI assumes that you can figure out what day a given event starts on by using the datetime class's toordinal() method.

Along similar lines, code that changes event information (e.g., editing of start and end times in the detail view, or drag-and-drop of events in the calendar UI) needed to be changed to make sure that timezone information was preserved. In many cases, the old code would drop all time zone info from the newly created datetime instances.

dateutil

JeffreyHarris pointed out that the python dateutil library, our underlying implementation for recurrence, uses unsafe datetime comparisons everywhere. As a result, we need to make sure that datetimes accessible to a given rruleset are either all naïve or all non-naïve. The current implementation attempts to do more than that by making sure that all these datetimes have the same tzinfo; there may be cases that have been missed, though. (For example, this would have to apply to any arguments to the before or after methods. Maybe subclassing dateutil.rrule.rrule{,set} would be a better way to deal with this problem than the current approach of tweaking all the call sites.)

PyICU DateFormat usage

There is somewhat of a mismatch between Python datetimes and PyICU's DateFormat (or MessageFormat). In ICU, times contain no timezone information (they're represented as a POSIX timestamp). Timezone is owned by DateFormat objects: Unless specified otherwise, Format objects are created with the user's current timezone. So, when getting a displayable string from a datetime with a non-default timezone, you have to call setTimeZone() before using any DateFormat instances. (For MessageFormat instances, the situation is more complex because you have to do this for any date sub-formats).

Note: DateFormat.parse() has similar issues.

Possibly we should make PyICU itself could deal with this (since it already has code to coerce datetime objects to ICU's UDate). This would probably not be hard to change for DateFormat, but MessageFormat would be more difficult. In general, the ICU formatting API isn't very Pythonic (e.g., Formattable objects would probably not exist if ICU had originated in a dynamic language). Whether it's worth addressing this general concern in PyICU is open to question.

For the moment, I've worked around this in AttributeEditors by adding a DateFormatter class that wraps a PyICU.DateFormat instance, but deals with changing the timezone when you call its format() method. Similarly, it has a parse() method that takes a ICUtzinfo argument and returns a datetime in the correct timezone.

-- GrantBaillie - 11 Aug 2005

Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r5 < r4 < r3 < r2 < r1 | More topic actions
 
Open Source Applications Foundation
Except where otherwise noted, this site and its content are licensed by OSAF under an Creative Commons License, Attribution Only 3.0.
See list of page contributors for attributions.