#945130 mingw-w64-x86-64-dev: strftime fails on %e and gives incorrect string for %z

#945130#5
Date:
2019-11-20 10:25:02 UTC
From:
To:
Dear Maintainer,

I am trying to get a string representation of a date via strftime function.
For some reason the function call fails whenever format contains %e (day of month with leading space instead of zero).
Also %z (time zone offset) results in "Central Europe Standard Time" (time zone name) instead of +0100 or +0000 (the time zone offset).

Here is a small program "strftime.c" demonstrating the issue:

#include <inttypes.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>

const char* get_str(char* buf, size_t max, const char* format, const struct tm* lt)
{
	snprintf(buf, max, "failure");
	size_t sz = strftime(buf, max, format, lt);
	if (sz==0 && buf[0]==0)
		snprintf(buf, max, "(empty) errno=%d", errno);
	return buf;
}

int main(){
	const char* format[] = {"[%a]","[%e]","[%d]","[%-d]","[%#d]","[%b]",
							"[%Y]", "[%H]","[%M]","[%S]",
							"[%z]", "[%Z]", "[%b%e, %Y, %H:%M:%S %z]"};
	const size_t format_sz = sizeof(format)/sizeof(format[0]);
	//                  sec min hr DoM  Mo    Year      DoW  DoY  dst
	struct tm mytime = {  1,  2, 3,  5, 11-1, 2019-1900,  2, 308,   0 };
	char buf[256];
	for (unsigned int i=0; i<format_sz; ++i)
		printf("%s: %s\n", format[i],
			   get_str(buf, sizeof(buf), format[i], &mytime));
}

Compile (three different versions: Windows 32bit, 64bit and Linux to compare):

i686-w64-mingw32-gcc -static -O3 -Wall -o strftime.exe strftime.c
x86_64-w64-mingw32-gcc -static -O3 -Wall -o strftime64.exe strftime.c
gcc -static -O3 -Wall -o strftime strftime.c

Execute:
./strftime.exe
[%a]: [Tue]
[%e]: (empty) errno=22
[%d]: [05]
[%-d]: (empty) errno=22
[%#d]: [5]
[%b]: [Nov]
[%Y]: [2019]
[%H]: [03]
[%M]: [02]
[%S]: [01]
[%z]: [Central Europe Standard Time]
[%Z]: [Central Europe Standard Time]
[%b%e, %Y, %H:%M:%S %z]: (empty) errno=22

./strftime64.exe
[%a]: [Tue]
[%e]: (empty) errno=22
[%d]: [05]
[%-d]: (empty) errno=22
[%#d]: [5]
[%b]: [Nov]
[%Y]: [2019]
[%H]: [03]
[%M]: [02]
[%S]: [01]
[%z]: [Central Europe Standard Time]
[%Z]: [Central Europe Standard Time]
[%b%e, %Y, %H:%M:%S %z]: (empty) errno=22

./strftime
[%a]: [Tue]
[%e]: [ 5]
[%d]: [05]
[%-d]: [5]
[%#d]: [05]
[%b]: [Nov]
[%Y]: [2019]
[%H]: [03]
[%M]: [02]
[%S]: [01]
[%z]: [+0000]
[%Z]: [CET]
[%b%e, %Y, %H:%M:%S %z]: [Nov 5, 2019, 03:02:01 +0000]

Note that [%e] yields a completely empty string, i.e. it does not even include the square braces,
therefore %e is not just empty, but it is rather an error from strftime (also errno 22 indicates "Invalid argument").
Also [%z] is just plain wrong.
The same program on Linux produces the expected (correct) results.

I am getting identical issues with wine and real Windows, also different locales (LANG=en_US.UTF-8, LANG=C), meaning that it is somewhere in Windows glibc implementation.
Also very old versions (from Jessie) have this issue, so it is probably a very old upstream problem.
I think it is unlikely that something is wrong with my installations (I tried on Jessie and current unstable).
I have some older static binaries (from 2014) where this issue does not appear (sorry being vague about versions).

Moreover Windows documentation seems to coincide with Linux (there is nothing special about %e and %z):
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strftime-wcsftime-strftime-l-wcsftime-l?view=vs-2019

Best regards,
Marius

#945130#10
Date:
2019-11-21 00:16:17 UTC
From:
To:
Hello Marius Mikucionis,
I am not anyhow involved in maintaining mingw-w64.
But I guess I found something.

First I fear that mingw-w64 does not link as much static
as you expect it to.
All crossbuilt executables still dynamically link to msvcrt.dll.

    $ i686-w64-mingw32-objdump -p strftime-6.exe | grep 'DLL Name:'
            DLL Name: KERNEL32.dll
            DLL Name: msvcrt.dll

And the Microsoft page might just be right for later versions
of c runtimes they provide.

Further I found this information [2], which is as far as
I understand about recompiling mingw.
But I tried just to modify the compile/link command,
which I guess is kind of the expected output?

    $ i686-w64-mingw32-gcc -static -O3 -Wall -o strftime-ucrt-7.exe strftime.c -lucrt
    $ i686-w64-mingw32-objdump -p strftime-ucrt-7.exe | grep 'DLL Name:'
            DLL Name: KERNEL32.dll
            DLL Name: msvcrt.dll
            DLL Name: api-ms-win-crt-environment-l1-1-0.dll
            DLL Name: api-ms-win-crt-heap-l1-1-0.dll
            DLL Name: api-ms-win-crt-private-l1-1-0.dll
            DLL Name: api-ms-win-crt-runtime-l1-1-0.dll
            DLL Name: api-ms-win-crt-stdio-l1-1-0.dll
            DLL Name: api-ms-win-crt-string-l1-1-0.dll
            DLL Name: api-ms-win-crt-time-l1-1-0.dll

    $ wine strftime-ucrt-7.exe
    [%a]: [Tue]
    [%e]: [ 5]
    [%d]: [05]
    [%-d]: (empty) errno=22
    [%#d]: [5]
    [%b]: [Nov]
    [%Y]: [2019]
    [%H]: [03]
    [%M]: [02]
    [%S]: [01]
    [%z]: [+0100]
    [%Z]: [Mitteleuropõische Zeit]
    [%b%e, %Y, %H:%M:%S %z]: [Nov 5, 2019, 03:02:01 +0100]

(msvcrt.dll still appears; the same result for 7.0.0-2 and 6.0.0-3.)


So maybe you could examine this binaries from 2014 if they
need also some Dlls?

At least the binaries produced in my tests behave the same
in Wine and Windows.

Kind regards,
Bernhard


[1] https://stackoverflow.com/questions/55728544/how-to-avoid-msvcrt-dll-compiling-with-mingw64

#945130#15
Date:
2019-11-21 11:09:44 UTC
From:
To:
2019-11-21, kt, 01:16 Bernhard Übelacker <bernhardu@mailbox.org> rašė:

Interesting. I get exactly the same.

I can reproduce this too.

Your output is correct, but I am not getting that, very strange.

Yes, they have those dependencies and more (like WS2_32.DLL and
IPHLPAPI.DLL, but they are unrelated).

Even without -lucrt ?
The ucrt adds those api-ms-win-crt-* dependencies, which do not seem to be
necessary.

Thank you for the effort, perhaps something strange is happening with my
setup, I could not find anyone else on internet with this issue.

#945130#20
Date:
2019-11-21 12:13:19 UTC
From:
To:
Hi,

Thanks to both of you for taking the time to file the bug and investigate
this!

Yes, strftime and most other C library functions are implemented in the
underlying CRT, not MinGW-w64.

I think so too; there’s a drop-down to select different versions of Visual
Studio, but all the versions for which documentation is currently provided
use UCRT.

Yes, this is normal. Doing a proper link with UCRT requires at minimum
overriding the GCC spec file, as described in
https://sourceforge.net/p/mingw-w64/mailman/message/36621319/

I’m guessing this works for Bernhard because he’s using a newer version of
Wine than either Marius or myself; %e was implemented in 4.18, for _MSVCR_VER
140 or later, and %z in 4.20.

They’re all part of the UCRT interface; you’ll get the same dependencies with
Visual Studio.

There’s nothing strange in your setup!

I’m wondering what the appropriate solution is. Strictly speaking, this isn’t
a bug in MinGW-w64, and using the default MSVCRT target there’s nothing much
we can do. However it does raise the question of UCRT support in Debian...

Regards,

Stephen

#945130#25
Date:
2019-11-21 12:14:46 UTC
From:
To:
Am 21.11.19 um 12:09 schrieb Marius Mikučionis:

I forgot to mention that I used a local built wine-4.20.
There were lately some changes in that area in Wine, therefore
if you use an older Wine version it might behave different.

https://source.winehq.org/git/wine.git/history/HEAD:/dlls/msvcrt

But does the executable with -lucrt print the expected
output in Windows?

Looking at the complete output of i686-w64-mingw32-objdump shows that
without -lucrt strftime is imported from msvcrt.dll.
With -lucrt it imports strftime from api-ms-win-crt-time-l1-1-0.dll.
That is what I guess makes the difference.

Kind regards,
Bernhard

#945130#30
Date:
2019-11-21 15:13:34 UTC
From:
To:
2019-11-21, th, 13:14 Bernhard Übelacker <bernhardu@mailbox.org> wrote:

OK.
I see that Debian has wine64-development-4.19 and 4.20, I will test those.

Summary for %e:
           | no -lucrt  | with -lucrt
windows    | fail       | pass
wine-4.0.2 | fail       | fail
wine-4.19  | fail       | pass
wine-4.20  | fail*      | pass

Summary for %z:
           | no -lucrt  | with -lucrt
windows    | fail       | pass
wine-4.0.2 | fail       | fail
wine-4.19  | fail       | fail
wine-4.20  | fail       | pass

* wine-4.20 also prints extra debug information for %e without -lucrt:
0009:err:msvcrt:MSVCRT__invalid_parameter (null):0 (null): (null) 0

Looks like the issue is with the runtime environment and not with mingw.
I still would like to understand if/why -lucrt is necessary in this case.
The documentation does not mention any libraries, only headers.

Does that mean that strftime implementation in msvcrt.dll is faulty?

#945130#35
Date:
2019-11-21 16:27:57 UTC
From:
To:
Am 21.11.19 um 16:13 schrieb Marius Mikučionis:

From my point of view, Microsoft cannot change msvcrt.dll because plenty
legacy software may rely on the "old" behaviour.
Therefore we got then msvcr90.dll and so on. And now they invented UCRT.
As Stephen wrote in message 20, if you compile with visual studio the
binary will link also against these UCRT libraries.

And documentation seems just to cover supported development
environments...

Kind regards,
Bernhard