bug-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Patch for timezone handling in cvs log


From: Bart Robinson
Subject: Re: Patch for timezone handling in cvs log
Date: Fri, 30 Apr 2004 15:04:34 -0700 (PDT)

Here is a simpler patch incorporating Derek's ideas with getting
rid of the -z and omitting the gratuitous MT stuff in log.c.

OVERVIEW
--------

These changes make log/rlog output dates in local time by
default.  If the user wants GMT or any other timezone supported
by their system, they can modify their TZ env var accordingly.

For example:
        $ TZ=EST cvs log foo

The way this works is that the server side sends the date in the
rlog output as an MT response, tagged with "date".  An aware
client notices these and does the conversion if necessary.
Unaware clients just print them as-is (in GMT).  Local
repositories are handled correctly as well.

Example output looks like this (the -0700 part).  Notice that
the DST change on Apr 4 is correctly accounted for.

  ----------------------------
  revision 1.291
  date: 2004/04/05 10:52:16-0700;  author: lomew;  state: Exp;  lines: +2 -1
  ...
  ----------------------------
  revision 1.290
  date: 2004/04/01 15:48:00-0800;  author: lomew;  state: Exp;  lines: +3 -3
  ...
  ----------------------------

If TZ=GMT, or equivalent, i.e., the offset from GMT is zero, no
offset is printed and it looks like current CVS:

  ----------------------------
  date: 2004/04/05 17:52:16;  author: lomew;  state: Exp;  lines: +2 -1
  ----------------------------

I will gladly update docs, ChangeLogs, etc if this approved.

ISSUES
------

1. I didn't change "cvs history" to handle dates in this
   manner.  It has a -z argument that can achieve similar
   results, but should eventually be updated.  Some other
   commands could benefit from time conversion, like stat and
   diff.

PATCH NOTES
-----------

Each patch is preceded by a verbal summary and explanation.
The patches are against the HEAD of ccvs as of today (Apr 30, 2004)

-- bart


###=====================================================================
### log.c: Dates in the version log are tagged with "date" to be
### handled on the other side in handle_mt.  Had to do extensive
### s/cvs_output/cvs_output_tagged/ since you can't just tag
### something in the middle of a line.  So basically, the second
### line in a version log is with MT, and the rest are done
### with normal M.
###=====================================================================
Index: src/log.c
===================================================================
RCS file: /cvsroot/ccvs/src/log.c,v
retrieving revision 1.94
diff -u -p -r1.94 log.c
--- src/log.c   2 Apr 2004 19:56:22 -0000       1.94
+++ src/log.c   30 Apr 2004 21:21:27 -0000
@@ -1566,22 +1566,23 @@ log_version (struct log_data *log_data, 
        cvs_output (p->data, 0);
        cvs_output (";", 1);
     }
+    cvs_output ("\n", 1);
 
-    cvs_output ("\ndate: ", 0);
+    cvs_output_tagged ("text", "date: ");
     (void)sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min,
                  &sec);
     if (year < 1900)
        year += 1900;
     sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday,
             hour, min, sec);
-    cvs_output (buf, 0);
+    cvs_output_tagged ("date", buf);
 
-    cvs_output (";  author: ", 0);
-    cvs_output (ver->author, 0);
+    cvs_output_tagged ("text", ";  author: ");
+    cvs_output_tagged ("text", ver->author);
 
-    cvs_output (";  state: ", 0);
-    cvs_output (ver->state, 0);
-    cvs_output (";", 1);
+    cvs_output_tagged ("text", ";  state: ");
+    cvs_output_tagged ("text", ver->state);
+    cvs_output_tagged ("text", ";");
 
     if (! trunk)
     {
@@ -1609,20 +1610,20 @@ log_version (struct log_data *log_data, 
 
     if (padd != NULL)
     {
-       cvs_output ("  lines: +", 0);
-       cvs_output (padd->data, 0);
-       cvs_output (" -", 2);
-       cvs_output (pdel->data, 0);
+       cvs_output_tagged ("text", "  lines: +");
+       cvs_output_tagged ("text", padd->data);
+       cvs_output_tagged ("text", " -");
+       cvs_output_tagged ("text", pdel->data);
     }
+    cvs_output_tagged ("newline", NULL);
 
     if (ver->branches != NULL)
     {
-       cvs_output ("\nbranches:", 0);
+       cvs_output ("branches:", 0);
        walklist (ver->branches, log_branch, NULL);
+       cvs_output ("\n", 1);
     }
 
-    cvs_output ("\n", 1);
-
     p = findnode (ver->other, "log");
     /* The p->date == NULL case is the normal one for an empty log
        message (rcs-14 in sanity.sh).  I don't think the case where

###=====================================================================
### main.c: Support routines.  Seemed like a strange place
### for common routines, but there were already tm_to_internet and the
### like in there.
###
### tm_diff is actually in getdate.y but is static, so I put
### another copy in there...
###
### format_date_alloc is the common routine used in the
### client/server for the "date" tag.
###=====================================================================
Index: src/main.c
===================================================================
RCS file: /cvsroot/ccvs/src/main.c,v
retrieving revision 1.208
diff -u -p -r1.208 main.c
--- src/main.c  28 Apr 2004 04:20:33 -0000      1.208
+++ src/main.c  30 Apr 2004 22:00:29 -0000
@@ -1190,6 +1190,88 @@ tm_to_internet (char *dest, const struct
             source->tm_year + 1900, source->tm_hour, source->tm_min, 
source->tm_sec);
 }
 
+/* Yield the difference between *A and *B,
+   measured in seconds, ignoring leap seconds.
+   The body of this function is taken directly from the GNU C Library;
+   see src/strftime.c.  */
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+#endif
+long int
+tm_diff (struct tm const *a, struct tm const *b)
+{
+  /* Compute intervening leap days correctly even if year is negative.
+     Take care to avoid int overflow in leap day calculations.  */
+  int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
+  int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
+  int a100 = a4 / 25 - (a4 % 25 < 0);
+  int b100 = b4 / 25 - (b4 % 25 < 0);
+  int a400 = a100 >> 2;
+  int b400 = b100 >> 2;
+  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+  long int ayear = a->tm_year;
+  long int years = ayear - b->tm_year;
+  long int days = (365 * years + intervening_leap_days
+                  + (a->tm_yday - b->tm_yday));
+  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+               + (a->tm_min - b->tm_min))
+         + (a->tm_sec - b->tm_sec));
+}
+
+/*
+ * Format a date for the current locale.
+ *
+ * Input looks like
+ *     2004/04/29 20:24:22
+ * Output looks the same (for GMT) or with +/-HHMM added to the end, like
+ *     2004/04/29 13:24:22-0700
+ */
+char *
+format_date_alloc (const char *datestr)
+{
+    struct timespec ts;
+    long gmtoff;
+    struct tm ltm, gtm;
+    char *buf;
+    size_t len = 0;
+
+    /*
+     * Get the date into a timespec so we can create struct tm's from it.
+     */
+    buf = asnprintf (NULL, &len, "%s GMT", datestr);
+    if (! get_date (&ts, buf, NULL))
+    {
+       free (buf);
+       goto as_is;
+    }
+    free (buf);
+    len = 0;
+
+    /*
+     * Convert to localtime and calculate the offset.
+     * The offset is used for printing the -HHMM part.
+     */
+    ltm = *(localtime (&ts.tv_sec));
+    gtm = *(gmtime (&ts.tv_sec));
+    gmtoff = tm_diff (&ltm, &gtm);
+
+    /*
+     * Format it and return.
+     */
+    if (gmtoff == 0)
+       goto as_is;                     /* don't add +0000 */
+    buf = asnprintf (NULL, &len, "%04d/%02d/%02d %02d:%02d:%02d%c%02d%02d",
+                    1900 + ltm.tm_year, ltm.tm_mon + 1, ltm.tm_mday,
+                    ltm.tm_hour, ltm.tm_min, ltm.tm_sec,
+                    (gmtoff < 0) ? '-' : '+',
+                    abs (gmtoff) / (60*60),
+                    (abs (gmtoff) % (60*60)) / 60);
+    return buf;
+
+ as_is:
+    return strdup (datestr);
+}
+
 void
 usage (register const char *const *cpp)
 {

###=====================================================================
### cvs.h: Protos
###=====================================================================
Index: src/cvs.h
===================================================================
RCS file: /cvsroot/ccvs/src/cvs.h,v
retrieving revision 1.291
diff -u -p -r1.291 cvs.h
--- src/cvs.h   28 Apr 2004 04:20:33 -0000      1.291
+++ src/cvs.h   30 Apr 2004 21:21:27 -0000
@@ -431,6 +431,8 @@ char *date_from_time_t (time_t);
 void date_to_internet (char *, const char *);
 void date_to_tm (struct tm *, const char *);
 void tm_to_internet (char *, const struct tm *);
+long int tm_diff (struct tm const *a, struct tm const *b);
+char *format_date_alloc (const char *text);
 
 char *Name_Repository (const char *dir, const char *update_dir);
 const char *Short_Repository (const char *repository);

###=====================================================================
### client.c: Fix handle_mt to handle the "date" tag.
###=====================================================================
Index: src/client.c
===================================================================
RCS file: /cvsroot/ccvs/src/client.c,v
retrieving revision 1.371
diff -u -p -r1.371 client.c
--- src/client.c        28 Apr 2004 04:20:33 -0000      1.371
+++ src/client.c        30 Apr 2004 21:21:26 -0000
@@ -2735,6 +2735,12 @@ handle_mt( char *args, int len )
            }
            else if (strcmp (tag, "newline") == 0)
                printf ("\n");
+           else if (strcmp (tag, "date") == 0)
+           {
+               char *date = format_date_alloc (text);
+               printf ("%s", date);
+               free (date);
+           }
            else if (text != NULL)
                printf ("%s", text);
     }

###=====================================================================
### server.c: Added code to handle the "date" tag in the event
### that MT is not supported or the repository is local.  Very
### similar to how "newline" is implemented.  We call cvs_output
### since that will either do printfs or convert the output to M
### commands.
###=====================================================================
Index: src/server.c
===================================================================
RCS file: /cvsroot/ccvs/src/server.c,v
retrieving revision 1.357
diff -u -p -r1.357 server.c
--- src/server.c        28 Apr 2004 04:20:33 -0000      1.357
+++ src/server.c        30 Apr 2004 21:21:28 -0000
@@ -6587,8 +6587,15 @@ cvs_output_tagged (const char *tag, cons
     else
 #endif
     {
+       /* No MT support or we are using a local repository. */
        if (strcmp (tag, "newline") == 0)
            cvs_output ("\n", 1);
+       else if (strcmp (tag, "date") == 0)
+       {
+           char *date = format_date_alloc (text);
+           cvs_output (date, 0);
+           free (date);
+       }
        else if (text != NULL)
            cvs_output (text, 0);
     }
###=====================================================================
### TODO: One down...
###=====================================================================
Index: TODO
===================================================================
RCS file: /cvsroot/ccvs/TODO,v
retrieving revision 1.80
diff -u -p -r1.80 TODO
--- TODO        7 Apr 2004 00:54:52 -0000       1.80
+++ TODO        30 Apr 2004 21:39:30 -0000
@@ -855,19 +855,6 @@ administrative file interfaces do not.)
 of universal character set (ISO 10646?) internally and converting on
 input and output, which opens the locale can of worms.
 
-224.  Better timezone handling.  Many people would like to see times
-output in local time rather than UTC, but that's tricky since the
-conversion from internal form is currently done by the server who has no
-idea what the user's timezone even is, let alone the rules for
-converting to it.
-
-   -  On the contrary, I think the MT server response should be easily 
adaptable
-for this purpose.  It is defined in cvsclient.texi as processed by the client
-if it knows how and printed to stdout otherwise.  A "time" tag or the like
-could be the usual CVS server UTC time string.  An old client could just print
-the time in UTC and a new client would know that it could convert the time to a
-local time string according to the localization settings before printing it.
-
 225.  Add support for --allow-root to server command.
 
 227.  'cvs release' should use the CVS/Root in the directory being released




reply via email to

[Prev in Thread] Current Thread [Next in Thread]