Reputation: 539
I have timezone in format GMT+09:00 and GMT+10:00. I am using Linux (Open Suse) and C++17. I am trying to find out the local time zone’s difference from GMT in minutes.
Here is my sample program.
static constexpr time_t const NULL_TIME = -1;
// returns difference in mintues
long tz_offset(time_t when = NULL_TIME) {
if (when == NULL_TIME)
when = std::time(nullptr);
auto const tm = *std::localtime(&when);
std::ostringstream os;
os << std::put_time(&tm, "%z");
std::string s = os.str();
// s is in ISO 8601 format: "±HHMM"
int h = std::stoi(s.substr(0, 3), nullptr, 10);
int m = std::stoi(s[0] + s.substr(3), nullptr, 10);
return h * 60 + m;
}
int main() {
for (auto &timezone : {"GMT+08:00", "GMT+09:30", "GMT+10:00", "GMT+10:30", "GMT+11:00"}) {
setenv("TZ", timezone, 1);
tzset();
std::cout << "----------- TZ Changed to " << timezone << "---------------\n";
std::cout << "difference in mins =" << tz_offset() << std::endl;
auto tz_env_var = getenv("TZ");
if (tz_env_var != nullptr) {
std::cout << "TZ=" << tz_env_var << "\n";
}
}
return 0;
}
But I am getting the difference in the negative, they should be positive.
----------- TZ Changed to GMT+08:00---------------
difference in mins =-480
TZ=GMT+08:00
----------- TZ Changed to GMT+09:30---------------
difference in mins =-570
TZ=GMT+09:30
----------- TZ Changed to GMT+10:00---------------
difference in mins =-600
TZ=GMT+10:00
----------- TZ Changed to GMT+10:30---------------
difference in mins =-630
TZ=GMT+10:30
----------- TZ Changed to GMT+11:00---------------
difference in mins =-660
TZ=GMT+11:00
Any pointer why I am getting them in the negative?
Upvotes: 1
Views: 832
Reputation: 218770
These strings are known as POSIX timezones and are defined here. This definition says this about the UTC offset:
If preceded by a '-', the timezone shall be east of the Prime Meridian; otherwise, it shall be west (which may be indicated by an optional preceding '+' ).
Meanwhile std::put_time
is defined in terms of the C/Posix strftime
which says this about offset:
%z is replaced by the offset from UTC in the ISO 8601 format ‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich), or by no characters if no time zone is determinable. [tm_isdst]
In summary, there's one convention for posix time zone strings (negative means east of the prime meridian), and another for just about everyone else, including other parts of posix (negative means west of the prime meridian).
So your code is actually getting the correct answer.
Fwiw, here is a C++17 free, open-source, header-only Posix time zone library which can to the job without changing the global environment variable TZ
, and does it with simpler syntax:
#include "date/ptz.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
using date::operator<<;
for (auto &timezone : {"GMT+08:00", "GMT+09:30", "GMT+10:00", "GMT+10:30", "GMT+11:00"})
{
auto offset = Posix::time_zone{timezone}.get_info(system_clock::now()).offset;
std::cout << "difference is " << duration_cast<minutes>(offset) << '\n';
std::cout << "TZ is " << timezone << "\n\n";
}
}
Output:
difference is -480min
TZ is GMT+08:00
difference is -570min
TZ is GMT+09:30
difference is -600min
TZ is GMT+10:00
difference is -630min
TZ is GMT+10:30
difference is -660min
TZ is GMT+11:00
Upvotes: 1