(Fencode_time, Fset_time_zone_rule): Use UTC if the zone is t.
[bpt/emacs.git] / src / editfns.c
index e405b06..88d8841 100644 (file)
@@ -267,9 +267,10 @@ save_excursion_save ()
 
 Lisp_Object
 save_excursion_restore (info)
-     register Lisp_Object info;
+     Lisp_Object info;
 {
-  register Lisp_Object tem, tem1, omark, nmark;
+  Lisp_Object tem, tem1, omark, nmark;
+  struct gcpro gcpro1, gcpro2, gcpro3;
 
   tem = Fmarker_buffer (Fcar (info));
   /* If buffer being returned to is now deleted, avoid error */
@@ -278,6 +279,10 @@ save_excursion_restore (info)
   /* In that case, Fmarker_buffer returns nil now.  */
   if (NILP (tem))
     return Qnil;
+
+  omark = nmark = Qnil;
+  GCPRO3 (info, omark, nmark);
+
   Fset_buffer (tem);
   tem = Fcar (info);
   Fgoto_char (tem);
@@ -313,6 +318,7 @@ save_excursion_restore (info)
       else if (! NILP (tem1))
        call1 (Vrun_hooks, intern ("deactivate-mark-hook"));
     }
+  UNGCPRO;
   return Qnil;
 }
 
@@ -601,8 +607,7 @@ FORMAT-STRING may contain %-sequences to substitute parts of the time.\n\
 %A is replaced by the full name of the day of week.\n\
 %b is replaced by the abbreviated name of the month.\n\
 %B is replaced by the full name of the month.\n\
-%c is a synonym for \"%x %X\".\n\
-%C is a locale-specific synonym, which defaults to \"%A, %B %e, %Y\" in the C locale.\n\
+%c stands for the preferred date/time format of the C locale.\n\
 %d is replaced by the day of month, zero-padded.\n\
 %D is a synonym for \"%m/%d/%y\".\n\
 %e is replaced by the day of month, blank-padded.\n\
@@ -688,7 +693,7 @@ ZONE is an integer indicating the number of seconds east of Greenwich.\n\
   XSETFASTINT (list_args[2], decoded_time->tm_hour);
   XSETFASTINT (list_args[3], decoded_time->tm_mday);
   XSETFASTINT (list_args[4], decoded_time->tm_mon + 1);
-  XSETFASTINT (list_args[5], decoded_time->tm_year + 1900);
+  XSETINT (list_args[5], decoded_time->tm_year + 1900);
   XSETFASTINT (list_args[6], decoded_time->tm_wday);
   list_args[7] = (decoded_time->tm_isdst)? Qt : Qnil;
 
@@ -702,36 +707,44 @@ ZONE is an integer indicating the number of seconds east of Greenwich.\n\
   return Flist (9, list_args);
 }
 
-DEFUN ("encode-time", Fencode_time, Sencode_time, 6, 7, 0,
+DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
   "Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time.\n\
-This is the reverse operation of `decode-time', which see.  ZONE defaults\n\
-to the current time zone rule if not specified; if specified, it can\n\
-be a string (as from `set-time-zone-rule'), or it can be a list\n\
+This is the reverse operation of `decode-time', which see.\n\
+ZONE defaults to the current time zone rule.  This can\n\
+be a string or t (as from `set-time-zone-rule'), or it can be a list\n\
 (as from `current-time-zone') or an integer (as from `decode-time')\n\
 applied without consideration for daylight savings time.\n\
+\n\
+You can pass more than 7 arguments; then the first six arguments\n\
+are used as SECOND through YEAR, and the *last* argument is used as ZONE.\n\
+The intervening arguments are ignored.\n\
+This feature lets (apply 'encode-time (decode-time ...)) work.\n\
+\n\
 Out-of-range values for SEC, MINUTE, HOUR, DAY, or MONTH are allowed;\n\
 for example, a DAY of 0 means the day preceding the given month.\n\
 Year numbers less than 100 are treated just like other year numbers.\n\
 If you want them to stand for years in this century, you must do that yourself.")
-  (second, minute, hour, day, month, year, zone)
-     Lisp_Object second, minute, hour, day, month, year, zone;
+  (nargs, args)
+     int nargs;
+     register Lisp_Object *args;
 {
   time_t time;
   struct tm tm;
-
-  CHECK_NUMBER (second, 0);
-  CHECK_NUMBER (minute, 1);
-  CHECK_NUMBER (hour, 2);
-  CHECK_NUMBER (day, 3);
-  CHECK_NUMBER (month, 4);
-  CHECK_NUMBER (year, 5);
-
-  tm.tm_sec = XINT (second);
-  tm.tm_min = XINT (minute);
-  tm.tm_hour = XINT (hour);
-  tm.tm_mday = XINT (day);
-  tm.tm_mon = XINT (month) - 1;
-  tm.tm_year = XINT (year) - 1900;
+  Lisp_Object zone = (nargs > 6)? args[nargs - 1] : Qnil;
+
+  CHECK_NUMBER (args[0], 0);   /* second */
+  CHECK_NUMBER (args[1], 1);   /* minute */
+  CHECK_NUMBER (args[2], 2);   /* hour */
+  CHECK_NUMBER (args[3], 3);   /* day */
+  CHECK_NUMBER (args[4], 4);   /* month */
+  CHECK_NUMBER (args[5], 5);   /* year */
+
+  tm.tm_sec = XINT (args[0]);
+  tm.tm_min = XINT (args[1]);
+  tm.tm_hour = XINT (args[2]);
+  tm.tm_mday = XINT (args[3]);
+  tm.tm_mon = XINT (args[4]) - 1;
+  tm.tm_year = XINT (args[5]) - 1900;
   tm.tm_isdst = -1;
 
   if (CONSP (zone))
@@ -744,7 +757,9 @@ If you want them to stand for years in this century, you must do that yourself."
       char *tzstring;
       char **oldenv = environ, **newenv;
       
-      if (STRINGP (zone))
+      if (zone == Qt)
+       tzstring = "UTC0";
+      else if (STRINGP (zone))
        tzstring = (char *) XSTRING (zone)->data;
       else if (INTEGERP (zone))
        {
@@ -752,6 +767,10 @@ If you want them to stand for years in this century, you must do that yourself."
          sprintf (tzbuf, "XXX%s%d:%02d:%02d", "-" + (XINT (zone) < 0),
                   abszone / (60*60), (abszone/60) % 60, abszone % 60);
          tzstring = tzbuf;
+#ifdef _NEXT_SOURCE
+         /* On NEXTSTEP, timezone environment var is ignored.  */
+         tm.tm_gmtoff = -abszone;
+#endif
        }
       else
        error ("Invalid time zone specification");
@@ -815,14 +834,17 @@ difftm (a, b)
 {
   int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
   int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+  /* Divide years by 100, rounding towards minus infinity.  */
+  int ac = ay / 100 - (ay % 100 < 0);
+  int bc = by / 100 - (by % 100 < 0);
   /* Some compilers can't handle this as a single return statement.  */
   long days = (
              /* difference in day of year */
              a->tm_yday - b->tm_yday
              /* + intervening leap days */
              +  ((ay >> 2) - (by >> 2))
-             -  (ay/100 - by/100)
-             +  ((ay/100 >> 2) - (by/100 >> 2))
+             -  (ac - bc)
+             +  ((ac >> 2) - (bc >> 2))
              /* + difference in years * 365 */
              +  (long)(ay-by) * 365
              );
@@ -894,7 +916,8 @@ static char **environbuf;
 
 DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0,
   "Set the local time zone using TZ, a string specifying a time zone rule.\n\
-If TZ is nil, use implementation-defined default time zone information.")
+If TZ is nil, use implementation-defined default time zone information.\n\
+If TZ is t, use Universal Time.")
   (tz)
      Lisp_Object tz;
 {
@@ -902,6 +925,8 @@ If TZ is nil, use implementation-defined default time zone information.")
 
   if (NILP (tz))
     tzstring = 0;
+  else if (tz == Qt)
+    tzstring = "UTC0";
   else
     {
       CHECK_STRING (tz, 0);
@@ -916,6 +941,17 @@ If TZ is nil, use implementation-defined default time zone information.")
   return Qnil;
 }
 
+/* These two values are known to load tz files in buggy implementations.
+   Their values shouldn't matter in non-buggy implementations.
+   We don't use string literals for these strings, 
+   since if a string in the environment is in readonly
+   storage, it runs afoul of bugs in SVR4 and Solaris 2.3.
+   See Sun bugs 1113095 and 1114114, ``Timezone routines
+   improperly modify environment''.  */
+
+static char set_time_zone_rule_tz1[] = "TZ=GMT0";
+static char set_time_zone_rule_tz2[] = "TZ=GMT1";
+
 /* Set the local time zone rule to TZSTRING.
    This allocates memory into `environ', which it is the caller's
    responsibility to free.  */
@@ -926,11 +962,14 @@ set_time_zone_rule (tzstring)
   int envptrs;
   char **from, **to, **newenv;
 
+  /* Make the ENVIRON vector longer with room for TZSTRING.  */
   for (from = environ; *from; from++)
     continue;
   envptrs = from - environ + 2;
   newenv = to = (char **) xmalloc (envptrs * sizeof (char *)
                                   + (tzstring ? strlen (tzstring) + 4 : 0));
+
+  /* Add TZSTRING to the end of environ, as a value for TZ.  */
   if (tzstring)
     {
       char *t = (char *) (to + envptrs);
@@ -939,6 +978,9 @@ set_time_zone_rule (tzstring)
       *to++ = t;
     }
 
+  /* Copy the old environ vector elements into NEWENV,
+     but don't copy the TZ variable.
+     So we have only one definition of TZ, which came from TZSTRING.  */
   for (from = environ; *from; from++)
     if (strncmp (*from, "TZ=", 3) != 0)
       *to++ = *from;
@@ -946,7 +988,45 @@ set_time_zone_rule (tzstring)
 
   environ = newenv;
 
+  /* If we do have a TZSTRING, NEWENV points to the vector slot where
+     the TZ variable is stored.  If we do not have a TZSTRING,
+     TO points to the vector slot which has the terminating null.  */
+
 #ifdef LOCALTIME_CACHE
+  {
+    /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like
+       "US/Pacific" that loads a tz file, then changes to a value like
+       "XXX0" that does not load a tz file, and then changes back to
+       its original value, the last change is (incorrectly) ignored.
+       Also, if TZ changes twice in succession to values that do
+       not load a tz file, tzset can dump core (see Sun bug#1225179).
+       The following code works around these bugs.  */
+
+    if (tzstring)
+      {
+       /* Temporarily set TZ to a value that loads a tz file
+          and that differs from tzstring.  */
+       char *tz = *newenv;
+       *newenv = (strcmp (tzstring, set_time_zone_rule_tz1 + 3) == 0
+                  ? set_time_zone_rule_tz2 : set_time_zone_rule_tz1);
+       tzset ();
+       *newenv = tz;
+      }
+    else
+      {
+       /* The implied tzstring is unknown, so temporarily set TZ to
+          two different values that each load a tz file.  */
+       *to = set_time_zone_rule_tz1;
+       to[1] = 0;
+       tzset ();
+       *to = set_time_zone_rule_tz2;
+       tzset ();
+       *to = 0;
+      }
+
+    /* Now TZ has the desired value, and tzset can be invoked safely.  */
+  }
+
   tzset ();
 #endif
 }
@@ -1383,7 +1463,7 @@ determines whether case is significant or ignored.")
       buf2 = Fget_buffer (buffer2);
       if (NILP (buf2))
        nsberror (buffer2);
-      bp2 = XBUFFER (buffer2);
+      bp2 = XBUFFER (buf2);
     }
 
   if (NILP (start2))