Recently I was tasked with writing some code to convert between arbitrary time zones. I did some research and there are a few good libraries out there for C++ that make this task easy. I decided out implementation would use boost. We already use boost to handle various other things within our code base and some of the other solutions I found would require introducing some new dependencies I wasn’t comfortable with for this particular project.
I ended up using boost’s timezone database to store all of the available timezones. Although boost includes a sample timezone database I ended coordinating with the web guys, they are using moment.js and provided me with the timezone file they are using. My implementation uses a local resource which is a duplicate of this file but it can also update itself in the event that the timezone data changes (If you would like a copy shoot me an email).
The timezone data is in POSIX TZ format, now.. an interesting thing about boost is the way UTF offset are handled. I ended up having to invert the values for the timezone in order to get the calculations to work correctly. The function I used looks like this :
void TimeConversion::POSIXToBOOST(std::string& posixTZ) { boost::local_time::posix_time_zone tz(posixTZ); //This conversion is required because boost expects offset inverse of standard format. // if (posixTZ.length() > 3) { uint32_t pos = tz.std_zone_abbrev().length(); auto isMinus = posixTZ.at(pos) == '-'; auto isZero = posixTZ.at(pos) == '0'; if (isMinus) { posixTZ.replace(pos, 1, ""); } else if (!isZero) { posixTZ.insert(pos, "-"); } } }
Now after I was done importing into the boost tz database, I wrote a few functions to do the actual conversion between time zones. The functions take the desired regions for which the conversion needs to happen.
boost::posix_time::ptime TimeConversion::ConvertTime(const std::string& timezoneRegion, const boost::posix_time::ptime& original, const std::string& convertToTimezoneRegion) const { auto srcTimezone = m_timeZoneDB.time_zone_from_region(timezoneRegion); auto dstTimezone = m_timeZoneDB.time_zone_from_region(convertToTimezoneRegion); boost::local_time::local_date_time srcDateTime(original.date(), original.time_of_day(), srcTimezone, boost::local_time::local_date_time_base<>::NOT_DATE_TIME_ON_ERROR); return srcDateTime.local_time_in(dstTimezone).local_time(); } boost::posix_time::ptime TimeConversion::ConvertTimeLocal(const std::string& timezoneRegion, const boost::posix_time::ptime& original) const { auto srcTimezone = m_timeZoneDB.time_zone_from_region(timezoneRegion); boost::local_time::local_date_time srcDateTime(original.date(), original.time_of_day(), srcTimezone, boost::local_time::local_date_time_base<>::NOT_DATE_TIME_ON_ERROR); auto totalOffset = GetUTCOffset() - (srcDateTime.local_time() - srcDateTime.utc_time()); return srcDateTime.local_time() + totalOffset; }
Finally, in order for this to all work on windows (our special snowflake) I had to write some special code to convert the windows timezone names to the standard region names. I was able to find someone who had went through the trouble of creating a table which had the mappings I needed. I load these from a file with support for updates from our server. You can find it here. If it goes offline for some reason you can email me to request a copy. I converted it from XML to JSON for our purposes as it was easier to consume that way. The function listed below gives me the timezone in use on the machine, I then map this using the file above to the appropriate region.
std::string NinjaAgentCommon::GetWindowsTimeZone() { // Windows Timezone info. TIME_ZONE_INFORMATION tzi; GetTimeZoneInformation(&tzi); // Timezone string return WCHAR2StdString(tzi.StandardName); }
This library was a close runner up
https://howardhinnant.github.io/date/tz.html
This timezone library is probably one of the best I found, and I would not hesitate to use it under different circumstances. It provides a straight forward way to do time zone conversions as well as being cross platform and using the latest IANA timezone database.
Great article! I was considering using boost.
Thanks for the great article!