congusbongus
congusbongus

Reputation: 14717

Get current GMT offset for a specific timezone

Given a specific time zone other than the local timezone (e.g. America/Los_Angeles), how can I find the current GMT offset of this timezone? The intention is to have this work with respect to DST changes. That is, the GMT offset for Los Angeles is -8, which is adjusted to -7 during daylight savings.

Some other answers suggest manipulating the TZ environment variable which affects what functions like localtime return, but I want a solution that does not mess with environment variables (being non-reentrant for instance).

It also appears that all this information is available on /usr/share/zoneinfo via zdump, but it's not obvious how I can get to this from within C++.

Upvotes: 1

Views: 3206

Answers (3)

Howard Hinnant
Howard Hinnant

Reputation: 219428

New information for an old question:

There is a new open source timezone library which will completely parse your local copy of the IANA database (including all historical data and even leap seconds). A C++11 or C++14 compiler is required. You can use this to find out detailed information about any timezone, at any time. Detailed information about this library is included in the link above.

One example program is:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    auto zone = locate_zone("America/Los_Angeles");
    auto tp = system_clock::now();
    auto info = zone->get_info(tp);
    std::cout << "Information for " << zone->name() << " at " << tp << " UTC is:\n"
              << info << '\n';

    tp = sys_days{nov/7/2014} + 53min;
    info = zone->get_info(tp);
    std::cout << "Information for " << zone->name() << " at " << tp << " UTC is:\n"
              << info << '\n';
}

Which just output for me:

Information for America/Los_Angeles at 2015-07-18 19:34:30.112820 UTC is:
2015-03-08 10:00:00
2015-11-01 09:00:00
-07:00:00
01:00
PDT

Information for America/Los_Angeles at 2014-11-07 00:53:00.000000 UTC is:
2014-11-02 09:00:00
2015-03-08 10:00:00
-08:00:00
00:00
PST

This output is interpreted as follows:

  • The day I'm answering this question, the UTC offset for "America/Los_Angeles" is -7 hours, and the abbreviation for the timezone is PDT. This is offset 1 hour from the region's "standard time". This offset and abbreviation are valid at least over this range of UTC time points: [2015-03-08 10:00:00, 2015-11-01 09:00:00).

  • The day this question was asked, the UTC offset for "America/Los_Angeles" was -8 hours, and the abbreviation for the timezone was PST. This was not offset from the region's "standard time". This offset and abbreviation are valid at least over this range of UTC time points: [2014-11-02 09:00:00, 2015-03-08 10:00:00).

This is a github project, and pull requests to help port it to as many C++11/14 platforms as practical are welcome.

Upvotes: 2

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241890

One approach you might consider is to use ICU. It contains (among other things) functions to convert between time zones. See the documentation for the TimeZone class.

For example, you can create a time zone object:

TimeZone *tz = TimeZone::createTimeZone("America/Los_Angeles");

You can then use methods like getOffset.

See also, the ICU Time Zone User Guide, and this complete example.

Upvotes: 2

congusbongus
congusbongus

Reputation: 14717

I've found a solution using Boost but it seems a bit roundabout. It uses the following steps:

  • Load a time zone database which can convert a time zone name which contains information about when DST starts and ends. This is a CSV a few hundred lines long and is easily available.
  • Get the current time using both GMT and a specific time zone, by looking up said database
  • Take the difference between the two times to get the GMT offset

It's probably clearer with a sample program:

#include <iostream>
#include <boost/date_time/local_time/local_time.hpp>

int main(int argc, char* argv[]) {
    if (argc != 2) {
            std::cerr << "Usage: program tz_name\n";
            return 1;
    }

    // Load time zone database
    boost::local_time::tz_database tz_db;
    tz_db.load_from_file("date_time_zonespec.csv");

    // Current time
    auto t = boost::posix_time::second_clock::local_time();

    // Get the time for GMT; we'll use this to compare against other time zones
    boost::local_time::time_zone_ptr gmt_tzp{new boost::local_time::posix_time_zone("GMT")};
    boost::local_time::local_date_time gmt{t, gmt_tzp};

    // Get time for specific time zone
    boost::local_time::local_date_time local{t, tz_db.time_zone_from_region(argv[1])};

    // Calculate difference between local time and GMT time
    auto difference = local.local_time() - gmt.local_time();

    // Print some information
    auto offset_minutes = difference.total_seconds() / 60;
    std::cout << "Zone " << local.zone()->std_zone_name() << " GMT offset minutes " << offset_minutes << "\n";
    return 0;
}

Sample usage and output:

$ ./a.out "America/Los_Angeles"
Zone Pacific Standard Time GMT offset minutes -480
$ ./a.out "Asia/Dubai"
Zone GST GMT offset minutes 240
$ ./a.out "Australia/Sydney"
Zone EST GMT offset minutes 660

Upvotes: 2

Related Questions