This is the mail archive of the libc-alpha@cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

glibc strftime (gmtime) enhancement request for GNU Emacs


GNU Emacs 20.3's front-end to strftime it mishandles time zones when
requested to generate Universal Time.  For example, on my Solaris 2.6
host in Los Angeles, (format-time-string "%Z %z %c" '(0 0) t) returns
"PST -0800 Thu Jan 01 00:00:00 1970"; it should return
"GMT +0000 Thu Jan 01 00:00:00 1970".

I tracked this problem down to a place where GNU Emacs passes the
output of gmtime to strftime.  There is an inadequacy in strftime on
hosts like Solaris 2.6 and GNU/Linux where struct tm does not contain
a tm_gmtoff or tm_zone member.  On such hosts, there's no way for
strftime to tell whether its argument is the output of gmtime or
localtime; it guesses localtime, but this is not always what the user
intends, and GNU Emacs runs afoul of this problem.

C9x will probably fix this, but its time functions seem to be
undergoing drastic revision and I'd rather fix the problem now rather
than wait for the C9x dust to settle and then convert Emacs to the C9x
equivalent of strftime.

So, here's a proposed small change to glibc strftime.c that adds a new
function strftimeu, which acts just like strftime except it has an
extra flag specifying whether universal time or local time is
intended.  This new interface is visible only to Emacs.  Once this
interface is added to glibc, I can patch Emacs to use it.  The
proposed change does not affect the behavior of strftime, so unpatched
Emacs (and other applications) continue to work as before.

1998-09-06  Paul Eggert  <eggert@twinsun.com>

	Add a new strftimeu interface, visible only to Emacs, that allows
	callers to tell strftime that the argument is gmtime's output.

	* strftime.c (my_strftimeu): New macro.
	(_strftimeu_copytm): Renamed from _strftime_copytm.  Pass extra ut arg.
	(my_strftimeu): Renamed from my_strftime.  Accept extra ut arg.
	(mystrftime): New function.

===================================================================
RCS file: strftime.c,v
retrieving revision 20.3.0.1
retrieving revision 20.3.0.2
diff -u -r20.3.0.1 -r20.3.0.2
--- strftime.c	1998/09/03 00:08:32	20.3.0.1
+++ strftime.c	1998/09/07 06:28:58	20.3.0.2
@@ -376,45 +376,57 @@
 
 
 #ifdef emacs
+# define my_strftimeu emacs_strftimeu
 # define my_strftime emacs_strftime
 #else
+# define my_strftimeu strftimeu
 # define my_strftime strftime
 #endif
 
 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
   /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
      Work around this bug by copying *tp before it might be munged.  */
-  size_t _strftime_copytm __P ((char *, size_t, const char *,
-			        const struct tm *));
+  size_t _strftimeu_copytm __P ((char *, size_t, const char *,
+				 const struct tm *, int));
   size_t
-  my_strftime (s, maxsize, format, tp)
+  my_strftimeu (s, maxsize, format, tp, ut)
       char *s;
       size_t maxsize;
       const char *format;
       const struct tm *tp;
+      int ut;
   {
     struct tm tmcopy;
     tmcopy = *tp;
-    return _strftime_copytm (s, maxsize, format, &tmcopy);
+    return _strftimeu_copytm (s, maxsize, format, &tmcopy, ut);
   }
-# undef my_strftime
-# define my_strftime(S, Maxsize, Format, Tp) \
-  _strftime_copytm (S, Maxsize, Format, Tp)
+# undef my_strftimeu
+# define my_strftimeu _strftimeu_copytm
 #endif
 
 
-/* Write information from TP into S according to the format
-   string FORMAT, writing no more that MAXSIZE characters
-   (including the terminating '\0') and returning number of
-   characters written.  If S is NULL, nothing will be written
-   anywhere, so to determine how many characters would be
-   written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
+/* Write information into buffer S of size MAXSIZE, according to the
+   FORMAT, using time information taken from *TP, defaulting to
+   universal time if UT is nonzero, local time otherwise.
+   Return the number of bytes written, not including the terminating
+   '\0'.  If S is NULL, nothing will be written anywhere; so to
+   determine how many bytes would be written, use NULL for S and
+   ((size_t) -1) for MAXSIZE.  */
+#ifndef emacs
+static
+#endif
+size_t my_strftimeu __P ((char *, size_t, const char *, const struct tm *,
+			  int));
+#ifndef emacs
+static
+#endif
 size_t
-my_strftime (s, maxsize, format, tp)
+my_strftimeu (s, maxsize, format, tp, ut)
       char *s;
       size_t maxsize;
       const char *format;
       const struct tm *tp;
+      int ut;
 {
   int hour12 = tp->tm_hour;
 #ifdef _NL_CURRENT
@@ -460,15 +472,23 @@
   zone = (const char *) tp->tm_zone;
 #endif
 #if HAVE_TZNAME
-  /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
-     time zone names contained in the external variable `tzname' shall
-     be set as if the tzset() function had been called.  */
+  if (ut)
+    {
+      if (! (zone && *zone))
+	zone = "GMT";
+    }
+  else
+    {
+      /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
+	 time zone names contained in the external variable `tzname' shall
+	 be set as if the tzset() function had been called.  */
 # if HAVE_TZSET
-  tzset ();
+      tzset ();
 # endif
 
-  if (!(zone && *zone) && tp->tm_isdst >= 0)
-    zone = tzname[tp->tm_isdst];
+      if (!(zone && *zone) && tp->tm_isdst >= 0)
+	zone = tzname[tp->tm_isdst];
+    }
 #endif
   if (! zone)
     zone = "";		/* POSIX.2 requires the empty string here.  */
@@ -721,10 +741,10 @@
 	subformat:
 	  {
 	    char *old_start = p;
-	    size_t len = my_strftime (NULL, maxsize - i, subfmt, tp);
+	    size_t len = my_strftimeu (NULL, maxsize - i, subfmt, tp, ut);
 	    if (len == 0 && *subfmt)
 	      return 0;
-	    add (len, my_strftime (p, maxsize - i, subfmt, tp));
+	    add (len, my_strftimeu (p, maxsize - i, subfmt, tp, ut));
 
 	    if (to_uppcase)
 	      while (old_start < p)
@@ -1160,34 +1180,39 @@
 #if HAVE_TM_GMTOFF
 	    diff = tp->tm_gmtoff;
 #else
-	    struct tm gtm;
-	    struct tm ltm;
-	    time_t lt;
+	    if (ut)
+	      diff = 0;
+	    else
+	      {
+		struct tm gtm;
+		struct tm ltm;
+		time_t lt;
 
-	    ltm = *tp;
-	    lt = mktime (&ltm);
+		ltm = *tp;
+		lt = mktime (&ltm);
 
-	    if (lt == (time_t) -1)
-	      {
-		/* mktime returns -1 for errors, but -1 is also a
-		   valid time_t value.  Check whether an error really
-		   occurred.  */
-		struct tm tm;
-
-		if (! localtime_r (&lt, &tm)
-		    || ((ltm.tm_sec ^ tm.tm_sec)
-			| (ltm.tm_min ^ tm.tm_min)
-			| (ltm.tm_hour ^ tm.tm_hour)
-			| (ltm.tm_mday ^ tm.tm_mday)
-			| (ltm.tm_mon ^ tm.tm_mon)
-			| (ltm.tm_year ^ tm.tm_year)))
-		  break;
-	      }
+		if (lt == (time_t) -1)
+		  {
+		    /* mktime returns -1 for errors, but -1 is also a
+		       valid time_t value.  Check whether an error really
+		       occurred.  */
+		    struct tm tm;
+
+		    if (! localtime_r (&lt, &tm)
+			|| ((ltm.tm_sec ^ tm.tm_sec)
+			    | (ltm.tm_min ^ tm.tm_min)
+			    | (ltm.tm_hour ^ tm.tm_hour)
+			    | (ltm.tm_mday ^ tm.tm_mday)
+			    | (ltm.tm_mon ^ tm.tm_mon)
+			    | (ltm.tm_year ^ tm.tm_year)))
+		      break;
+		  }
 
-	    if (! gmtime_r (&lt, &gtm))
-	      break;
+		if (! gmtime_r (&lt, &gtm))
+		  break;
 
-	    diff = tm_diff (&ltm, &gtm);
+		diff = tm_diff (&ltm, &gtm);
+	      }
 #endif
 
 	    if (diff < 0)
@@ -1224,3 +1249,19 @@
     *p = '\0';
   return i;
 }
+
+/* Write information into buffer S of size MAXSIZE, according to the
+   FORMAT, using time information taken from *TP, defaulting to local
+   time.  Return the number of bytes written, not including the
+   terminating '\0'.  If S is NULL, nothing will be written anywhere;
+   so to determine how many bytes would be written, use NULL for S and
+   ((size_t) -1) for MAXSIZE.  */
+size_t
+my_strftime (s, maxsize, format, tp)
+      char *s;
+      size_t maxsize;
+      const char *format;
+      const struct tm *tp;
+{
+  return my_strftimeu (s, maxsize, format, tp, 0);
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]