Peter
Peter

Reputation: 2320

put_time() ignores conversion specifier 'z' for time zone offset

The following code shall print the time with the time zone offset, the lowercase format specifier z is passed:

#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;

int main() {
    time_t current_time = time(nullptr);
    tm* timeinfo = localtime(&current_time);
    cout << put_time(timeinfo, "%c %z") << "\n";
    return 0;
}

g++ -o time time.cpp -Wall -pedantic

The function put_time() (C++) is described on cppreference.com. The format specifiers are the ones from strftime() (C). You can also read about them in the C Standard, currently page 396 in chapter 7.27.3.5:

  %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]

This works well on Linux:

Fri Jul 26 12:30:02 2019 +0200

But on Windows it's returning:

07/26/19 12:03:53 Mitteleuropäisches Sommerzeit

That's wrong, my code doesn't ask for the name of the timezone, but for the offset to UTC. I'm using on both platforms GCC as compiler.

Question
Am I doing something wrong here? Or is either GCC or Microsoft Windows violating the standard? I read some "rumors" about that it could be influenced through some settings on Windows.

Caluclating the offset by hand is rather treacherous. The best helper would be tm.gmt_offset, which is platform specific field. Therefore gmt_offset is not listed and only available on GNU based systems. I'm currently using a workaround based on GetTimeZoneInformation and the fields Bias and DaylightBias, which is a platform specific Windows-API. I appreciate more secure suggestions.

Thank you for help and insights!

Environments:
GNU/Linux with GCC 9.1.0 (Archlinux)
Windows with GCC 8.3.0 (MSYS2)

Upvotes: 1

Views: 420

Answers (1)

Howard Hinnant
Howard Hinnant

Reputation: 219205

If you are willing to use a free, open-source (MIT license) 3rd party library, it will give portable results.

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

int
main()
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;
    zoned_seconds zt{current_zone(), floor<seconds>(system_clock::now())};
    cout << format("%c %z", zt) << '\n';
}

Though on Windows you will need to install curl and 7-zip, and on linux you'll need to link with -lcurl. More detailed installation instructions are available here.

This is a preview of the draft C++20 <chrono> library, though admittedly the syntax for the format statement is going to change slightly due to votes being taken just last week in Cologne. C++20 will incorporate the fmt library (which is great news).

This library still uses put_time for the %c part of the format, but formats the %z part itself (and gives the offset in [+/-]hhmm form as desired).

Upvotes: 1

Related Questions