Hi,
I'm sending this patch to -beta and not -patches because I wanted some
help/guidance on its desirability and correctness.
I've noted that the format-time-string function does not support a %z
control to generate an offset from UTC (e.g. "-0800") that is provided
by many systems (strftime in glibc, BSD, OSF1, also Emacs). It's not
ISO C, but it is now quite common feature. It's very nice when
producing RFC822 time stamps.
Anyway, since this is my first attempt at a patch for any Emacs I
found myself questioning the patch and I'd appreciate any feedback (if
only to learn).
First: is this something that would be useful and considered for
inclusion (if only to improve Emacs compatibility)?
Second: I use tm_gmtoff (in struct tm) when HAVE_TM_ZONE is
defined. Perhaps this is not correct, but tm_gmtoff and tm_zone go
hand in hand as far as I know (they are both BSDisms that are never
implemented one without the other AFAIK). Should I be defining an
autoconf macro to detect HAVE_TM_GMTOFF specifically, or is the patch
okay as is (Autoconf 2.13 has no builtin to check for tm_gmtoff, just
one for tm_zone)?
Third: If I need to do a new macro, do I need to submit a patched
configure script too, or just the source .in files?
Fourth: editfns.c has a difftm() function which is just what I needed
for systems without tm_gmtoff. I discovered it after I adapted the
diff code in the patch below (copied from glibc). But that difftm has
static scope so I can't use it. Should I be making it global and using
it instead?
Fifth: editfns.c uses tm_zone quite a bit, but never tm_gmtoff. It
makes me wonder if I'm missing something huge. I suspect if tm_gmtoff
was used on systems that had it then the static difftm() might even go
away. Is this just an oversight?
Anything else? I'm not sure what XEmacs coding conventions are (I've
read the internal's document but thats it).
Sorry about the long newbie mail....
Cheers!
Shyamal
src/ChangeLog addition:
2004-11-28 Shyamal Prasad <shyamal(a)member.fsf.org>
* strftime.c (strftime): Added support for %z format (numeric
offset in hours east of UTC as in RFC822 headers).
* editfns.c (Fformat_time_string): Make copy of structure returned
by localtime() so it does not get trashed by anything in
emacs_strftime. Document %z format code.
XEmacs source patch:
Diff command: cvs -q diff -u
Files affected: src/strftime.c src/editfns.c
Index: src/editfns.c
===================================================================
RCS file: /pack/xemacscvs/XEmacs/xemacs/src/editfns.c,v
retrieving revision 1.50
diff -u -r1.50 editfns.c
--- src/editfns.c 2004/11/04 23:06:23 1.50
+++ src/editfns.c 2004/11/29 01:19:47
@@ -1039,6 +1039,7 @@
%X is a locale-specific synonym, which defaults to "%T" in the C locale.
%y is replaced by the year without century (00-99).
%Y is replaced by the year with century.
+%z is replaced by the time zone as a numeric offset (e.g +0530, -0800 etc.)
%Z is replaced by the time zone abbreviation.
The number of options reflects the `strftime' function.
@@ -1063,13 +1064,15 @@
{
Extbyte *buf = alloca_extbytes (size);
Extbyte *formext;
+ /* make a copy of the static buffer returned by localtime() */
+ struct tm tm = * localtime(&value);
+
*buf = 1;
/* !!#### this use of external here is not totally safe, and
potentially data lossy. */
LISP_STRING_TO_EXTERNAL (format_string, formext, Qnative);
- if (emacs_strftime (buf, size, formext,
- localtime (&value))
+ if (emacs_strftime (buf, size, formext, &tm)
|| !*buf)
return build_ext_string (buf, Qnative);
/* If buffer was too small, make it bigger. */
Index: src/strftime.c
===================================================================
RCS file: /pack/xemacscvs/XEmacs/xemacs/src/strftime.c,v
retrieving revision 1.6
diff -u -r1.6 strftime.c
--- src/strftime.c 2002/03/29 04:48:37 1.6
+++ src/strftime.c 2004/11/29 01:19:47
@@ -58,6 +58,7 @@
%S second (00..61)
%T time, 24-hour (hh:mm:ss)
%X locale's time representation (%H:%M:%S)
+ %z time zone offset (e.g. +0530, -0800 etc)
%Z time zone (EDT), or nothing if no time zone is determinable
Date fields:
@@ -366,6 +367,67 @@
length +=
strftime (&string[length], max - length, "%H:%M:%S", tm);
break;
+ case 'z':
+ {
+ long int offset;
+ long int minutes;
+
+ /* ####FIXME: really we should define HAVE_TM_GMTOFF
+ But tm_gmtoff and tm_zone are both BSD extensions
+ that go hand in hand, so for now I'll get by
+ without updating the autconf files */
+
+#ifdef HAVE_TM_ZONE
+ offset = tm->tm_gmtoff;
+#else
+ {
+ /* we have to get the offset from UTC the hard way */
+ int lt4, ut4, lt100, ut100, lt400, ut400;
+ int intervening_leap_days, years, days;
+ struct tm lt, *ut;
+ time_t utc;
+
+ lt = *tm;
+ utc = mktime(<);
+ ut = gmtime(&utc);
+ /* if we trust that tm is valid, then utc is valid,
+ and so is ut */
+ assert( utc != (time_t) -1 && ut != NULL );
+
+ /* Compute intervening leap days correctly even if
+ year is negative. Take care to avoid int
+ overflow in leap day calculations, but it's OK
+ to assume that A and B are close to each other.
+
+ (This code is based on mktime.c in glibc 2.3.2)
+ */
+ lt4 = (lt.tm_year >> 2) + (1900 >> 2) -
+ ! (lt.tm_year & 3);
+ ut4 = (ut->tm_year >> 2) + (1900 >> 2) -
+ ! (ut->tm_year & 3);
+ lt100 = lt4 / 25 - (lt4 % 25 < 0);
+ ut100 = ut4 / 25 - (ut4 % 25 < 0);
+ lt400 = lt100 >> 2;
+ ut400 = ut100 >> 2;
+ intervening_leap_days =
+ (lt4 - ut4) - (lt100 - ut100) + (lt400 - ut400);
+ years = lt.tm_year - ut->tm_year;
+ days = (365 * years + intervening_leap_days
+ + (lt.tm_yday - ut->tm_yday));
+ offset =(60 * (60 * (24 * days + (lt.tm_hour - ut->tm_hour))
+ + (lt.tm_min - ut->tm_min))
+ + (lt.tm_sec - ut->tm_sec));
+ }
+#endif
+ minutes = offset / ( offset < 0 ? -60 : 60 );
+
+ add_char((offset < 0 ? '-' : '+'));
+ length +=
+ add_num2 (&string[length], minutes / 60, max - length, zero);
+ length +=
+ add_num2 (&string[length], minutes % 60, max - length, zero);
+ break;
+ }
case 'Z':
#ifdef HAVE_TM_ZONE
length += add_str (&string[length], tm->tm_zone, max - length);