[This part of my reply was composed after reading several replies:] When a zone defines that a rule set is valid until before 1990 Sep 3 at 2:00w or 2:00s, how exactly am I supposed to find out when that is? Either way, the exact time of that switch depends on the UTC and save offsets around that point in time, ie the rule set (Zone definition) and/or the relevant rule in the moment before and/or after that time. Both base UTC offset and additional save offset may change in that moment. Which of them shall be applied, the previous or the next interval's values? Is there some higher-level algorithm available of how that works? Right now I can't even think of a way to do it 'manually' by reading the text files and calculating stuff in my brain. How should I tell a stupid computer what to do? The ZoneInfo code (C#) doesn't even care about it and produces errors. The (now dead?) (full-blown) PublicDomain library (C#) seems to be restricted to two transition times per year (like Windows) and also doesn't seem to care very well about the exact moment of change. I don't understand the cryptic original code (C) of the tz distribution. Android's implementation (Java) in org.apache.harmony seems too short to be complete. The tz format description doesn't explain how to use the data programmatically. So right now I understand the data format but don't see how it can be used for real computing. [This part of my reply was composed after reading Guy Harris' reply:] I was totally unaware that zic does all that. It sounds interesting though. I think I could find an algorithm for UTC to local conversion with all transition times in UTC. The reverse function however would still be educated try and error. I can't compile zic.c with Visual Studio 2008, some unixoid libraries are missing and I don't think I have them. Using the Makefile doesn't look more promising. So I need to implement zic's function - or something similar - myself. The format description of the compiled tzfile is very verbose which makes it hard to get an overview of what is stored where. I have attached an incomplete pseudo-code description of what I think it might look like. Anyway it would just be an aid in reverse-analysing the zic code. (I find it helpful to start from the output so I won't see anything I don't need for that.) But now I'm wondering about where the annual rules are gone. The compiled tzfile only seems to contain fixed transition times. Anything that's not stored literally in the file is unknown. Are all dates between 0001-01-01 and 9999-12-31 stored in the file? Or just between 1970-01-01 and 2038-xx-xx, or maybe even in a configurable time span? Doesn't that produce masses of redundant data and limit the usage of the database? -- Yves Goergen "LonelyPixel" <nospam.list@unclassified.de> Visit my web laboratory at http://beta.unclassified.de struct TZFileFormat { char8[4] magic = "TZif"; char8 version = '2'; byte[15] reserved; int32 ttIsGmtCount; int32 ttIsStdCount; int32 leapCount; int32 timeCount; int32 typeCount; int32 charCount; int32[timeCount] transitionTimes; // UNIX timestamp* of each transition time (that's when the rule changes) byte[timeCount] ttLocalTimeTypes; // specify 'local time type' (see timeTypes) for the same-indexed transitionTimes item ttInfo[typeCount] timeTypes; // purpose unclear ??? // array of time zone abbreviation characters (undefined) leapData[leapCount] leapSeconds; // sorted by .time byte[ttIsStdCount]; // standard/wall indicators for transition times associated with local time types - unused? byte[ttIsGmtCount]; // UTC/local indicators for transition times associated with local time types - unused? // now comes version-2-specific data (see tzfile(5), last paragraph) // which is the entire file once again, only with int64 types for all transition times and leap seconds times // followed by char8[] = /\n[^\n]*\n/ (purpose unclear) } struct ttInfo { int32 gmtOffset; // offset to UTC in seconds byte isDst; // indicates whether this is DST byte abbrIndex; // index into "array of time zone abbreviation characters" (undefined) } struct leapData { int32 time; // UNIX timestamp* of the time when a leap second occurs int32 total; // total number of leap seconds to be applied after that time } // *) UNIX timestamp: seconds since 1970-01-01T00:00:00Z