(This happened last Friday, but I've had not enough time to write about it.) After fixing the Evolution Data Server crashes (let's call it E-D-S for simplicity), I noticed a strange problem caused by it. The GNOME Clock applet showed the right local time before it was clicked, but, after the calendar was shown (by clicking on the text), the time got changed to UTC and there was no way to reverse it (other than killing the applet).
How strange. My first action was to verify that I had selected the right timezone in Evolution's configuration; no problems here. So "it must be another problem in E-D-S", I thought. Ok... "let's debug it; it's going to be fun".
I started by looking at the applet's source code to see how it gathered the local time: it uses the localtime(3) function provided by libc; "Hmm, interesting; E-D-S is corrupting the results of a libc function". Took a quick look at its manpage, and saw that its behavior was affected by the tzname global variable and by the TZ environment variable. So it was fair to think that E-D-S was modifying one of these two variables in an unexpected way. The problem was to locate where this was happening, specially because GNU GDB doesn't work very well with threaded applications in NetBSD.
I tried to grep E-D-S sources to look for a tzname variable (but I didn't bother to look for TZ; stupid me). I didn't find what I was looking for, though I saw multiple functions in the libical library that dealt with timezones. Hmm... the GNOME Clock applet was using them. So, I took its sources and added several printf(3)s before and after calls to the timezone-related functions, showing the value returned by localtime(3). After multiple tries, I located the function with side effects: icaltime_as_timet_with_zone.
Went to E-D-S sources again, searched for this function and found some suspicious code:
/* Set TZ to UTC and use mktime to convert to a time_t. */
old_tz = set_tz ("UTC");
t = mktime (&stm);
unset_tz (old_tz);
The next step was to look for the set_tz function in the same file and check what it was doing. Effectively, it was changing the TZ environment variable, using putenv(3) function. Looking at the unset_tz function confirmed my predictions. This simple line of code:
putenv("TZ"); /* Delete from environment */
was the guilty one (I had met this problem before in some other program that I can't remember now, so this is why I know without other tests). "Aha! I've got you, little bug!", I thought (locating and fixing a bug is a good sensation ;-). Let's see why this was a problem.
putenv("FOO") deletes a variable from the environment in some systems (like Linux). However, on others (such as NetBSD), it does nothing, leaving the environment unmodified.
The solution is to use unsetenv("FOO") on systems that have it, because it behaves in a deterministic way. However, if this function does not exist, putenv(3) is probably the only way to go, hoping that it will do the right thing.
This bug is reported here.