diff options
| author | Paul Eggert | 1995-09-10 19:38:19 +0000 |
|---|---|---|
| committer | Paul Eggert | 1995-09-10 19:38:19 +0000 |
| commit | c59b5089765fbe387b3d30014c4aefa64952fdc9 (patch) | |
| tree | 9c38fb13c211871a1d4f7cee4a50aa1d0f51573f /src | |
| parent | 6bbb008e7ed360c4ecbe5854a03ae246a08a93a3 (diff) | |
| download | emacs-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.c | 170 |
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 | ||
| 41 | extern char **environ; | ||
| 42 | extern Lisp_Object make_time (); | ||
| 41 | extern void insert_from_buffer (); | 43 | extern void insert_from_buffer (); |
| 42 | static long difftm (); | 44 | static long difftm (); |
| 45 | static 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 | ||
| 694 | static char days_per_month[11] | ||
| 695 | = { 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 }; | ||
| 696 | |||
| 697 | DEFUN ("encode-time", Fencode_time, Sencode_time, 6, 7, 0, | 697 | DEFUN ("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\ |
| 699 | This is the reverse operation of `decode-time', which see. ZONE defaults\n\ | 699 | This is the reverse operation of `decode-time', which see. ZONE defaults\n\ |
| 700 | to the current time zone and daylight savings time if not specified; if\n\ | 700 | to the current time zone rule if not specified; if specified, it can\n\ |
| 701 | specified, it can be either a list (as from `current-time-zone') or an\n\ | 701 | be a string (as from `set-time-zone-rule'), or it can be a list\n\ |
| 702 | integer (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\ |
| 703 | daylight savings time.\n\ | 703 | applied without consideration for daylight savings time.\n\ |
| 704 | Out-of-range values for SEC, MINUTE, HOUR, DAY, or MONTH are allowed;\n\ | ||
| 705 | for example, a DAY of 0 means the day preceding the given month.\n\ | ||
| 704 | Year numbers less than 100 are treated just like other year numbers.\n\ | 706 | Year numbers less than 100 are treated just like other year numbers.\n\ |
| 705 | If you want them to stand for years above 1900, you must do that yourself.") | 707 | If 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 | ||
| 792 | DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string, 0, 1, 0, | 772 | DEFUN ("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. */ | ||
| 910 | static void | ||
| 911 | set_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 | ||
| 952 | void | 942 | void |