bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* nstrftime %c bug w.r.t. time zone
@ 2024-02-08  0:04 Bruno Haible
  2024-02-08 11:50 ` Bruno Haible
  0 siblings, 1 reply; 5+ messages in thread
From: Bruno Haible @ 2024-02-08  0:04 UTC (permalink / raw)
  To: bug-gnulib

Hi Paul,

time.in.h says:

  (timezone_t) NULL stands for UTC.

However, nstrftime, on NetBSD 7 and Solaris 11, prints my local time zone
("CET"), not "UTC" or "GMT", in this test program:

================================== foo.c =================================
#include <config.h>
#include <assert.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "strftime.h"
int main ()
{
  setlocale (LC_ALL, "fr_FR.UTF-8");

  time_t t = 1509000003;
  struct tm *tm = gmtime (&t);
  int ns = 123456789;
  char buf[100];
  size_t n;

  n = nstrftime (buf, sizeof buf, "%+4Y-%m-%d %H:%M:%S.%N", tm, 0, ns);
  assert (n > 0);
  printf ("Format as ISO 8601: %s\n", buf);
  assert (strcmp (buf, "2017-10-26 06:40:03.123456789") == 0);

  n = nstrftime (buf, sizeof buf, "%c", tm, 0, ns);
  assert (n > 0);
  printf ("%%c directive: %s\n", buf);
}
/* Output on
NetBSD 7.1:   26 octobre 2017 06:40:03 CET
Solaris 11.4: 26 octobre 2017 à 06:40:03 CET
*/
============================================================================
How to run it:
- Use a testdir that contains module 'nstrftime'.
- Build it.
- $ gcc -Wall foo.c -I. -Igllib -I../gllib gllib/libgnu.a
  $ ./a.out

Another way to reproduce it:
  1. Create a testdir for module 'nstrftime'.
  2. Transport it to a NetBSD or Solaris system.
  3. Run it. Result: test-nstrftime-2.sh fails at test-nstrftime.h:421.

Bruno





^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: nstrftime %c bug w.r.t. time zone
  2024-02-08  0:04 nstrftime %c bug w.r.t. time zone Bruno Haible
@ 2024-02-08 11:50 ` Bruno Haible
  2024-02-08 13:55   ` Bruno Haible
  2024-02-09  7:36   ` Paul Eggert
  0 siblings, 2 replies; 5+ messages in thread
From: Bruno Haible @ 2024-02-08 11:50 UTC (permalink / raw)
  To: bug-gnulib

I wrote:

> time.in.h says:
> 
>   (timezone_t) NULL stands for UTC.
> 
> However, nstrftime, on NetBSD 7 and Solaris 11, prints my local time zone
> ("CET"), not "UTC" or "GMT", in this test program:

This patch provides a workaround, and thus removes the test-nstrftime-2.sh
failure at test-nstrftime.h:421.


2024-02-08  Bruno Haible  <bruno@clisp.org>

	nstrtime, c-nstrftime: Fix %c directive's result on NetBSD, Solaris.
	* lib/strftime.c (__strftime_internal): On NetBSD and Solaris, remove
	the last word of the %c directive's result if it looks like a time zone.

diff --git a/lib/strftime.c b/lib/strftime.c
index f6714524bd..b70494cf78 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -967,7 +967,48 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
             len = strftime (ubuf, sizeof ubuf, ufmt, tp);
 # endif
             if (len != 0)
-              cpy (len - 1, ubuf + 1);
+              {
+# if defined __NetBSD__ || defined __sun /* NetBSD, Solaris */
+                if (format_char == L_('c'))
+                  {
+                    /* The output of the strftime %c directive consists of the
+                       date, the time, and the time zone.  But the time zone is
+                       wrong, since neither TZ nor ZONE was passed as argument.
+                       Therefore, remove the the last space-delimited word.
+                       In order not to accidentally remove a date or a year
+                       (that contains no letter) or an AM/PM indicator (that has
+                       length 2), remove that last word only if it contains a
+                       letter and has length >= 3.  */
+                    char *space;
+                    for (space = ubuf + len - 1; *space != ' '; space--)
+                      ;
+                    if (space > ubuf)
+                      {
+                        /* Found a space.  */
+                        if (strlen (space + 1) >= 3)
+                          {
+                            /* The last word has length >= 3.  */
+                            bool found_letter = false;
+                            const char *p;
+                            for (p = space + 1; *p != '\0'; p++)
+                              if ((*p >= 'A' && *p <= 'Z')
+                                  || (*p >= 'a' && *p <= 'z'))
+                                {
+                                  found_letter = true;
+                                  break;
+                                }
+                            if (found_letter)
+                              {
+                                /* The last word contains a letter.  */
+                                *space = '\0';
+                                len = space - ubuf;
+                              }
+                          }
+                      }
+                  }
+# endif
+                cpy (len - 1, ubuf + 1);
+              }
           }
           break;
 #endif





^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: nstrftime %c bug w.r.t. time zone
  2024-02-08 11:50 ` Bruno Haible
@ 2024-02-08 13:55   ` Bruno Haible
  2024-02-09  7:36   ` Paul Eggert
  1 sibling, 0 replies; 5+ messages in thread
From: Bruno Haible @ 2024-02-08 13:55 UTC (permalink / raw)
  To: bug-gnulib

> 2024-02-08  Bruno Haible  <bruno@clisp.org>
> 
> 	nstrtime, c-nstrftime: Fix %c directive's result on NetBSD, Solaris.
> 	* lib/strftime.c (__strftime_internal): On NetBSD and Solaris, remove
> 	the last word of the %c directive's result if it looks like a time zone.

This patch adds a mention of the problem to the documentation:


diff --git a/doc/posix-functions/strftime.texi b/doc/posix-functions/strftime.texi
index 7c4c0391fa..b62ea37fdb 100644
--- a/doc/posix-functions/strftime.texi
+++ b/doc/posix-functions/strftime.texi
@@ -16,6 +16,11 @@
 Portability problems not fixed by Gnulib:
 @itemize
 @item
+The %c specifier does not work with a time retrieved through @code{gmtime}
+or @code{gmtime_r}, since it outputs also the current time zone,
+on some platforms:
+NetBSD 9.3, Solaris 11.4.
+@item
 The %r specifier produces empty output, at least in a French locale,
 on some platforms:
 macOS 12.5, FreeBSD 14.0.





^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: nstrftime %c bug w.r.t. time zone
  2024-02-08 11:50 ` Bruno Haible
  2024-02-08 13:55   ` Bruno Haible
@ 2024-02-09  7:36   ` Paul Eggert
  2024-02-09 10:10     ` Bruno Haible
  1 sibling, 1 reply; 5+ messages in thread
From: Paul Eggert @ 2024-02-09  7:36 UTC (permalink / raw)
  To: Bruno Haible, bug-gnulib

On 2024-02-08 03:50, Bruno Haible wrote:
> This patch provides a workaround, and thus removes the test-nstrftime-2.sh
> failure at test-nstrftime.h:421.

I was thinking of something a bit more general: temporarily set the time 
zone before calling the underlying strftime, and reset it afterwards. 
This of course won't work in a multithreaded system without some 
locking, but we can either add the locking or not bother.

time_rz.c already does this sort of thing, and I suppose it could export 
its secrets to nstrftime.c.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: nstrftime %c bug w.r.t. time zone
  2024-02-09  7:36   ` Paul Eggert
@ 2024-02-09 10:10     ` Bruno Haible
  0 siblings, 0 replies; 5+ messages in thread
From: Bruno Haible @ 2024-02-09 10:10 UTC (permalink / raw)
  To: bug-gnulib, Paul Eggert

Paul Eggert wrote:
> > This patch provides a workaround, and thus removes the test-nstrftime-2.sh
> > failure at test-nstrftime.h:421.
> 
> I was thinking of something a bit more general: temporarily set the time 
> zone before calling the underlying strftime, and reset it afterwards.
> This of course won't work in a multithreaded system ...

Yes, this is not MT-safe. For 20 years I've been training myself to avoid
thinking in a non-MT-safe way, and apparently now in this case it worked :-)

> time_rz.c already does this sort of thing, and I suppose it could export 
> its secrets to nstrftime.c.

Ouch ouch. No, instead of making more code *not* MT-safe, we should make more
code MT-safe!

Indeed, time_rz.c with its setenv_TZ function is not MT-safe, and with it
also the modules
  nstrftime
  c-nstrftime
  fprintftime
  parse-datetime
!!

This is a show-stopper, since we've been discussing nstrftime and c-nstrftime,
as replacements to recommend to developers, for three days. And of course we
cannot recommend functions that look MT-safe but are not.

Bruno





^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-02-09 10:11 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-08  0:04 nstrftime %c bug w.r.t. time zone Bruno Haible
2024-02-08 11:50 ` Bruno Haible
2024-02-08 13:55   ` Bruno Haible
2024-02-09  7:36   ` Paul Eggert
2024-02-09 10:10     ` Bruno Haible

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).