Horde Hackathon 2011 part 1
A few more details about my personal programming projects during the Horde Hackathon 2011 in Boston last weekend. There's also some insight into how timezones work in Horde and a new PHP library for timezone support that might be interesting for other projects too.
Chuck already summed up the Hackathon on his blog pretty well. I'd like to get a bit more into detail about what I've been working on personally.
Improved time zone support
Event time zones
Horde's calendar application Kronolith introduced time-zone-independent events already in version H4. Until then all events had been stored with a "floating" time zone which actually means with no time zone information at all. It was assumed that the event time was specified in the user's time zone. This doesn't work well if using shared calendars or planning meetings with users from different time zones though.
We introduced an option in Kronolith H4 to store all event times in UTC, to make them easily portable to all users' time zones. This allows to schedule meetings with other users across the globe.
This was an improvement to earlier calendar versions but still didn't solve all problems. This only works correctly for single events because in those cases it's simple to convert them to the users' local time zones. This doesn't work anymore if using recurring events, because those pesky daylight saving time (DST) transitions might occur during the event lifetime.
With the next version of Kronolith, you will be able to specify an individual time zone for each event. This is a great feature for organizations that work across different time zones or that even experience different daylight saving time rules in their countries.
It's great if you can create events in different time zones and have them always correctly converted to the user's time zone within Kronolith. But that doesn't help while interacting with other calendar clients. The iCalendar standard RFC 5545 for exchanging calendar data allows to specify times in 3 different formats:
- Coordinated Universal Time (UTC)
- Floating time zone (always in the user's current time zone)
- Local time zone (alwas in a fixed local time zone)
So far we have only been using UTC format when exporting to iCalendar. But this solution is affected by the same problems with recurring events outlined above. To fix those issues we have to specify event time zones in iCalendar data where appropriate. Unfortunately the specification authors overcomplicated how to pass time zone information in iCalendar. Instead of relying on the de-facto standard time zone names and rules specified by the Olson database, it requires developers to actually pass all time zone rules affecting a single event with this event. They introduced the VTIMEZONE component that's purpose is to provide DST rules for arbitrary time zone names. These names can then be used in TZID attributes for events provided in the same iCalendar file. Fortunately the RFC authors at least suggested to use the names from the Olson database, and all calendar developers seem to have silently agreed to follow that advise and to not make up any arbitrary new time zone names.
And we actually only use these time zone names when importing iCalendar data to Kronolith and ignore the specified time zone rules completely. Otherwise we would have to store the complete VTIMEZONE component with each imported event, parse those rules into algorithms, and then use these algorithms to calculate an event's actual time each time this event is used anywhere. This is of course completely insane and a performance nightmare, especially with the easy alternative available, to simply hand off the time calculation to the great Date/Time extension in PHP.
There is an alternative to TZID attributes and VTIMEZONE components, the non-standard X-WR-TIMEZONE attribute. But guess what, it doesn't work with all clients. For best compatibility the only option is to take the DST rules from the Olson database and convert them into VTIMEZONE components. I was very suprised to find that there doesn't seem to be any (at least publicly available) PHP library for that. Even worse, there is no easy way to retrieve these rules from the database either. The timezonedb provided by PHP and available via PECL only provides pre-calculated rules for a few decades. What I needed was access to the low-level rules though because:
- I don't want to list all DST switches for the duration of the event recurrance.
- This wouldn't even be possible for events that don't have a recurrence end or duration.
- Those rules could actually be translated directly into iCalendar recurrence rules covering any year in the past or future.
I ended up writing the Horde_Timezone library that retrieves the Olson database (now simply called Time Zone Database after it has been handed over to the IANA), parses it, and converts the rules to VTIMEZONE components and RRULE attributes.
The library has not been released yet, i.e. it's only available from Git right now, but it will be available from the Horde PEAR channel. Even though it's designed to work as seemlessly with other Horde libraries as possible, it works just fine as a standalone component like any Horde library. It could be easily integrated into other projects. And feedback from other projects interested in this library, e.g. suggestions about useful API additions are very welcome.
I'm not yet happy with how I convert <= and >= rules to RRULEs either, so any hints for a more elegant solution are appreciated.
Floating time zone
What's left to do to complete the time zone support in Kronolith is to implement events with floating time zones while still using the UTC storage. At the moment, the administrator has two choices:
- Using floating time zones, i.e. each user is literally seeing the same time of events, no matter in which time zone he actually is.
- Enabling the UTC storage and thus binding all events to a certain time zone, i.e. to UTC by default, or to a different time zone per event.
But you cannot have both right now. Floating time zones still make sense though, because you might want to set up events like "Workout at 7am" no matter on which side of the globe you currently are. (Okay, I really made up this example, no sane person would be doing workouts at this time of day).
I'm also reconsidering whether to save events in UTC at all, now that we have time zone support for individual events. Most probably the user really wants to use his current time zone, so we should at least default to that.
The flip side of this whole story is, that searching for events within a certain time period cannot be (completely) delegated to the backend anymore, because there is no real time zone support in SQL, not even in the different RDBM dialects.
More from the Hackathon in part 2.