bug-cvs
[Top][All Lists]
Advanced

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

Patch for timezone handling in cvs log


From: Bart Robinson
Subject: Patch for timezone handling in cvs log
Date: Tue, 27 Apr 2004 16:53:56 -0700

OVERVIEW
--------

These changes add to log/rlog a -z option, which specifies the
timezone to use for output.  This is very similar to the -z option to
RCS's rlog command.  It works for remote repositories as well.

A special timezone "LT" is used to specify the local time at the
client.  When this is used, the client calculates the GMT offset and
passes this absolute value over to the server in a -z arg.  E.g.,
client specifies -zLT in PDT, server gets -z-0700.

Also included is a change to getdate.y to handle fractional timezones
such as IST (+0530).  This is allow -zIST, etc.  This is NOT required
for the -z option, users can always use the numeric form.

Example output looks like this (the -0700 part).

  ----------------------------
  revision 1.293
  date: 2004/04/12 12:30:00-0700;  author: bob;  state: Exp;  lines: +3 -2
  Fix for bug #1697. Listen for timezone change events and then update the
  time.
  ----------------------------
  revision 1.292
  date: 2004/04/09 11:40:51-0700;  author: lomew;  state: Exp;  lines: +1 -0
  Run with --version after packaging.  This helps to determine
  which run was used with a test.
  ----------------------------

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

ISSUES
------

1. The way "cvs history" handles the special LT timezone is
   inconsistent with how these changes do it for "cvs log".  cvs
   history uses it to mean local time at the server.  I found this to
   be a less useful interpretation of LT, so I went with LT meaning
   client time.  It is easy to fix "history" to do this but I didn't
   want to change current behavior.

2. Using the -z option for a remote repository requires server-side
   support or else an error is generated.  The error is useful and
   informative but I think a superior model would be for old servers
   to ignore the timezone-offsetting request.  That way clients could
   put the -zLT or whatever in their cvsrc and not have to worry about
   it.  One way to do this would be to put the timezone in some
   optional thing in the remote protocol.  I didn't persue this route
   since I wanted to get some feedback first.

3. RCS rlog formats the timezone offset somewhat differently.  I.e.,

        offset  RCS     CVS
        -0700   -07     -0700
        +0530   +05:30  +0530

   rlog also allows either -z+0530 or -z+05:30 as an argument.  I was
   going to adopt this format for some sort of consistency but found
   that CVS's getdate.y doesn't parse ones with a colon in the middle,
   and I wasn't comfortable "fixing" that.

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

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


###=====================================================================
### history.c: This was changed to use tz_offset_east.  Also I removed
### the tz_local variable, which was used to determine whether to call
### localtime or gmtime.  Instead, we always calculate the offset and
### use gmtime.  This way the code doesn't need to know if it is using
### localtime or not: it is either using GMT or some offset from that.
###
### As mentioned earlier, when the special LT timezone is used, it is
### passed to the server, resulting in localtime on the server.  The
### log command instead calculates the offset and passes that to the
### server, effecting localtime on the client.
###=====================================================================
Index: src/history.c
===================================================================
RCS file: /cvsroot/ccvs/src/history.c,v
retrieving revision 1.71
diff -u -p -r1.71 history.c
--- src/history.c       22 Mar 2004 15:37:34 -0000      1.71
+++ src/history.c       27 Apr 2004 23:21:15 -0000
@@ -232,7 +232,6 @@ static short repos_sort;
 static short file_sort;
 static short module_sort;
 
-static short tz_local;
 static time_t tz_seconds_east_of_GMT;
 static char *tz_name = "+0000";
 
@@ -487,35 +486,13 @@ history (int argc, char **argv)
                rec_types = xstrdup (optarg);
                break;
            case 'z':
-               tz_local = 
-                   (optarg[0] == 'l' || optarg[0] == 'L')
-                   && (optarg[1] == 't' || optarg[1] == 'T')
-                   && !optarg[2];
-               if (tz_local)
+               if (tz_offset_east (optarg, &tz_seconds_east_of_GMT) == 0)
+               {
                    tz_name = optarg;
+               }
                else
                {
-                   /*
-                    * Convert a known time with the given timezone to time_t.
-                    * Use the epoch + 23 hours, so timezones east of GMT work.
-                    */
-                   static char f[] = "1/1/1970 23:00 %s";
-                   char *buf = xmalloc (sizeof (f) - 2 + strlen (optarg));
-                   time_t t;
-                   sprintf (buf, f, optarg);
-                   t = get_date (buf, (struct timeb *) NULL);
-                   free (buf);
-                   if (t == (time_t) -1)
-                       error (0, 0, "%s is not a known time zone", optarg);
-                   else
-                   {
-                       /*
-                        * Convert to seconds east of GMT, removing the
-                        * 23-hour offset mentioned above.
-                        */
-                       tz_seconds_east_of_GMT = (time_t)23 * 60 * 60  -  t;
-                       tz_name = optarg;
-                   }
+                   error (0, 0, "%s is not a known time zone", optarg);
                }
                break;
            case '?':
@@ -1465,6 +1442,7 @@ report_hrecs (void)
     {
        char *workdir;
        char *repos;
+       time_t t;
 
        if (!hrec_count)
            hr = NULL;
@@ -1472,13 +1450,8 @@ report_hrecs (void)
            continue;
 
        ty = *(lr->type);
-       if (!tz_local)
-       {
-           time_t t = lr->date + tz_seconds_east_of_GMT;
-           tm = gmtime (&t);
-       }
-       else
-           tm = localtime (&(lr->date));
+       t = lr->date + tz_seconds_east_of_GMT;
+       tm = gmtime (&t);
 
        (void) printf ("%c %04d-%02d-%02d %02d:%02d %s %-*s", ty,
                  tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,

###=====================================================================
### log.c: Added the -z option.  If the special LT timezone is used
### for a remote repository, localtime is first determined on the
### client side and the offset is passed to the server.  This way LT
### means localtime at the client.
###=====================================================================
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   27 Apr 2004 23:21:15 -0000
@@ -143,6 +143,9 @@ static int version_compare (const char *
 static struct log_data log_data;
 static int is_rlog;
 
+static time_t tz_seconds_east_of_GMT;
+static char tz_name[] = "+0000";
+
 static const char *const log_usage[] =
 {
     "Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n",
@@ -168,6 +171,7 @@ static const char *const log_usage[] =
     "\t        \t(D1<D2 for range, D for latest before).\n",
     "\t-s states\tOnly list revisions with specified states.\n",
     "\t-w[logins]\tOnly list revisions checked in by specified logins.\n",
+    "\t-z[tz]\tOutput for specific time zone (e.g. -z-0700, -zLT, -zEDT)\n",
     "(Specify the --help global option for a list of other help options)\n",
     NULL
 };
@@ -228,7 +232,7 @@ cvslog (int argc, char **argv)
     prl = &log_data.revlist;
 
     optind = 0;
-    while ((c = getopt (argc, argv, "+bd:hlNSRr::s:tw::")) != -1)
+    while ((c = getopt (argc, argv, "+bd:hlNSRr::s:tw::z:")) != -1)
     {
        switch (c)
        {
@@ -269,6 +273,20 @@ cvslog (int argc, char **argv)
                else
                    log_parse_list (&log_data.authorlist, "@@MYSELF");
                break;
+           case 'z':
+               if (tz_offset_east (optarg, &tz_seconds_east_of_GMT) == 0)
+               {
+                   /* Convert to +0500, etc, form. */
+                   sprintf (tz_name, "%c%02d%02d",
+                            (tz_seconds_east_of_GMT < 0) ? '-' : '+',
+                            abs (tz_seconds_east_of_GMT) / (60*60),
+                            (abs (tz_seconds_east_of_GMT) % (60*60)) / 60);
+               }
+               else
+               {
+                   error (0, 0, "%s is not a known time zone", optarg);
+               }
+               break;
            case '?':
            default:
                usage (log_usage);
@@ -345,6 +363,7 @@ cvslog (int argc, char **argv)
            send_arg("-R");
        if (log_data.long_header)
            send_arg("-t");
+       option_with_arg("-z", tz_name);
 
        while (log_data.revlist != NULL)
        {
@@ -1572,8 +1591,11 @@ log_version (struct log_data *log_data, 
                  &sec);
     if (year < 1900)
        year += 1900;
-    sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday,
-            hour, min, sec);
+    if (tz_seconds_east_of_GMT)
+       adjust_time (tz_seconds_east_of_GMT, &year, &mon, &mday,
+                    &hour, &min, &sec);
+    sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d%s", year, mon, mday,
+            hour, min, sec, (tz_seconds_east_of_GMT ? tz_name : ""));
     cvs_output (buf, 0);
 
     cvs_output (";  author: ", 0);

###=====================================================================
### main.c: Support routines for adding to a y/m/d/h/m/s tuple and to
### calculate the offset east from GMT.  Seemed like a strange place
### for common routines, but there were already tm_to_internet and the
### like in there.  tz_offset_east was mostly taken from what
### history.c used to do.  The LT case is new and correctly handles
### daylight-savings time.
###=====================================================================
Index: src/main.c
===================================================================
RCS file: /cvsroot/ccvs/src/main.c,v
retrieving revision 1.207
diff -u -p -r1.207 main.c
--- src/main.c  24 Apr 2004 00:02:35 -0000      1.207
+++ src/main.c  27 Apr 2004 23:21:16 -0000
@@ -1187,6 +1187,83 @@ tm_to_internet (char *dest, const struct
             source->tm_year + 1900, source->tm_hour, source->tm_min, 
source->tm_sec);
 }
 
+/* Add some seconds to a year, mon, day, etc tuple.
+   Seconds can be negative. */
+void
+adjust_time (time_t off, int *year, int *mon, int *mday,
+            int *hour, int *min, int *sec)
+{
+    char buf[MAXDATELEN];
+    time_t t;
+
+    (void) sprintf (buf, "%d/%02d/%02d GMT %02d:%02d:%02d",
+                   *year, *mon, *mday, *hour, *min, *sec);
+    t = get_date (buf, (struct timeb *) NULL);
+    if (t != (time_t) -1)
+    {
+       struct tm *xtm;
+
+       t += off;
+       xtm = gmtime (&t);
+       *sec = xtm->tm_sec;
+       *min = xtm->tm_min;
+       *hour = xtm->tm_hour;
+       *mday = xtm->tm_mday;
+       *mon = xtm->tm_mon + 1;
+       *year = 1900 + xtm->tm_year;
+    }
+}
+
+/*
+ * Calculates how many seconds East a zone is from GMT.
+ * Positive for East, negative for West.
+ * Special value "LT" for local time.
+ * Returns non-zero if the TZNAME is invalid.
+ */
+int
+tz_offset_east (const char *tzname, time_t *offset)
+{
+    if (strcasecmp (tzname, "LT") == 0)
+    {
+       /*
+        * Compare the results from localtime and gmtime on the
+        * current time.  Have to copy since both return the same
+        * pointer.
+        */
+       time_t t = time (NULL);
+       struct tm ltm, gtm;
+       memcpy (&ltm, localtime (&t), sizeof (struct tm));
+       memcpy (&gtm, gmtime (&t), sizeof (struct tm));
+       *offset = ((ltm.tm_hour - gtm.tm_hour)*3600 +
+                  (ltm.tm_min  - gtm.tm_min)*60 +
+                  (ltm.tm_sec  - gtm.tm_sec));
+    }
+    else
+    {
+       /*
+        * Convert a known time with the given timezone to time_t.
+        * Use the epoch + 23 hours, so timezones east of GMT work.
+        */
+       static char f[] = "1/1/1970 23:00 %s";
+       char *buf = xmalloc (sizeof (f) - 2 + strlen (tzname));
+       time_t t;
+       sprintf (buf, f, tzname);
+       t = get_date (buf, (struct timeb *) NULL);
+       free (buf);
+       if (t == (time_t) -1)
+           return -1;
+       else
+       {
+           /*
+            * Convert to seconds east of GMT, removing the
+            * 23-hour offset mentioned above.
+            */
+           *offset = (time_t)23 * 60 * 60  -  t;
+       }
+    }
+    return 0;
+}
+
 void
 usage (register const char *const *cpp)
 {

###=====================================================================
### cvs.h: Add the prototypes for stuff we added to main.c.
###=====================================================================
Index: src/cvs.h
===================================================================
RCS file: /cvsroot/ccvs/src/cvs.h,v
retrieving revision 1.290
diff -u -p -r1.290 cvs.h
--- src/cvs.h   24 Apr 2004 00:02:35 -0000      1.290
+++ src/cvs.h   27 Apr 2004 23:21:15 -0000
@@ -430,6 +430,9 @@ 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 *);
+void adjust_time (time_t off, int *year, int *mon, int *mday,
+                 int *hour, int *min, int *sec); 
+int tz_offset_east (const char *tzname, time_t *offset);
 
 char *Name_Repository (const char *dir, const char *update_dir);
 const char *Short_Repository (const char *repository);


###=====================================================================
### getdate.y: This change allows getdate.y to handle symbolic names of
### fractional timezones such as IST being +0530.  There was a comment
### in the code about time_t not handling floats, but it was inaccurate
### since that would only be needed for fractional seconds.  All
### timezones in getdate.y are whole numbers of seconds.
###
### This patch is NOT required for the -z option -- users in those
### timezones can always specify the zone in numeric form.
###---------------------------------------------------------------------
Index: lib/getdate.y
===================================================================
RCS file: /cvsroot/ccvs/lib/getdate.y,v
retrieving revision 1.19
diff -u -p -r1.19 getdate.y
--- lib/getdate.y       9 Dec 2003 01:06:10 -0000       1.19
+++ lib/getdate.y       27 Apr 2004 23:21:15 -0000
@@ -85,7 +85,7 @@ static int yylex ();
 static int yyerror ();
 
 #define EPOCH          1970
-#define HOUR(x)                ((time_t)(x) * 60)
+#define HOUR(x)                ((time_t)((x) * 60))
 #define SECSPERDAY     (24L * 60L * 60L)
 
 
@@ -450,7 +450,6 @@ static TABLE const OtherTable[] = {
 };
 
 /* The timezone table. */
-/* Some of these are commented out because a time_t can't store a float. */
 static TABLE const TimezoneTable[] = {
     { "gmt",   tZONE,     HOUR( 0) },  /* Greenwich Mean */
     { "ut",    tZONE,     HOUR( 0) },  /* Universal (Coordinated) */
@@ -465,11 +464,9 @@ static TABLE const TimezoneTable[] = {
     { "bst",   tZONE,     HOUR( 3) },  /* Brazil Standard */
     { "gst",   tZONE,     HOUR( 3) },  /* Greenland Standard */
 #endif
-#if 0
     { "nft",   tZONE,     HOUR(3.5) }, /* Newfoundland */
     { "nst",   tZONE,     HOUR(3.5) }, /* Newfoundland Standard */
     { "ndt",   tDAYZONE,  HOUR(3.5) }, /* Newfoundland Daylight */
-#endif
     { "ast",   tZONE,     HOUR( 4) },  /* Atlantic Standard */
     { "adt",   tDAYZONE,  HOUR( 4) },  /* Atlantic Daylight */
     { "est",   tZONE,     HOUR( 5) },  /* Eastern Standard */
@@ -498,14 +495,10 @@ static TABLE const TimezoneTable[] = {
     { "fst",   tDAYZONE,  -HOUR(1) },  /* French Summer */
     { "eet",   tZONE,     -HOUR(2) },  /* Eastern Europe, USSR Zone 1 */
     { "bt",    tZONE,     -HOUR(3) },  /* Baghdad, USSR Zone 2 */
-#if 0
     { "it",    tZONE,     -HOUR(3.5) },/* Iran */
-#endif
     { "zp4",   tZONE,     -HOUR(4) },  /* USSR Zone 3 */
     { "zp5",   tZONE,     -HOUR(5) },  /* USSR Zone 4 */
-#if 0
     { "ist",   tZONE,     -HOUR(5.5) },/* Indian Standard */
-#endif
     { "zp6",   tZONE,     -HOUR(6) },  /* USSR Zone 5 */
 #if    0
     /* For completeness.  NST is also Newfoundland Stanard, and SST is
@@ -515,15 +508,11 @@ static TABLE const TimezoneTable[] = {
 #endif /* 0 */
     { "wast",  tZONE,     -HOUR(7) },  /* West Australian Standard */
     { "wadt",  tDAYZONE,  -HOUR(7) },  /* West Australian Daylight */
-#if 0
     { "jt",    tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
-#endif
     { "cct",   tZONE,     -HOUR(8) },  /* China Coast, USSR Zone 7 */
     { "jst",   tZONE,     -HOUR(9) },  /* Japan Standard, USSR Zone 8 */
-#if 0
     { "cast",  tZONE,     -HOUR(9.5) },/* Central Australian Standard */
     { "cadt",  tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
-#endif
     { "east",  tZONE,     -HOUR(10) }, /* Eastern Australian Standard */
     { "eadt",  tDAYZONE,  -HOUR(10) }, /* Eastern Australian Daylight */
     { "gst",   tZONE,     -HOUR(10) }, /* Guam Standard, USSR Zone 9 */




reply via email to

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