aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert1995-09-10 19:38:19 +0000
committerPaul Eggert1995-09-10 19:38:19 +0000
commitc59b5089765fbe387b3d30014c4aefa64952fdc9 (patch)
tree9c38fb13c211871a1d4f7cee4a50aa1d0f51573f /src
parent6bbb008e7ed360c4ecbe5854a03ae246a08a93a3 (diff)
downloademacs-c59b5089765fbe387b3d30014c4aefa64952fdc9.tar.gz
emacs-c59b5089765fbe387b3d30014c4aefa64952fdc9.zip
(Fencode_time): Use mktime to do the real work;
this fixes bugs involving out-of-range dates and leap seconds, and allows date arithmetic via out-of-range values for arguments. Allow the ZONE parameter to be a TZ-style string. Doc string fix: `1900' -> `this century'. (set_time_zone_rule): New function. (Fset_time_zone_rule): Use it. (environ, make_time): Add extern declarations. (days_per_month): Remove.
Diffstat (limited to 'src')
-rw-r--r--src/editfns.c170
1 files changed, 80 insertions, 90 deletions
diff --git a/src/editfns.c b/src/editfns.c
index 778646be5a1..ccfc4a860b1 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -38,8 +38,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
38#define min(a, b) ((a) < (b) ? (a) : (b)) 38#define min(a, b) ((a) < (b) ? (a) : (b))
39#define max(a, b) ((a) > (b) ? (a) : (b)) 39#define max(a, b) ((a) > (b) ? (a) : (b))
40 40
41extern char **environ;
42extern Lisp_Object make_time ();
41extern void insert_from_buffer (); 43extern void insert_from_buffer ();
42static long difftm (); 44static long difftm ();
45static void set_time_zone_rule ();
43 46
44/* Some static data, and a function to initialize it for each run */ 47/* Some static data, and a function to initialize it for each run */
45 48
@@ -691,102 +694,79 @@ ZONE is an integer indicating the number of seconds east of Greenwich.\n\
691 return Flist (9, list_args); 694 return Flist (9, list_args);
692} 695}
693 696
694static char days_per_month[11]
695 = { 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 };
696
697DEFUN ("encode-time", Fencode_time, Sencode_time, 6, 7, 0, 697DEFUN ("encode-time", Fencode_time, Sencode_time, 6, 7, 0,
698 "Convert SEC, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time.\n\ 698 "Convert SEC, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time.\n\
699This is the reverse operation of `decode-time', which see. ZONE defaults\n\ 699This is the reverse operation of `decode-time', which see. ZONE defaults\n\
700to the current time zone and daylight savings time if not specified; if\n\ 700to the current time zone rule if not specified; if specified, it can\n\
701specified, it can be either a list (as from `current-time-zone') or an\n\ 701be a string (as from `set-time-zone-rule'), or it can be a list\n\
702integer (as from `decode-time'), and is applied without consideration for\n\ 702(as from `current-time-zone') or an integer (as from `decode-time')\n\
703daylight savings time.\n\ 703applied without consideration for daylight savings time.\n\
704Out-of-range values for SEC, MINUTE, HOUR, DAY, or MONTH are allowed;\n\
705for example, a DAY of 0 means the day preceding the given month.\n\
704Year numbers less than 100 are treated just like other year numbers.\n\ 706Year numbers less than 100 are treated just like other year numbers.\n\
705If you want them to stand for years above 1900, you must do that yourself.") 707If you want them to stand for years in this century, you must do that yourself.")
706 (sec, minute, hour, day, month, year, zone) 708 (sec, minute, hour, day, month, year, zone)
707 Lisp_Object sec, minute, hour, day, month, year, zone; 709 Lisp_Object sec, minute, hour, day, month, year, zone;
708{ 710{
709 time_t time; 711 time_t time;
710 int fullyear, mon, days, seconds, tz = 0; 712 struct tm tm;
711 713
712 CHECK_NATNUM (sec, 0); 714 CHECK_NUMBER (sec, 0);
713 CHECK_NATNUM (minute, 1); 715 CHECK_NUMBER (minute, 1);
714 CHECK_NATNUM (hour, 2); 716 CHECK_NUMBER (hour, 2);
715 CHECK_NATNUM (day, 3); 717 CHECK_NUMBER (day, 3);
716 CHECK_NATNUM (month, 4); 718 CHECK_NUMBER (month, 4);
717 CHECK_NATNUM (year, 5); 719 CHECK_NUMBER (year, 5);
718 720
719 fullyear = XINT (year); 721 tm.tm_sec = XINT (sec);
720 722 tm.tm_min = XINT (minute);
721 /* Adjust incoming datespec to epoch = March 1, year 0. 723 tm.tm_hour = XINT (hour);
722 The "date" March 1, year 0, is an abstraction used purely for its 724 tm.tm_mday = XINT (day);
723 computational convenience; year 0 never existed. */ 725 tm.tm_mon = XINT (month) - 1;
724 mon = XINT (month) - 1 + 10; 726 tm.tm_year = XINT (year) - 1900;
725 fullyear += mon/12 - 1; 727 tm.tm_isdst = -1;
726 mon %= 12; 728
727 729 if (CONSP (zone))
728 days = XINT (day) - 1; /* day of month */ 730 zone = Fcar (zone);
729 while (mon-- > 0) /* day of year */
730 days += days_per_month[mon];
731 days += 146097 * (fullyear/400); /* 400 years = 146097 days */
732 fullyear %= 400;
733 days += 36524 * (fullyear/100); /* 100 years = 36524 days */
734 fullyear %= 100;
735 days += 1461 * (fullyear/4); /* 4 years = 1461 days */
736 fullyear %= 4;
737 days += 365 * fullyear; /* 1 year = 365 days */
738
739 /* Adjust computed datespec to epoch = January 1, 1970. */
740 days += 59; /* March 1 is 59th day. */
741 days -= 719527; /* 1970 years = 719527 days */
742
743 seconds = XINT (sec) + 60 * XINT (minute) + 3600 * XINT (hour);
744
745 if (sizeof (time_t) == 4
746 && ((days+(seconds/86400) > 24854) || (days+(seconds/86400) < -24854)))
747 error ("the specified time is outside the representable range");
748
749 time = days * 86400 + seconds;
750
751 /* We have the correct value for UTC. Adjust for timezones. */
752 if (NILP (zone)) 731 if (NILP (zone))
732 time = mktime (&tm);
733 else
753 { 734 {
754 struct tm gmt, *t; 735 char tzbuf[100];
755 time_t adjusted_time; 736 char *tzstring;
756 int adjusted_tz; 737 char **oldenv = environ, **newenv;
757 /* If the system does not use timezones, gmtime returns 0, and we 738
758 already have the correct value, by definition. */ 739 if (STRINGP (zone))
759 if ((t = gmtime (&time)) != 0) 740 tzstring = XSTRING (zone)->data;
741 else if (INTEGERP (zone))
760 { 742 {
761 gmt = *t; 743 int abszone = abs (XINT (zone));
762 t = localtime (&time); 744 sprintf (tzbuf, "XXX%s%d:%02d:%02d", "-" + (XINT (zone) < 0),
763 tz = difftm (t, &gmt); 745 abszone / (60*60), (abszone/60) % 60, abszone % 60);
764 /* The timezone returned is that at the specified Universal Time, 746 tzstring = tzbuf;
765 not the local time, which is what we want. Adjust, repeat. */
766 adjusted_time = time - tz;
767 gmt = *gmtime (&adjusted_time); /* this is safe now */
768 t = localtime (&adjusted_time);
769 adjusted_tz = difftm (t, &gmt);
770 /* In case of discrepancy, adjust again for extra accuracy. */
771 if (adjusted_tz != tz)
772 {
773 adjusted_time = time - adjusted_tz;
774 gmt = *gmtime (&adjusted_time);
775 t = localtime (&adjusted_time);
776 adjusted_tz = difftm (t, &gmt);
777 }
778 tz = adjusted_tz;
779 } 747 }
748 else
749 error ("Invalid time zone specification");
750
751 /* Set TZ before calling mktime; merely adjusting mktime's returned
752 value doesn't suffice, since that would mishandle leap seconds. */
753 set_time_zone_rule (tzstring);
754
755 time = mktime (&tm);
756
757 /* Restore TZ to previous value. */
758 newenv = environ;
759 environ = oldenv;
760 free (newenv);
761#ifdef LOCALTIME_CACHE
762 tzset ();
763#endif
780 } 764 }
781 else
782 {
783 if (CONSP (zone))
784 zone = Fcar (zone);
785 CHECK_NUMBER (zone, 6);
786 tz = XINT (zone);
787 }
788 765
789 return make_time (time - tz); 766 if (time == (time_t) -1)
767 error ("Specified time is not representable");
768
769 return make_time (time);
790} 770}
791 771
792DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string, 0, 1, 0, 772DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string, 0, 1, 0,
@@ -905,10 +885,7 @@ If TZ is nil, use implementation-defined default time zone information.")
905 (tz) 885 (tz)
906 Lisp_Object tz; 886 Lisp_Object tz;
907{ 887{
908 extern char **environ;
909 static char **environbuf; 888 static char **environbuf;
910 int envptrs;
911 char **from, **to, **newenv;
912 char *tzstring; 889 char *tzstring;
913 890
914 if (NILP (tz)) 891 if (NILP (tz))
@@ -919,6 +896,24 @@ If TZ is nil, use implementation-defined default time zone information.")
919 tzstring = XSTRING (tz)->data; 896 tzstring = XSTRING (tz)->data;
920 } 897 }
921 898
899 set_time_zone_rule (tzstring);
900 if (environbuf)
901 free (environbuf);
902 environbuf = environ;
903
904 return Qnil;
905}
906
907/* Set the local time zone rule to TZSTRING.
908 This allocates memory into `environ', which it is the caller's
909 responsibility to free. */
910static void
911set_time_zone_rule (tzstring)
912 char *tzstring;
913{
914 int envptrs;
915 char **from, **to, **newenv;
916
922 for (from = environ; *from; from++) 917 for (from = environ; *from; from++)
923 continue; 918 continue;
924 envptrs = from - environ + 2; 919 envptrs = from - environ + 2;
@@ -938,15 +933,10 @@ If TZ is nil, use implementation-defined default time zone information.")
938 *to = 0; 933 *to = 0;
939 934
940 environ = newenv; 935 environ = newenv;
941 if (environbuf)
942 free (environbuf);
943 environbuf = newenv;
944 936
945#ifdef LOCALTIME_CACHE 937#ifdef LOCALTIME_CACHE
946 tzset (); 938 tzset ();
947#endif 939#endif
948
949 return Qnil;
950} 940}
951 941
952void 942void