
Ben Gamari wrote:
Recently I've been working on an iCalendar parser implementation with support for recurrence rules. For the recurrence logic, I've been relying on Chris Heller's excellent time-recurrence package. Unfortunately, it seems we have both encountered great difficulty in cleanly handling time zones... tz = unsafePerformIO getCurrentTimeZone Ouch... Passing the current timezone as an argument seems heavy-handed and reveals the leakiness of the CalendarTimeConvertible abstraction. Short of making toCalendarTime an action, can anyone see a suitable way of refactoring CalendarTimeConvertible to avoid unsafe IO?
Well, timezone info definitely is something from the outside world, and it even changes over time (very slowly). So it's IO. Yet just about all of what your library does is pure. So passing timezone info as a parameter to pure functions is the most straightforward approach. You would pass the timezone as a parameter to all pure functions that need it. If the need for the tz parameter is ubiquitous, it might then be worth it to use a pure reader monad of some kind. As a convenience, you might provide one or more functions that do the timezone lookup, do the pure calculation, and return the result in IO. All of that does require refactoring CalendarTimeConvertible though, yes. I think the use of unsafePerformIO is a pretty sure sign that the design of CalendarTimeConvertible could use some improvement.
Perhaps a TimeZoneInfo monad is in order, exposing lookup of arbitrary (through timezone-olson) as well the current timezone? It seems like this would be inconvenient and overkill, however.
I agree. This whole calculation is pure, really. It would be a shame to force the entire thing into an impure monad just because of timezones. Regards, Yitz