Dealing with time zone conversions (C++ / Boost)

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.

 

Share Button

Comments

  1. Adel says:

    Great article! I was considering using boost.

    Thanks for the great article!

Speak Your Mind

*