emacs-diffs
[Top][All Lists]
Advanced

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

[Emacs-diffs] master 93fe420 4/5: New (TICKS . HZ) timestamp format


From: Paul Eggert
Subject: [Emacs-diffs] master 93fe420 4/5: New (TICKS . HZ) timestamp format
Date: Sun, 7 Oct 2018 02:31:14 -0400 (EDT)

branch: master
commit 93fe420942c08111a6048af7c4d7807c61d80a09
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    New (TICKS . HZ) timestamp format
    
    This follows on a suggestion by Stefan Monnier in:
    https://lists.gnu.org/r/emacs-devel/2018-08/msg00991.html
    (Bug#32902).
    * doc/lispref/buffers.texi (Modification Time):
    * doc/lispref/os.texi (Processor Run Time, Time Calculations)
    * doc/lispref/processes.texi (System Processes):
    * doc/lispref/text.texi (Undo):
    Let the "Time of Day" section cover timestamp format details.
    * doc/lispref/os.texi (Time of Day):
    Say that timestamp internal format should not be assumed.
    Document new (ticks . hz) format.  Omit mention of seconds-to-time
    since it is now just an alias for encode-time.
    (Time Conversion): Document encode-time extension.
    * etc/NEWS: Mention changes.
    * lisp/calendar/cal-dst.el (calendar-system-time-basis): Now const.
    * lisp/calendar/cal-dst.el (calendar-absolute-from-time)
    (calendar-time-from-absolute)
    (calendar-next-time-zone-transition):
    * lisp/emacs-lisp/timer.el (timer-next-integral-multiple-of-time):
    Simplify by using bignums, (TICKS . HZ), and new encode-time.
    * lisp/emacs-lisp/timer.el (timer-next-integral-multiple-of-time):
    Simplify by using bignums and new encode-time.
    * lisp/calendar/parse-time.el (parse-iso8601-time-string):
    Handle DST more accurately, by using new encode-time.
    * lisp/calendar/time-date.el (seconds-to-time):
    * lisp/calendar/timeclock.el (timeclock-seconds-to-time):
    Now just an alias for encode-time.
    * lisp/calendar/time-date.el (days-to-time):
    * lisp/emacs-lisp/timer.el (timer--time-setter):
    * lisp/net/ntlm.el (ntlm-compute-timestamp):
    * lisp/obsolete/vc-arch.el (vc-arch-add-tagline):
    * lisp/org/org-id.el (org-id-uuid, org-id-time-to-b36):
    * lisp/tar-mode (tar-octal-time):
    Don't assume timestamps default to list form.
    * lisp/tar-mode.el (tar-parse-octal-long-integer):
    Now an obsolete alias for tar-parse-octal-integer.
    * src/keyboard.c (decode_timer): Adjust to changes to
    time decoding functions elsewhere.
    * src/timefns.c: Include bignum.h, limits.h.
    (FASTER_TIMEFNS): New macro.
    (WARN_OBSOLETE_TIMESTAMPS, CURRENT_TIME_LIST)
    (timespec_hz, trillion, ztrillion):
    New constants.
    (make_timeval): Use TIME_T_MAX instead of its definiens.
    (check_time_validity, time_add, time_subtract):
    Remove.  All uses removed.
    (disassemble_lisp_time): Remove; old code now folded into
    decode_lisp_time.  All callers changed.
    (invalid_hz, s_ns_to_double, ticks_hz_list4, mpz_set_time)
    (timespec_mpz, timespec_ticks, time_hz_ticks)
    (lisp_time_hz_ticks, lisp_time_seconds)
    (time_form_stamp, lisp_time_form_stamp, decode_ticks_hz)
    (decode_lisp_time, mpz_time, list4_to_timespec):
    New functions.
    (decode_float_time, decode_time_components, lisp_to_timespec):
    Adjust to new struct lisp_time, which does not lose
    information like the old one did.
    (enum timeform): New enum.
    (decode_time_components): New arg FORM.  All callers changed.
    RESULT and DRESULT are now mutually exclusive; no callers need
    to change because of this.
    (decode_time_components, lisp_time_struct)
    (lisp_seconds_argument, time_arith, make_lisp_time, Ffloat_time)
    (Fencode_time):
    Add support for (TICKS . HZ) form.
    (DECODE_SECS_ONLY): New constant.
    (lisp_time_struct): 2nd arg is now enum timeform, not int.
    All callers changed.
    (check_tm_member): Support bignums.m
    (Fencode_time): Add new two-arg functionality.
    * src/systime.h (struct lisp_time): Now ticks+hz rather than
    hi+lo+us+ps, since ticks+hz does not lose info.
    * test/src/systime-tests.el (time-equal-p-nil-nil):
    New test.
---
 doc/lispref/buffers.texi    |   10 +-
 doc/lispref/os.texi         |  157 ++++--
 doc/lispref/processes.texi  |   19 +-
 doc/lispref/text.texi       |    5 +-
 doc/misc/emacs-mime.texi    |   69 +--
 etc/NEWS                    |   15 +
 lisp/calendar/cal-dst.el    |   55 +--
 lisp/calendar/parse-time.el |    5 +-
 lisp/calendar/time-date.el  |   10 +-
 lisp/calendar/timeclock.el  |    3 +-
 lisp/emacs-lisp/timer.el    |   42 +-
 lisp/net/ntlm.el            |    3 +-
 lisp/obsolete/vc-arch.el    |    3 +-
 lisp/org/org-id.el          |    4 +-
 lisp/tar-mode.el            |   28 +-
 src/bignum.c                |    2 +-
 src/keyboard.c              |   11 +-
 src/systime.h               |   15 +-
 src/timefns.c               | 1137 ++++++++++++++++++++++++++++++-------------
 test/src/timefns-tests.el   |    3 +
 20 files changed, 1033 insertions(+), 563 deletions(-)

diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi
index 1acf4ba..8789a8d 100644
--- a/doc/lispref/buffers.texi
+++ b/doc/lispref/buffers.texi
@@ -648,10 +648,7 @@ file should not be done.
 
 @defun visited-file-modtime
 This function returns the current buffer's recorded last file
-modification time, as a list of the form @code{(@var{high} @var{low}
address@hidden @var{picosec})}.  (This is the same format that
address@hidden uses to return time values; @pxref{File
-Attributes}.)
+modification time, as a Lisp timestamp (@pxref{Time of Day}).
 
 If the buffer has no recorded last modification time, this function
 returns zero.  This case occurs, for instance, if the buffer is not
@@ -671,9 +668,8 @@ is not @code{nil}, and otherwise to the last modification 
time of the
 visited file.
 
 If @var{time} is neither @code{nil} nor an integer flag returned
-by @code{visited-file-modtime}, it should have the form
address@hidden(@var{high} @var{low} @var{microsec} @var{picosec})},
-the format used by @code{current-time} (@pxref{Time of Day}).
+by @code{visited-file-modtime}, it should be a Lisp time value
+(@pxref{Time of Day}).
 
 This function is useful if the buffer was not read from the file
 normally, or if the file itself has been changed for some known benign
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 8ce5a5e..ea69153 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -1233,11 +1233,44 @@ return value is @code{nil}.
   This section explains how to determine the current time and time
 zone.
 
address@hidden Lisp timestamp
address@hidden timestamp, Lisp
+  Many functions like @code{current-time} and @code{file-attributes}
+return @dfn{Lisp timestamp} values that count seconds, and that can
+represent absolute time by counting seconds since the @dfn{epoch} of
+1970-01-01 00:00:00 UTC.
+
+  Although traditionally Lisp timestamps were integer pairs, their
+form has evolved and programs ordinarily should not depend on the
+current default form.  If your program needs a particular timestamp
+form, you can use the @code{encode-time} function to convert it to the
+needed form.  @xref{Time Conversion}.
+
 @cindex epoch
-  Most of these functions represent time as a list of four integers
address@hidden(@var{sec-high} @var{sec-low} @var{microsec} @var{picosec})}.
-This represents the number of seconds from the @dfn{epoch} (January
-1, 1970 at 00:00 UTC), using the formula:
+  There are currently three forms of Lisp timestamps, each of
+which represents a number of seconds:
+
address@hidden @bullet
address@hidden
+An integer.  Although this is the simplest form, it cannot represent
+subsecond timestamps.
+
address@hidden
+A pair of integers @code{(@var{ticks} . @var{hz})}, where @var{hz} is
+positive.  This represents @var{ticks}/@var{hz} seconds, which is the
+same time as plain @var{ticks} if @var{hz} is 1.  A common value for
address@hidden is 1000000000, for a nanosecond-resolution
address@hidden @var{hz} should be at least 65536 to avoid
+compatibility warnings when the timestamp is passed to standard
+functions, as previous versions of Emacs would interpret such a
+timestamps differently due to backward-compatibility concerns.  These
+warnings are intended to be removed in a future Emacs version.}
+
address@hidden
+A list of four integers @code{(@var{high} @var{low} @var{micro}
address@hidden)}, where 0 @leq{} @var{low} < 65536, 0 @leq{} @var{micro} <
+1000000, and 0 @leq{} @var{pico} < 1000000.
+This represents the number of seconds using the formula:
 @ifnottex
 @var{high} * 2**16 + @var{low} + @var{micro} * address@hidden +
 @var{pico} * address@hidden
@@ -1245,21 +1278,23 @@ This represents the number of seconds from the 
@dfn{epoch} (January
 @tex
 $high*2^{16} + low + micro*10^{-6} + pico*10^{-12}$.
 @end tex
-The return value of @code{current-time} represents time using this
-form, as do the timestamps in the return values of other functions
-such as @code{file-attributes} (@pxref{Definition of
-file-attributes}).  In some cases, functions may return two- or
+In some cases, functions may default to returning two- or
 three-element lists, with omitted @var{microsec} and @var{picosec}
 components defaulting to zero.
+On all current machines @var{picosec} is a multiple of 1000, but this
+may change as higher-resolution clocks become available.
address@hidden itemize
 
 @cindex time value
   Function arguments, e.g., the @var{time} argument to
 @code{current-time-string}, accept a more-general @dfn{time value}
-format, which can be a list of integers as above, or a single number
-for seconds since the epoch, or @code{nil} for the current time.  You
-can convert a time value into a human-readable string using
address@hidden and @code{format-time-string}, into a list
-of integers using @code{seconds-to-time}, and into other forms using
+format, which can be a Lisp timestamp, @code{nil} for the current
+time, a single floating-point number for seconds, or a list
address@hidden(@var{high} @var{low} @var{micro})} or @code{(@var{high}
address@hidden)} that is a truncated list timestamp with missing elements
+taken to be zero.  You can convert a time value into
+a human-readable string using @code{format-time-string}, into a Lisp
+timestamp using @code{encode-time}, and into other forms using
 @code{decode-time} and @code{float-time}.  These functions are
 described in the following sections.
 
@@ -1287,12 +1322,7 @@ defaults to the current time zone rule.  @xref{Time Zone 
Rules}.
 @end defun
 
 @defun current-time
-This function returns the current time, represented as a list of four
-integers @code{(@var{sec-high} @var{sec-low} @var{microsec} @var{picosec})}.
-These integers have trailing zeros on systems that return time with
-lower resolutions.  On all current machines @var{picosec} is a
-multiple of 1000, but this may change as higher-resolution clocks
-become available.
+This function returns the current time as a Lisp timestamp.
 @end defun
 
 @defun float-time &optional time
@@ -1306,13 +1336,6 @@ exact.  Do not use this function if precise time stamps 
are required.
 @code{time-to-seconds} is an alias for this function.
 @end defun
 
address@hidden seconds-to-time time
-This function converts a time value to list-of-integer form.
-For example, if @var{time} is a number, @code{(time-to-seconds
-(seconds-to-time @var{time}))} equals the number unless overflow
-or rounding errors occur.
address@hidden defun
-
 @node Time Zone Rules
 @section Time Zone Rules
 @cindex time zone rules
@@ -1434,32 +1457,63 @@ seconds east of Greenwich.
 @var{dow} and @var{utcoff}.
 @end defun
 
address@hidden encode-time seconds minutes hour day month year &optional zone
-This function is the inverse of @code{decode-time}.  It converts seven
-items of calendrical data into a list-of-integer time value.  For the
-meanings of the arguments, see the table above under
address@hidden
address@hidden encode-time time &optional form
+This function converts @var{time} to a Lisp timestamp.
+It can act as the inverse of @code{decode-time}.
+
+The first argument can be a Lisp time value such as @code{nil} for the
+current time, a number of seconds, a pair @code{(@var{ticks}
+. @var{hz})}, or a list @code{(@var{high} @var{low} @var{micro}
address@hidden)} (@pxref{Time of Day}).  It can also be a list
address@hidden(@var{second} @var{minute} @var{hour} @var{day} @var{month}
address@hidden @var{ignored} @var{dst} @var{zone})} that specifies a
+decoded time in the style of @code{decode-time}, so that
address@hidden(encode-time (decode-time ...))}  works.  For the meanings of
+these list members, see the table under @code{decode-time}.
+
+The optional @var{form} argument specifies the desired timestamp form
+to be returned.  If @var{form} is the symbol @code{integer}, this
+function returns an integer count of seconds.  If @var{form} is a
+positive integer, it specifies a clock frequency and this function
+returns an integer-pair timestamp @code{(@var{ticks}
+. @var{form})address@hidden a positive integer @var{form}
+should be at least 65536 if the returned value is intended to be given
+to standard functions expecting Lisp timestamps.}  If @var{form} is
address@hidden, this function treats it as a positive integer suitable for
+representing the timestamp; for example, it is treated as 1000000000
+if the platform timestamp has nanosecond resolution.  If @var{form} is
address@hidden, this function returns an integer list @code{(@var{high}
address@hidden @var{micro} @var{pico})}.  Although an omitted or @code{nil}
address@hidden currently acts like @code{list}, this is planned to change
+in a future Emacs version, so callers requiring list timestamps should
+pass @code{list} explicitly.
+
+As an obsolescent calling convention, this function can be given six
+or more arguments.  The first six arguments @var{second},
address@hidden, @var{hour}, @var{day}, @var{month}, and @var{year}
+specify most of the components of a decoded time.  If there are more
+than six arguments the @emph{last} argument is used as @var{zone} and
+any other extra arguments are ignored, so that @code{(apply
+'encode-time (decode-time ...))} works; otherwise @var{zone} defaults
+to the current time zone rule (@pxref{Time Zone Rules}).  The decoded
+time's @var{dst} component is treated as if it was @minus{}1, and
address@hidden so it takes its default value.
 
 Year numbers less than 100 are not treated specially.  If you want them
 to stand for years above 1900, or years above 2000, you must alter them
 yourself before you call @code{encode-time}.
 
-The optional argument @var{zone} defaults to the current time zone rule.
address@hidden Zone Rules}.
-
-If you pass more than seven arguments to @code{encode-time}, the first
-six are used as @var{seconds} through @var{year}, the last argument is
-used as @var{zone}, and the arguments in between are ignored.  This
-feature makes it possible to use the elements of a list returned by
address@hidden as the arguments to @code{encode-time}, like this:
+The @code{encode-time} function acts as a rough inverse to
address@hidden  For example, you can pass the output of
+the latter to the former as follows:
 
 @example
-(apply 'encode-time (decode-time @dots{}))
+(encode-time (decode-time @dots{}))
 @end example
 
 You can perform simple date arithmetic by using out-of-range values for
-the @var{seconds}, @var{minutes}, @var{hour}, @var{day}, and @var{month}
-arguments; for example, day 0 means the day preceding the given month.
address@hidden, @var{minutes}, @var{hour}, @var{day}, and @var{month};
+for example, day 0 means the day preceding the given month.
 
 The operating system puts limits on the range of possible time values;
 if you try to encode a time that is out of range, an error results.
@@ -1474,12 +1528,12 @@ on others, years as early as 1901 do work.
 @cindex formatting time values
 
   These functions convert time values to text in a string, and vice versa.
-Time values include @code{nil}, numbers, and lists of two to four
-integers (@pxref{Time of Day}).
+Time values include @code{nil}, numbers, and Lisp timestamps
+(@pxref{Time of Day}).
 
 @defun date-to-time string
 This function parses the time-string @var{string} and returns the
-corresponding time value.
+corresponding Lisp timestamp.
 @end defun
 
 @defun format-time-string format-string &optional time zone
@@ -1701,10 +1755,8 @@ When called interactively, it prints the uptime in the 
echo area.
 @end deffn
 
 @defun get-internal-run-time
-This function returns the processor run time used by Emacs as a list
-of four integers: @code{(@var{sec-high} @var{sec-low} @var{microsec}
address@hidden)}, using the same format as @code{current-time}
-(@pxref{Time of Day}).
+This function returns the processor run time used by Emacs, as a Lisp
+timestamp (@pxref{Time of Day}).
 
 Note that the time returned by this function excludes the time Emacs
 was not using the processor, and if the Emacs process has several
@@ -1729,9 +1781,10 @@ interactively, it prints the duration in the echo area.
 @cindex calendrical computations
 
   These functions perform calendrical computations using time values
-(@pxref{Time of Day}).  A value of @code{nil} for any of their
+(@pxref{Time of Day}).  As with any time value, a value of
address@hidden for any of their
 time-value arguments stands for the current system time, and a single
-integer number stands for the number of seconds since the epoch.
+number stands for the number of seconds since the epoch.
 
 @defun time-less-p t1 t2
 This returns @code{t} if time value @var{t1} is less than time value
@@ -1757,7 +1810,7 @@ float-time}) to convert the result into seconds.
 This returns the sum of two time values, as a time value.
 However, the result is a float if either argument is a float infinity or 
address@hidden
 One argument should represent a time difference rather than a point in time,
-either as a list or as a single number of elapsed seconds.
+as a time value that is often just a single number of elapsed seconds.
 Here is how to add a number of seconds to a time value:
 
 @example
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index 89ad1cf..e1113e3 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -2158,19 +2158,17 @@ faults for all the child processes of the given process.
 
 @item utime
 Time spent by the process in the user context, for running the
-application's code.  The corresponding @var{value} is in the
address@hidden@code{(@var{high} @var{low} @var{microsec} @var{picosec})}} 
format, the same
-format used by functions @code{current-time} (@pxref{Time of Day,
-current-time}) and @code{file-attributes} (@pxref{File Attributes}).
+application's code.  The corresponding @var{value} is a Lisp
+timestamp (@pxref{Time of Day}).
 
 @item stime
 Time spent by the process in the system (kernel) context, for
-processing system calls.  The corresponding @var{value} is in the same
-format as for @code{utime}.
+processing system calls.  The corresponding @var{value} is a Lisp
+timestamp.
 
 @item time
 The sum of @code{utime} and @code{stime}.  The corresponding
address@hidden is in the same format as for @code{utime}.
address@hidden is a Lisp timestamp.
 
 @item cutime
 @itemx cstime
@@ -2189,13 +2187,10 @@ nice values get scheduled more favorably.)
 The number of threads in the process.
 
 @item start
-The time when the process was started, in the same
address@hidden(@var{high} @var{low} @var{microsec} @var{picosec})} format used 
by
address@hidden and @code{current-time}.
+The time when the process was started, as a Lisp timestamp.
 
 @item etime
-The time elapsed since the process started, in the format @code{(@var{high}
address@hidden @var{microsec} @var{picosec})}.
+The time elapsed since the process started, as a Lisp timestamp.
 
 @item vsize
 The virtual memory size of the process, measured in kilobytes.
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 8258270..6c38d8e 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -1327,9 +1327,8 @@ elements follow immediately after this element.
 
 @item (t . @var{time-flag})
 This kind of element indicates that an unmodified buffer became
-modified.  A @var{time-flag} of the form
address@hidden(@var{sec-high} @var{sec-low} @var{microsec}
address@hidden)} represents the visited file's modification time as of
+modified.  A @var{time-flag} that is a non-integer Lisp timestamp
+represents the visited file's modification time as of
 when it was previously visited or saved, using the same format as
 @code{current-time}; see @ref{Time of Day}.
 A @var{time-flag} of 0 means the buffer does not correspond to any file;
diff --git a/doc/misc/emacs-mime.texi b/doc/misc/emacs-mime.texi
index 9280311..f46b2a7 100644
--- a/doc/misc/emacs-mime.texi
+++ b/doc/misc/emacs-mime.texi
@@ -1524,12 +1524,12 @@ many mailers don't support it.  @xref{rfc2231}.
 @section time-date
 
 While not really a part of the @acronym{MIME} library, it is convenient to
-document this library here.  It deals with parsing @code{Date} headers
+document time conversion functions often used when parsing @code{Date} headers
 and manipulating time.  (Not by using tesseracts, though, I'm sorry to
 say.)
 
-These functions convert between five formats: A date string, an Emacs
-time structure, a decoded time list, a second number, and a day number.
+These functions convert between five formats: A date string, a Lisp
+timestamp, a decoded time list, a second number, and a day number.
 
 Here's a bunch of time/date/second/day examples:
 
@@ -1537,35 +1537,41 @@ Here's a bunch of time/date/second/day examples:
 (parse-time-string "Sat Sep 12 12:21:54 1998 +0200")
 @result{} (54 21 12 12 9 1998 6 -1 7200)
 
-(date-to-time "Sat Sep 12 12:21:54 1998 +0200")
address@hidden (13818 19266)
+(encode-time (date-to-time "Sat Sep 12 12:21:54 1998 +0200")
+             1000000)
address@hidden (905595714000000 . 1000000)
 
-(parse-iso8601-time-string "1998-09-12T12:21:54+0200")
address@hidden (13818 19266)
+(encode-time (parse-iso8601-time-string "1998-09-12T12:21:54+0200")
+             1000000)
address@hidden (905595714000000 . 1000000)
 
-(float-time '(13818 19266))
+(float-time '(905595714000000 . 1000000))
 @result{} 905595714.0
 
-(seconds-to-time 905595714.0)
address@hidden (13818 19266 0 0)
+(encode-time 905595714.0 1000000)
address@hidden (905595714000000 . 1000000)
 
-(time-to-days '(13818 19266))
+(time-to-days '(905595714000000 . 1000000))
 @result{} 729644
 
-(days-to-time 729644)
address@hidden (961933 512)
+(encode-time (days-to-time 729644) 1000000)
address@hidden (63041241600000000 . 1000000)
 
-(time-since '(13818 19266))
address@hidden (6797 9607 984839 247000)
+(encode-time (time-since '(905595714000000 . 1000000))
+             1000000)
address@hidden (631963244775642171 . 1000000000)
 
-(time-less-p '(13818 19266) '(13818 19145))
+(time-less-p '(905595714000000 . 1000000)
+             '(905595593000000000 . 1000000000))
 @result{} nil
 
-(time-equal-p '(13818 19266) '(13818 19145))
address@hidden nil
+(time-equal-p '(905595593000000000 . 1000000000)
+              '(905595593000000    . 1000000   ))
address@hidden t
 
-(time-subtract '(13818 19266) '(13818 19145))
address@hidden (0 121)
+(time-subtract '(905595714000000 . 1000000)
+               '(905595593000000000 . 1000000000))
address@hidden (121000000000 . 1000000000)
 
 (days-between "Sat Sep 12 12:21:54 1998 +0200"
               "Sat Sep 07 12:21:54 1998 +0200")
@@ -1574,13 +1580,13 @@ Here's a bunch of time/date/second/day examples:
 (date-leap-year-p 2000)
 @result{} t
 
-(time-to-day-in-year '(13818 19266))
+(time-to-day-in-year '(905595714000000 . 1000000))
 @result{} 255
 
 (time-to-number-of-days
  (time-since
   (date-to-time "Mon, 01 Jan 2001 02:22:26 GMT")))
address@hidden 4314.095589286675
address@hidden 6472.722661506652
 @end example
 
 And finally, we have @code{safe-date-to-time}, which does the same as
@@ -1595,22 +1601,24 @@ An RFC822 (or similar) date string.  For instance: 
@code{"Sat Sep 12
 12:21:54 1998 +0200"}.
 
 @item time
-An internal Emacs time.  For instance: @code{(13818 26466 0 0)}.
+A Lisp timestamp.
+For instance: @code{(905595714000000 . 1000000)}.
 
 @item seconds
-A floating point representation of the internal Emacs time.  For
-instance: @code{905595714.0}.
+An integer or floating point count of seconds.  For instance:
address@hidden, @code{905595714}.
 
 @item days
 An integer number representing the number of days since 00000101.  For
 instance: @code{729644}.
 
 @item decoded time
-A list of decoded time.  For instance: @code{(54 21 12 12 9 1998 6 t
+A list of decoded time.  For instance: @code{(54 21 12 12 9 1998 6 nil
 7200)}.
 @end table
 
-All the examples above represent the same moment.
+All the examples above represent the same moment, except that
address@hidden represents the day containing the moment.
 
 These are the functions available:
 
@@ -1621,8 +1629,9 @@ Take a date and return a time.
 @item float-time
 Take a time and return seconds.  (This is a built-in function.)
 
address@hidden seconds-to-time
-Take seconds and return a time.
address@hidden encode-time
+Take seconds (and other ways to represent time, notably decoded time
+lists), and return a time.
 
 @item time-to-days
 Take a time and return days.
@@ -1645,7 +1654,7 @@ Take two times and say whether the first time is less 
(i.e., earlier)
 than the second time.  (This is a built-in function.)
 
 @item time-equal-p
-Check, whether two time values are equal.  The time values must not be
+Check whether two time values are equal.  The time values need not be
 in the same format.  (This is a built-in function.)
 
 @item time-since
diff --git a/etc/NEWS b/etc/NEWS
index daacf49..020450c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -996,6 +996,21 @@ functions like process-id that compute process IDs, and 
functions like
 user-uid and group-gid that compute user and group IDs.
 
 +++
+** Although the default timestamp format is still (HI LO US PS),
+it is planned to change in a future Emacs version, to exploit bignums.
+The documentation has been updated to mention that the timestamp
+format may change and that programs should use functions like
+format-time-string, decode-time, and encode-time rather than probing
+the innards of a timestamp directly, or creating a timestamp by hand.
+
++++
+** encode-time supports a new API (encode-time TIME &optional FORM).
+This can convert decoded times and Lisp time values to Lisp timestamps
+of various forms, including a new timestamp form (TICKS . HZ), where
+TICKS is an integer and HZ is a positive integer denoting a clock
+frequency.  The old encode-time API is still supported.
+
++++
 ** 'time-add', 'time-subtract', and 'time-less-p' now accept
 infinities and NaNs too, and propagate them or return nil like
 floating-point operators do.
diff --git a/lisp/calendar/cal-dst.el b/lisp/calendar/cal-dst.el
index 00a8e74..25264bd 100644
--- a/lisp/calendar/cal-dst.el
+++ b/lisp/calendar/cal-dst.el
@@ -97,62 +97,48 @@ If the locale never uses daylight saving time, set this to 
nil."
 ;;;###autoload
 (put 'calendar-current-time-zone-cache 'risky-local-variable t)
 
-(defvar calendar-system-time-basis
+(defconst calendar-system-time-basis
   (calendar-absolute-from-gregorian '(1 1 1970))
   "Absolute date of starting date of system clock.")
 
 (defun calendar-absolute-from-time (x utc-diff)
   "Absolute local date of time X; local time is UTC-DIFF seconds from UTC.
 
-X is (HIGH . LOW) or (HIGH LOW . IGNORED) where HIGH and LOW are the
-high and low 16 bits, respectively, of the number of seconds since
-1970-01-01 00:00:00 UTC, ignoring leap seconds.
+X is the number of seconds since 1970-01-01 00:00:00 UTC,
+ignoring leap seconds.
 
 Returns the pair (ABS-DATE . SECONDS) where SECONDS after local midnight on
 absolute date ABS-DATE is the equivalent moment to X."
-  (let* ((h (car x))
-         (xtail (cdr x))
-         (l (+ utc-diff (if (numberp xtail) xtail (car xtail))))
-         (u (+ (* 512 (mod h 675)) (floor l 128))))
-    ;; Overflow is a terrible thing!
-    (cons (+ calendar-system-time-basis
-             ;; floor((2^16 h +l) / (60*60*24))
-             (* 512 (floor h 675)) (floor u 675))
-          ;; (2^16 h +l) mod (60*60*24)
-          (+ (* (mod u 675) 128) (mod l 128)))))
+  (let ((secsperday 86400)
+        (local (+ x utc-diff)))
+    (cons (+ calendar-system-time-basis (floor local secsperday))
+          (mod local secsperday))))
 
 (defun calendar-time-from-absolute (abs-date s)
   "Time of absolute date ABS-DATE, S seconds after midnight.
 
-Returns the list (HIGH LOW) where HIGH and LOW are the high and low
-16 bits, respectively, of the number of seconds 1970-01-01 00:00:00 UTC,
-ignoring leap seconds, that is the equivalent moment to S seconds after
-midnight UTC on absolute date ABS-DATE."
-  (let* ((a (- abs-date calendar-system-time-basis))
-         (u (+ (* 163 (mod a 512)) (floor s 128))))
-    ;; Overflow is a terrible thing!
-    (list
-     ;; floor((60*60*24*a + s) / 2^16)
-     (+ a (* 163 (floor a 512)) (floor u 512))
-     ;; (60*60*24*a + s) mod 2^16
-     (+ (* 128 (mod u 512)) (mod s 128)))))
+Return the number of seconds since 1970-01-01 00:00:00 UTC,
+ignoring leap seconds, that is the equivalent moment to S seconds
+after midnight UTC on absolute date ABS-DATE."
+  (let ((secsperday 86400))
+    (+ s (* secsperday (- abs-date calendar-system-time-basis)))))
 
 (defun calendar-next-time-zone-transition (time)
   "Return the time of the next time zone transition after TIME.
 Both TIME and the result are acceptable arguments to `current-time-zone'.
 Return nil if no such transition can be found."
-  (let* ((base 65536)           ; 2^16 = base of current-time output
-         (quarter-multiple 120) ; approx = (seconds per quarter year) / base
+  (let* ((time (encode-time time 'integer))
          (time-zone (current-time-zone time))
          (time-utc-diff (car time-zone))
          hi
          hi-zone
          (hi-utc-diff time-utc-diff)
+         (quarter-seconds 7889238) ; Average seconds per 1/4 Gregorian year.
          (quarters '(2 1 3)))
     ;; Heuristic: probe the time zone offset in the next three calendar
     ;; quarters, looking for a time zone offset different from TIME.
     (while (and quarters (eq time-utc-diff hi-utc-diff))
-      (setq hi (cons (+ (car time) (* (car quarters) quarter-multiple)) 0)
+      (setq hi (+ time (* (car quarters) quarter-seconds))
             hi-zone (current-time-zone hi)
             hi-utc-diff (car hi-zone)
             quarters (cdr quarters)))
@@ -163,23 +149,16 @@ Return nil if no such transition can be found."
      ;; Now HI is after the next time zone transition.
      ;; Set LO to TIME, and then binary search to increase LO and decrease HI
      ;; until LO is just before and HI is just after the time zone transition.
-     (let* ((tail (cdr time))
-            (lo (cons (car time) (if (numberp tail) tail (car tail))))
+     (let* ((lo time)
             probe)
        (while
            ;; Set PROBE to halfway between LO and HI, rounding down.
            ;; If PROBE equals LO, we are done.
-           (let* ((lsum (+ (cdr lo) (cdr hi)))
-                  (hsum (+ (car lo) (car hi) (/ lsum base)))
-                  (hsumodd (logand 1 hsum)))
-             (setq probe (cons (/ (- hsum hsumodd) 2)
-                               (/ (+ (* hsumodd base) (% lsum base)) 2)))
-             (not (equal lo probe)))
+           (not (= lo (setq probe (/ (+ lo hi) 2))))
          ;; Set either LO or HI to PROBE, depending on probe results.
          (if (eq (car (current-time-zone probe)) hi-utc-diff)
              (setq hi probe)
            (setq lo probe)))
-       (setcdr hi (list (cdr hi)))
        hi))))
 
 (autoload 'calendar-persian-to-absolute "cal-persia")
diff --git a/lisp/calendar/parse-time.el b/lisp/calendar/parse-time.el
index d6c1e9e..9443fde 100644
--- a/lisp/calendar/parse-time.el
+++ b/lisp/calendar/parse-time.el
@@ -227,7 +227,7 @@ If DATE-STRING cannot be parsed, it falls back to
         (tz-re (nth 2 parse-time-iso8601-regexp))
          re-start
          time seconds minute hour
-         day month year day-of-week dst tz)
+         day month year day-of-week (dst -1) tz)
     ;; We need to populate 'time' with
     ;; (SEC MIN HOUR DAY MON YEAR DOW DST TZ)
 
@@ -243,6 +243,7 @@ If DATE-STRING cannot be parsed, it falls back to
              seconds (string-to-number (match-string 3 date-string))
              re-start (match-end 0))
        (when (string-match tz-re date-string re-start)
+          (setq dst nil)
           (if (string= "Z" (match-string 1 date-string))
               (setq tz 0)  ;; UTC timezone indicated by Z
             (setq tz (+
@@ -260,7 +261,7 @@ If DATE-STRING cannot be parsed, it falls back to
       (setq time (parse-time-string date-string)))
 
     (and time
-        (apply 'encode-time time))))
+        (encode-time time))))
 
 (provide 'parse-time)
 
diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el
index 74c607c..c3898e0 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -168,15 +168,15 @@ If DATE lacks timezone information, GMT is assumed."
 (defalias 'time-to-seconds 'float-time)
 
 ;;;###autoload
-(defun seconds-to-time (seconds)
-  "Convert SECONDS to a time value."
-  (time-add 0 seconds))
+(defalias 'seconds-to-time 'encode-time)
 
 ;;;###autoload
 (defun days-to-time (days)
   "Convert DAYS into a time value."
-  (let ((time (seconds-to-time (* 86400 days))))
-    (if (integerp days)
+  (let ((time (encode-time (* 86400 days))))
+    ;; Traditionally, this returned a two-element list if DAYS was an integer.
+    ;; Keep that tradition if encode-time outputs timestamps in list form.
+    (if (and (integerp days) (consp (cdr time)))
        (setcdr (cdr time) nil))
     time))
 
diff --git a/lisp/calendar/timeclock.el b/lisp/calendar/timeclock.el
index b46e773..ddc2976 100644
--- a/lisp/calendar/timeclock.el
+++ b/lisp/calendar/timeclock.el
@@ -534,8 +534,7 @@ non-nil, the amount returned will be relative to past time 
worked."
       string)))
 
 (define-obsolete-function-alias 'timeclock-time-to-seconds 'float-time "26.1")
-(define-obsolete-function-alias 'timeclock-seconds-to-time 'seconds-to-time
-  "26.1")
+(define-obsolete-function-alias 'timeclock-seconds-to-time 'encode-time "26.1")
 
 ;; Should today-only be removed in favor of timeclock-relative? - gm
 (defsubst timeclock-when-to-leave (&optional today-only)
diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el
index 74d37b0..927e640 100644
--- a/lisp/emacs-lisp/timer.el
+++ b/lisp/emacs-lisp/timer.el
@@ -57,17 +57,11 @@
 
 (defun timer--time-setter (timer time)
   (timer--check timer)
-  (setf (timer--high-seconds timer) (pop time))
-  (let ((low time) (usecs 0) (psecs 0))
-    (when (consp time)
-      (setq low (pop time))
-      (when time
-        (setq usecs (pop time))
-        (when time
-          (setq psecs (car time)))))
-    (setf (timer--low-seconds timer) low)
-    (setf (timer--usecs timer) usecs)
-    (setf (timer--psecs timer) psecs)
+  (let ((lt (encode-time time 'list)))
+    (setf (timer--high-seconds timer) (nth 0 lt))
+    (setf (timer--low-seconds timer) (nth 1 lt))
+    (setf (timer--usecs timer) (nth 2 lt))
+    (setf (timer--psecs timer) (nth 3 lt))
     time))
 
 ;; Pseudo field `time'.
@@ -102,24 +96,14 @@ fire each time Emacs is idle for that many seconds."
   "Yield the next value after TIME that is an integral multiple of SECS.
 More precisely, the next value, after TIME, that is an integral multiple
 of SECS seconds since the epoch.  SECS may be a fraction."
-  (let* ((trillion 1000000000000)
-        (time-sec (+ (nth 1 time)
-                     (* 65536 (nth 0 time))))
-        (delta-sec (mod (- time-sec) secs))
-        (next-sec (+ time-sec (floor delta-sec)))
-        (next-sec-psec (floor (* trillion (mod delta-sec 1))))
-        (sub-time-psec (+ (or (nth 3 time) 0)
-                          (* 1000000 (nth 2 time))))
-        (psec-diff (- sub-time-psec next-sec-psec)))
-    (if (and (<= next-sec time-sec) (< 0 psec-diff))
-       (setq next-sec-psec (+ sub-time-psec
-                              (mod (- psec-diff) (* trillion secs)))))
-    (setq next-sec (+ next-sec (floor next-sec-psec trillion)))
-    (setq next-sec-psec (mod next-sec-psec trillion))
-    (list (floor next-sec 65536)
-         (floor (mod next-sec 65536))
-         (floor next-sec-psec 1000000)
-         (floor (mod next-sec-psec 1000000)))))
+  (let* ((ticks-hz (if (and (consp time) (integerp (car time))
+                           (integerp (cdr time)) (< 0 (cdr time)))
+                      time
+                    (encode-time time 1000000000000)))
+        (hz (cdr ticks-hz))
+        (s-ticks (* secs hz))
+        (more-ticks (+ (car ticks-hz) s-ticks)))
+    (encode-time (cons (- more-ticks (% more-ticks s-ticks)) hz))))
 
 (defun timer-relative-time (time secs &optional usecs psecs)
   "Advance TIME by SECS seconds and optionally USECS microseconds
diff --git a/lisp/net/ntlm.el b/lisp/net/ntlm.el
index 217f0b8..7a68c68 100644
--- a/lisp/net/ntlm.el
+++ b/lisp/net/ntlm.el
@@ -155,8 +155,7 @@ signed integer."
                                    ;; tenths of microseconds between
                                    ;; 1601-01-01 and 1970-01-01
                                    "116444736000000000)")
-                ;; add trailing zeros to support old current-time formats
-                'rawnum (append (current-time) '(0 0))))
+                'rawnum (encode-time nil 'list)))
         result-bytes)
     (dotimes (byte 8)
       (push (calc-eval "and($1,16#FF)" 'rawnum tenths-of-us-since-jan-1-1601)
diff --git a/lisp/obsolete/vc-arch.el b/lisp/obsolete/vc-arch.el
index 9860c9d..e4c52d5 100644
--- a/lisp/obsolete/vc-arch.el
+++ b/lisp/obsolete/vc-arch.el
@@ -133,7 +133,8 @@ If nil, use the value of `vc-diff-switches'.  If t, use no 
switches."
        (file-error (insert (format "%s <%s> %s"
                                    (current-time-string)
                                    user-mail-address
-                                   (+ (nth 2 (current-time))
+                                   (+ (% (car (encode-time nil 1000000))
+                                         1000000)
                                       (buffer-size)))))))
     (comment-region beg (point))))
 
diff --git a/lisp/org/org-id.el b/lisp/org/org-id.el
index 26b203f..ad9b7d1 100644
--- a/lisp/org/org-id.el
+++ b/lisp/org/org-id.el
@@ -357,7 +357,7 @@ So a typical ID could look like \"Org:4nd91V40HI\"."
   "Return string with random (version 4) UUID."
   (let ((rnd (md5 (format "%s%s%s%s%s%s%s"
                          (random)
-                         (current-time)
+                         (encode-time nil 'list)
                          (user-uid)
                          (emacs-pid)
                          (user-full-name)
@@ -416,7 +416,7 @@ The input I may be a character, or a single-letter string."
   "Encode TIME as a 10-digit string.
 This string holds the time to micro-second accuracy, and can be decoded
 using `org-id-decode'."
-  (setq time (or time (current-time)))
+  (setq time (encode-time time 'list))
   (concat (org-id-int-to-b36 (nth 0 time) 4)
          (org-id-int-to-b36 (nth 1 time) 4)
          (org-id-int-to-b36 (or (nth 2 time) 0) 4)))
diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el
index 19e5159..cf4e53a 100644
--- a/lisp/tar-mode.el
+++ b/lisp/tar-mode.el
@@ -304,7 +304,7 @@ write-date, checksum, link-type, and link-name."
            (tar-parse-octal-integer string tar-uid-offset tar-gid-offset)
            (tar-parse-octal-integer string tar-gid-offset tar-size-offset)
            (tar-parse-octal-integer string tar-size-offset tar-time-offset)
-           (tar-parse-octal-long-integer string tar-time-offset tar-chk-offset)
+           (tar-parse-octal-integer string tar-time-offset tar-chk-offset)
            (tar-parse-octal-integer string tar-chk-offset tar-linkp-offset)
            link-p
            linkname
@@ -342,20 +342,8 @@ write-date, checksum, link-type, and link-name."
              start (1+ start)))
       n)))
 
-(defun tar-parse-octal-long-integer (string &optional start end)
-  (if (null start) (setq start 0))
-  (if (null end) (setq end (length string)))
-  (if (= (aref string start) 0)
-      (list 0 0)
-    (let ((lo 0)
-         (hi 0))
-      (while (< start end)
-       (if (>= (aref string start) ?0)
-           (setq lo (+ (* lo 8) (- (aref string start) ?0))
-                 hi (+ (* hi 8) (ash lo -16))
-                 lo (logand lo 65535)))
-       (setq start (1+ start)))
-      (list hi lo))))
+(define-obsolete-function-alias 'tar-parse-octal-long-integer
+  'tar-parse-octal-integer "27.1")
 
 (defun tar-parse-octal-integer-safe (string)
   (if (zerop (length string)) (error "empty string"))
@@ -1276,14 +1264,8 @@ for this to be permanent."
 
 
 (defun tar-octal-time (timeval)
-  ;; Format a timestamp as 11 octal digits.  Ghod, I hope this works...
-  (let ((hibits (car timeval)) (lobits (car (cdr timeval))))
-    (format "%05o%01o%05o"
-           (ash hibits -2)
-           (logior (ash (logand 3 hibits) 1)
-                   (if (> (logand lobits 32768) 0) 1 0))
-           (logand 32767 lobits)
-           )))
+  ;; Format a timestamp as 11 octal digits.
+  (format "%011o" (encode-time timeval 'integer)))
 
 (defun tar-subfile-save-buffer ()
   "In tar subfile mode, save this buffer into its parent tar-file buffer.
diff --git a/src/bignum.c b/src/bignum.c
index 5d8ab67..0ab8de3 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -31,7 +31,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    storage is exhausted.  Admittedly this is not ideal.  An mpz value
    in a temporary is made permanent by mpz_swapping it with a bignum's
    value.  Although typically at most two temporaries are needed,
-   rounding_driver and rounddiv_q need four altogther.  */
+   time_arith, rounddiv_q and rounding_driver each need four.  */
 
 mpz_t mpz[4];
 
diff --git a/src/keyboard.c b/src/keyboard.c
index 35d74f4..8ea15d3 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4163,18 +4163,13 @@ decode_timer (Lisp_Object timer, struct timespec 
*result)
   Lisp_Object *vec;
 
   if (! (VECTORP (timer) && ASIZE (timer) == 9))
-    return 0;
+    return false;
   vec = XVECTOR (timer)->contents;
   if (! NILP (vec[0]))
-    return 0;
-  if (! FIXNUMP (vec[2]))
     return false;
-
-  struct lisp_time t;
-  if (decode_time_components (vec[1], vec[2], vec[3], vec[8], &t, 0) <= 0)
+  if (! FIXNUMP (vec[2]))
     return false;
-  *result = lisp_to_timespec (t);
-  return timespec_valid_p (*result);
+  return list4_to_timespec (vec[1], vec[2], vec[3], vec[8], result);
 }
 
 
diff --git a/src/systime.h b/src/systime.h
index f2f51b0..0bc1e90 100644
--- a/src/systime.h
+++ b/src/systime.h
@@ -75,19 +75,22 @@ extern void set_waiting_for_input (struct timespec *);
    (HI << LO_TIME_BITS) + LO + US / 1e6 + PS / 1e12.  */
 enum { LO_TIME_BITS = 16 };
 
-/* A Lisp time (HI LO US PS), sans the cons cells.  */
+/* Components of a new-format Lisp timestamp.  */
 struct lisp_time
 {
-  EMACS_INT hi;
-  int lo, us, ps;
+  /* Clock count as a Lisp integer.  */
+  Lisp_Object ticks;
+
+  /* Clock frequency (ticks per second) as a positive Lisp integer.
+     (TICKS . HZ) is a valid Lisp timestamp unless HZ < 65536.  */
+  Lisp_Object hz;
 };
 
 /* defined in timefns.c */
 extern struct timeval make_timeval (struct timespec) ATTRIBUTE_CONST;
 extern Lisp_Object make_lisp_time (struct timespec);
-extern int decode_time_components (Lisp_Object, Lisp_Object, Lisp_Object,
-                                  Lisp_Object, struct lisp_time *, double *);
-extern struct timespec lisp_to_timespec (struct lisp_time);
+extern bool list4_to_timespec (Lisp_Object, Lisp_Object, Lisp_Object,
+                              Lisp_Object, struct timespec *);
 extern struct timespec lisp_time_argument (Lisp_Object);
 extern _Noreturn void time_overflow (void);
 extern void init_timefns (bool);
diff --git a/src/timefns.c b/src/timefns.c
index fcb4485..72cb54d 100644
--- a/src/timefns.c
+++ b/src/timefns.c
@@ -22,12 +22,14 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "systime.h"
 
 #include "blockinput.h"
+#include "bignum.h"
 #include "coding.h"
 #include "lisp.h"
 
 #include <strftime.h>
 
 #include <errno.h>
+#include <limits.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -55,6 +57,47 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 # define TIME_T_MAX TYPE_MAXIMUM (time_t)
 #endif
 
+/* Compile with -DFASTER_TIMEFNS=0 to disable common optimizations and
+   allow easier testing of some slow-path code.  */
+#ifndef FASTER_TIMEFNS
+# define FASTER_TIMEFNS 1
+#endif
+
+/* Whether to warn about Lisp timestamps (TICKS . HZ) that may be
+   instances of obsolete-format timestamps (HI . LO) where HI is
+   the high-order bits and LO the low-order 16 bits.  Currently this
+   is true, but it should change to false in a future version of
+   Emacs.  Compile with -DWARN_OBSOLETE_TIMESTAMPS=0 to see what the
+   future will be like.  */
+#ifndef WARN_OBSOLETE_TIMESTAMPS
+enum { WARN_OBSOLETE_TIMESTAMPS = true };
+#endif
+
+/* Although current-time etc. generate list-format timestamps
+   (HI LO US PS), the plan is to change these functions to generate
+   frequency-based timestamps (TICKS . HZ) in a future release.
+   To try this now, compile with -DCURRENT_TIME_LIST=0.  */
+#ifndef CURRENT_TIME_LIST
+enum { CURRENT_TIME_LIST = true };
+#endif
+
+#if FIXNUM_OVERFLOW_P (1000000000)
+static Lisp_Object timespec_hz;
+#else
+# define timespec_hz make_fixnum (TIMESPEC_HZ)
+#endif
+
+#define TRILLION 1000000000000
+#if FIXNUM_OVERFLOW_P (TRILLION)
+static Lisp_Object trillion;
+# define ztrillion (XBIGNUM (trillion)->value)
+#else
+# define trillion make_fixnum (TRILLION)
+# if ULONG_MAX < TRILLION || !FASTER_TIMEFNS
+mpz_t ztrillion;
+# endif
+#endif
+
 /* Return a struct timeval that is roughly equivalent to T.
    Use the least timeval not less than T.
    Return an extremal value if the result would overflow.  */
@@ -69,7 +112,7 @@ make_timeval (struct timespec t)
     {
       if (tv.tv_usec < 999999)
        tv.tv_usec++;
-      else if (tv.tv_sec < TYPE_MAXIMUM (time_t))
+      else if (tv.tv_sec < TIME_T_MAX)
        {
          tv.tv_sec++;
          tv.tv_usec = 0;
@@ -309,52 +352,430 @@ invalid_time (void)
   error ("Invalid time specification");
 }
 
-/* Check a return value compatible with that of decode_time_components.  */
-static void
-check_time_validity (int validity)
+static _Noreturn void
+invalid_hz (Lisp_Object hz)
 {
-  if (validity <= 0)
-    {
-      if (validity < 0)
-       time_overflow ();
-      else
-       invalid_time ();
-    }
+  xsignal2 (Qerror, build_string ("Invalid time frequency"), hz);
 }
 
 /* Return the upper part of the time T (everything but the bottom 16 bits).  */
-static EMACS_INT
+static Lisp_Object
 hi_time (time_t t)
 {
-  time_t hi = t >> LO_TIME_BITS;
-  if (FIXNUM_OVERFLOW_P (hi))
-    time_overflow ();
-  return hi;
+  return INT_TO_INTEGER (t >> LO_TIME_BITS);
 }
 
 /* Return the bottom bits of the time T.  */
-static int
+static Lisp_Object
 lo_time (time_t t)
 {
-  return t & ((1 << LO_TIME_BITS) - 1);
+  return make_fixnum (t & ((1 << LO_TIME_BITS) - 1));
 }
 
-/* Decode a Lisp list SPECIFIED_TIME that represents a time.
-   Set *PHIGH, *PLOW, *PUSEC, *PPSEC to its parts; do not check their values.
-   Return 2, 3, or 4 to indicate the effective length of SPECIFIED_TIME
-   if successful, 0 if unsuccessful.  */
-static int
-disassemble_lisp_time (Lisp_Object specified_time, Lisp_Object *phigh,
-                      Lisp_Object *plow, Lisp_Object *pusec,
-                      Lisp_Object *ppsec)
+/* Convert T into an Emacs time *RESULT, truncating toward minus infinity.
+   Return true if T is in range, false otherwise.  */
+static bool
+decode_float_time (double t, struct lisp_time *result)
+{
+  if (!isfinite (t))
+    return false;
+  /* Actual hz unknown; guess TIMESPEC_HZ.  */
+  mpz_set_d (mpz[1], t);
+  mpz_set_si (mpz[0], floor ((t - trunc (t)) * TIMESPEC_HZ));
+  mpz_addmul_ui (mpz[0], mpz[1], TIMESPEC_HZ);
+  result->ticks = make_integer_mpz ();
+  result->hz = timespec_hz;
+  return true;
+}
+
+/* Compute S + NS/TIMESPEC_HZ as a double.
+   Calls to this function suffer from double-rounding;
+   work around some of the problem by using long double.  */
+static double
+s_ns_to_double (long double s, long double ns)
+{
+  return s + ns / TIMESPEC_HZ;
+}
+
+/* Make a 4-element timestamp (HI LO US PS) from TICKS and HZ.
+   Drop any excess precision.  */
+static Lisp_Object
+ticks_hz_list4 (Lisp_Object ticks, Lisp_Object hz)
+{
+  mpz_t *zticks = bignum_integer (&mpz[0], ticks);
+#if FASTER_TIMEFNS && TRILLION <= ULONG_MAX
+  mpz_mul_ui (mpz[0], *zticks, TRILLION);
+#else
+  mpz_mul (mpz[0], *zticks, ztrillion);
+#endif
+  mpz_fdiv_q (mpz[0], mpz[0], *bignum_integer (&mpz[1], hz));
+#if FASTER_TIMEFNS && TRILLION <= ULONG_MAX
+  unsigned long int fullps = mpz_fdiv_q_ui (mpz[0], mpz[0], TRILLION);
+  int us = fullps / 1000000;
+  int ps = fullps % 1000000;
+#else
+  mpz_fdiv_qr (mpz[0], mpz[1], mpz[0], ztrillion);
+  int ps = mpz_fdiv_q_ui (mpz[1], mpz[1], 1000000);
+  int us = mpz_get_ui (mpz[1]);
+#endif
+  unsigned long ulo = mpz_get_ui (mpz[0]);
+  if (mpz_sgn (mpz[0]) < 0)
+    ulo = -ulo;
+  int lo = ulo & ((1 << LO_TIME_BITS) - 1);
+  mpz_fdiv_q_2exp (mpz[0], mpz[0], LO_TIME_BITS);
+  return list4 (make_integer_mpz (), make_fixnum (lo),
+               make_fixnum (us), make_fixnum (ps));
+}
+
+/* Set ROP to T.  */
+static void
+mpz_set_time (mpz_t rop, time_t t)
+{
+  if (EXPR_SIGNED (t))
+    mpz_set_intmax (rop, t);
+  else
+    mpz_set_uintmax (rop, t);
+}
+
+/* Store into mpz[0] a clock tick count for T, assuming a
+   TIMESPEC_HZ-frequency clock.  Use mpz[1] as a temp.  */
+static void
+timespec_mpz (struct timespec t)
+{
+  mpz_set_ui (mpz[0], t.tv_nsec);
+  mpz_set_time (mpz[1], t.tv_sec);
+  mpz_addmul_ui (mpz[0], mpz[1], TIMESPEC_HZ);
+}
+
+/* Convert T to a Lisp integer counting TIMESPEC_HZ ticks.  */
+static Lisp_Object
+timespec_ticks (struct timespec t)
+{
+  intmax_t accum;
+  if (FASTER_TIMEFNS
+      && !INT_MULTIPLY_WRAPV (t.tv_sec, TIMESPEC_HZ, &accum)
+      && !INT_ADD_WRAPV (t.tv_nsec, accum, &accum))
+    return make_int (accum);
+  timespec_mpz (t);
+  return make_integer_mpz ();
+}
+
+/* Convert T to a Lisp integer counting HZ ticks, taking the floor.
+   Assume T is valid, but check HZ.  */
+static Lisp_Object
+time_hz_ticks (time_t t, Lisp_Object hz)
+{
+  if (FIXNUMP (hz))
+    {
+      if (XFIXNUM (hz) <= 0)
+       invalid_hz (hz);
+      intmax_t ticks;
+      if (FASTER_TIMEFNS && !INT_MULTIPLY_WRAPV (t, XFIXNUM (hz), &ticks))
+       return make_int (ticks);
+    }
+  else if (! (BIGNUMP (hz) && 0 < mpz_sgn (XBIGNUM (hz)->value)))
+    invalid_hz (hz);
+
+  mpz_set_time (mpz[0], t);
+  mpz_mul (mpz[0], mpz[0], *bignum_integer (&mpz[1], hz));
+  return make_integer_mpz ();
+}
+static Lisp_Object
+lisp_time_hz_ticks (struct lisp_time t, Lisp_Object hz)
+{
+  if (FASTER_TIMEFNS && EQ (t.hz, hz))
+    return t.ticks;
+  if (FIXNUMP (hz))
+    {
+      if (XFIXNUM (hz) <= 0)
+       invalid_hz (hz);
+      intmax_t ticks;
+      if (FASTER_TIMEFNS && FIXNUMP (t.ticks) && FIXNUMP (t.hz)
+         && !INT_MULTIPLY_WRAPV (XFIXNUM (t.ticks), XFIXNUM (hz), &ticks))
+       return make_int (ticks / XFIXNUM (t.hz)
+                        - (ticks % XFIXNUM (t.hz) < 0));
+    }
+  else if (! (BIGNUMP (hz) && 0 < mpz_sgn (XBIGNUM (hz)->value)))
+    invalid_hz (hz);
+
+  mpz_mul (mpz[0],
+          *bignum_integer (&mpz[0], t.ticks),
+          *bignum_integer (&mpz[1], hz));
+  mpz_fdiv_q (mpz[0], mpz[0], *bignum_integer (&mpz[1], t.hz));
+  return make_integer_mpz ();
+}
+
+/* Convert T to a Lisp integer counting seconds, taking the floor.  */
+static Lisp_Object
+lisp_time_seconds (struct lisp_time t)
+{
+  if (!FASTER_TIMEFNS)
+    return lisp_time_hz_ticks (t, make_fixnum (1));
+  if (FIXNUMP (t.ticks) && FIXNUMP (t.hz))
+    return make_fixnum (XFIXNUM (t.ticks) / XFIXNUM (t.hz)
+                       - (XFIXNUM (t.ticks) % XFIXNUM (t.hz) < 0));
+  mpz_fdiv_q (mpz[0],
+             *bignum_integer (&mpz[0], t.ticks),
+             *bignum_integer (&mpz[1], t.hz));
+  return make_integer_mpz ();
+}
+
+/* Convert T to a Lisp timestamp.  */
+Lisp_Object
+make_lisp_time (struct timespec t)
+{
+  if (CURRENT_TIME_LIST)
+    {
+      time_t s = t.tv_sec;
+      int ns = t.tv_nsec;
+      return list4 (hi_time (s), lo_time (s),
+                   make_fixnum (ns / 1000), make_fixnum (ns % 1000 * 1000));
+    }
+  else
+    return Fcons (timespec_ticks (t), timespec_hz);
+}
+
+/* Convert T to a Lisp timestamp.  FORM specifies the timestamp format.  */
+static Lisp_Object
+time_form_stamp (time_t t, Lisp_Object form)
+{
+  if (NILP (form))
+    form = CURRENT_TIME_LIST ? Qlist : Qt;
+  if (EQ (form, Qlist))
+    return list2 (hi_time (t), lo_time (t));
+  if (EQ (form, Qt) || EQ (form, Qinteger))
+    return INT_TO_INTEGER (t);
+  return Fcons (time_hz_ticks (t, form), form);
+}
+static Lisp_Object
+lisp_time_form_stamp (struct lisp_time t, Lisp_Object form)
+{
+  if (NILP (form))
+    form = CURRENT_TIME_LIST ? Qlist : Qt;
+  if (EQ (form, Qlist))
+    return ticks_hz_list4 (t.ticks, t.hz);
+  if (EQ (form, Qinteger))
+    return lisp_time_seconds (t);
+  if (EQ (form, Qt))
+    form = t.hz;
+  return Fcons (lisp_time_hz_ticks (t, form), form);
+}
+
+/* From what should be a valid timestamp (TICKS . HZ), generate the
+   corresponding time values.
+
+   If RESULT is not null, store into *RESULT the converted time.
+   Otherwise, store into *DRESULT the number of seconds since the
+   start of the POSIX Epoch.  Unsuccessful calls may or may not store
+   results.
+
+   Return true if successful, false if (TICKS . HZ) would not
+   be a valid new-format timestamp.  */
+static bool
+decode_ticks_hz (Lisp_Object ticks, Lisp_Object hz,
+                struct lisp_time *result, double *dresult)
+{
+  int ns;
+  mpz_t *q = &mpz[0];
+
+  if (! (INTEGERP (ticks)
+        && ((FIXNUMP (hz) && 0 < XFIXNUM (hz))
+            || (BIGNUMP (hz) && 0 < mpz_sgn (XBIGNUM (hz)->value)))))
+    return false;
+
+  if (result)
+    {
+      result->ticks = ticks;
+      result->hz = hz;
+    }
+  else
+    {
+      if (FASTER_TIMEFNS && EQ (hz, timespec_hz))
+       {
+         if (FIXNUMP (ticks))
+           {
+             verify (1 < TIMESPEC_HZ);
+             EMACS_INT s = XFIXNUM (ticks) / TIMESPEC_HZ;
+             ns = XFIXNUM (ticks) % TIMESPEC_HZ;
+             if (ns < 0)
+               s--, ns += TIMESPEC_HZ;
+             *dresult = s_ns_to_double (s, ns);
+             return true;
+           }
+         ns = mpz_fdiv_q_ui (*q, XBIGNUM (ticks)->value, TIMESPEC_HZ);
+       }
+      else if (FASTER_TIMEFNS && EQ (hz, make_fixnum (1)))
+       {
+         ns = 0;
+         if (FIXNUMP (ticks))
+           {
+             *dresult = XFIXNUM (ticks);
+             return true;
+           }
+         q = &XBIGNUM (ticks)->value;
+       }
+      else
+       {
+         mpz_mul_ui (*q, *bignum_integer (&mpz[1], ticks), TIMESPEC_HZ);
+         mpz_fdiv_q (*q, *q, *bignum_integer (&mpz[1], hz));
+         ns = mpz_fdiv_q_ui (*q, *q, TIMESPEC_HZ);
+       }
+
+      *dresult = s_ns_to_double (mpz_get_d (*q), ns);
+    }
+
+  return true;
+}
+
+/* Lisp timestamp classification.  */
+enum timeform
+  {
+   TIMEFORM_INVALID = 0,
+   TIMEFORM_HI_LO, /* seconds in the form (HI << LO_TIME_BITS) + LO.  */
+   TIMEFORM_HI_LO_US, /* seconds plus microseconds (HI LO US) */
+   TIMEFORM_NIL, /* current time in nanoseconds */
+   TIMEFORM_HI_LO_US_PS, /* seconds plus micro and picoseconds (HI LO US PS) */
+   TIMEFORM_FLOAT, /* time as a float */
+   TIMEFORM_TICKS_HZ /* fractional time: HI is ticks, LO is ticks per second */
+  };
+
+/* From the valid form FORM and the time components HIGH, LOW, USEC
+   and PSEC, generate the corresponding time value.  If LOW is
+   floating point, the other components should be zero and FORM should
+   not be TIMEFORM_TICKS_HZ.
+
+   If RESULT is not null, store into *RESULT the converted time.
+   Otherwise, store into *DRESULT the number of seconds since the
+   start of the POSIX Epoch.  Unsuccessful calls may or may not store
+   results.
+
+   Return true if successful, false if the components are of the wrong
+   type.  */
+static bool
+decode_time_components (enum timeform form,
+                       Lisp_Object high, Lisp_Object low,
+                       Lisp_Object usec, Lisp_Object psec,
+                       struct lisp_time *result, double *dresult)
+{
+  switch (form)
+    {
+    case TIMEFORM_INVALID:
+      return false;
+
+    case TIMEFORM_TICKS_HZ:
+      return decode_ticks_hz (high, low, result, dresult);
+
+    case TIMEFORM_FLOAT:
+      {
+       double t = XFLOAT_DATA (low);
+       if (result)
+         return decode_float_time (t, result);
+       else
+         {
+           *dresult = t;
+           return true;
+         }
+      }
+
+    case TIMEFORM_NIL:
+      {
+       struct timespec now = current_timespec ();
+       if (result)
+         {
+           result->ticks = timespec_ticks (now);
+           result->hz = timespec_hz;
+         }
+       else
+         *dresult = s_ns_to_double (now.tv_sec, now.tv_nsec);
+       return true;
+      }
+
+    default:
+      break;
+    }
+
+  if (! (INTEGERP (high) && INTEGERP (low)
+        && FIXNUMP (usec) && FIXNUMP (psec)))
+    return false;
+  EMACS_INT us = XFIXNUM (usec);
+  EMACS_INT ps = XFIXNUM (psec);
+
+  /* Normalize out-of-range lower-order components by carrying
+     each overflow into the next higher-order component.  */
+  us += ps / 1000000 - (ps % 1000000 < 0);
+  mpz_set_intmax (mpz[0], us / 1000000 - (us % 1000000 < 0));
+  mpz_add (mpz[0], mpz[0], *bignum_integer (&mpz[1], low));
+  mpz_addmul_ui (mpz[0], *bignum_integer (&mpz[1], high), 1 << LO_TIME_BITS);
+  ps = ps % 1000000 + 1000000 * (ps % 1000000 < 0);
+  us = us % 1000000 + 1000000 * (us % 1000000 < 0);
+
+  if (result)
+    {
+      switch (form)
+       {
+       case TIMEFORM_HI_LO:
+         /* Floats and nil were handled above, so it was an integer.  */
+         result->hz = make_fixnum (1);
+         break;
+
+       case TIMEFORM_HI_LO_US:
+         mpz_mul_ui (mpz[0], mpz[0], 1000000);
+         mpz_add_ui (mpz[0], mpz[0], us);
+         result->hz = make_fixnum (1000000);
+         break;
+
+       case TIMEFORM_HI_LO_US_PS:
+         mpz_mul_ui (mpz[0], mpz[0], 1000000);
+         mpz_add_ui (mpz[0], mpz[0], us);
+         mpz_mul_ui (mpz[0], mpz[0], 1000000);
+         mpz_add_ui (mpz[0], mpz[0], ps);
+         result->hz = trillion;
+         break;
+
+       default:
+         eassume (false);
+       }
+      result->ticks = make_integer_mpz ();
+    }
+  else
+    *dresult = mpz_get_d (mpz[0]) + (us * 1e6L + ps) / 1e12L;
+
+  return true;
+}
+
+enum { DECODE_SECS_ONLY = WARN_OBSOLETE_TIMESTAMPS + 1 };
+
+/* Decode a Lisp timestamp SPECIFIED_TIME that represents a time.
+
+   FLAGS specifies conversion flags.  If FLAGS & DECODE_SECS_ONLY,
+   ignore and do not validate any sub-second components of an
+   old-format SPECIFIED_TIME.  If FLAGS & WARN_OBSOLETE_TIMESTAMPS,
+   diagnose what could be obsolete (HIGH . LOW) timestamps.
+
+   If PFORM is not null, store into *PFORM the form of SPECIFIED-TIME.
+   If RESULT is not null, store into *RESULT the converted time;
+   otherwise, store into *DRESULT the number of seconds since the
+   start of the POSIX Epoch.  Unsuccessful calls may or may not store
+   results.
+
+   Return true if successful, false if SPECIFIED_TIME is
+   not a valid Lisp timestamp.  */
+static bool
+decode_lisp_time (Lisp_Object specified_time, int flags,
+                 enum timeform *pform,
+                 struct lisp_time *result, double *dresult)
 {
   Lisp_Object high = make_fixnum (0);
   Lisp_Object low = specified_time;
   Lisp_Object usec = make_fixnum (0);
   Lisp_Object psec = make_fixnum (0);
-  int len = 4;
+  enum timeform form = TIMEFORM_HI_LO;
 
-  if (CONSP (specified_time))
+  if (NILP (specified_time))
+    form = TIMEFORM_NIL;
+  else if (FLOATP (specified_time))
+    form = TIMEFORM_FLOAT;
+  else if (CONSP (specified_time))
     {
       high = XCAR (specified_time);
       low = XCDR (specified_time);
@@ -362,259 +783,185 @@ disassemble_lisp_time (Lisp_Object specified_time, 
Lisp_Object *phigh,
        {
          Lisp_Object low_tail = XCDR (low);
          low = XCAR (low);
-         if (CONSP (low_tail))
+         if (! (flags & DECODE_SECS_ONLY))
            {
-             usec = XCAR (low_tail);
-             low_tail = XCDR (low_tail);
              if (CONSP (low_tail))
-               psec = XCAR (low_tail);
-             else
-               len = 3;
-           }
-         else if (!NILP (low_tail))
-           {
-             usec = low_tail;
-             len = 3;
+               {
+                 usec = XCAR (low_tail);
+                 low_tail = XCDR (low_tail);
+                 if (CONSP (low_tail))
+                   {
+                     psec = XCAR (low_tail);
+                     form = TIMEFORM_HI_LO_US_PS;
+                   }
+                 else
+                   form = TIMEFORM_HI_LO_US;
+               }
+             else if (!NILP (low_tail))
+               {
+                 usec = low_tail;
+                 form = TIMEFORM_HI_LO_US;
+               }
            }
-         else
-           len = 2;
        }
       else
-       len = 2;
+       {
+         if (flags & WARN_OBSOLETE_TIMESTAMPS
+             && RANGED_FIXNUMP (0, low, (1 << LO_TIME_BITS) - 1))
+           message ("obsolete timestamp with cdr %"pI"d", XFIXNUM (low));
+         form = TIMEFORM_TICKS_HZ;
+       }
 
-      /* When combining components, require LOW to be an integer,
-        as otherwise it would be a pain to add up times.  */
+      /* Require LOW to be an integer, as otherwise the computation
+        would be considerably trickier.  */
       if (! INTEGERP (low))
-       return 0;
+       form = TIMEFORM_INVALID;
     }
-  else if (INTEGERP (specified_time))
-    len = 2;
-
-  *phigh = high;
-  *plow = low;
-  *pusec = usec;
-  *ppsec = psec;
-  return len;
+
+  if (pform)
+    *pform = form;
+  return decode_time_components (form, high, low, usec, psec, result, dresult);
 }
 
-/* Convert T into an Emacs time *RESULT, truncating toward minus infinity.
-   Return true if T is in range, false otherwise.  */
+/* Convert Z to time_t, returning true if it fits.  */
 static bool
-decode_float_time (double t, struct lisp_time *result)
+mpz_time (mpz_t const z, time_t *t)
 {
-  double lo_multiplier = 1 << LO_TIME_BITS;
-  double emacs_time_min = MOST_NEGATIVE_FIXNUM * lo_multiplier;
-  if (! (emacs_time_min <= t && t < -emacs_time_min))
-    return false;
-
-  double small_t = t / lo_multiplier;
-  EMACS_INT hi = small_t;
-  double t_sans_hi = t - hi * lo_multiplier;
-  int lo = t_sans_hi;
-  long double fracps = (t_sans_hi - lo) * 1e12L;
-#ifdef INT_FAST64_MAX
-  int_fast64_t ifracps = fracps;
-  int us = ifracps / 1000000;
-  int ps = ifracps % 1000000;
-#else
-  int us = fracps / 1e6L;
-  int ps = fracps - us * 1e6L;
-#endif
-  us -= (ps < 0);
-  ps += (ps < 0) * 1000000;
-  lo -= (us < 0);
-  us += (us < 0) * 1000000;
-  hi -= (lo < 0);
-  lo += (lo < 0) << LO_TIME_BITS;
-  result->hi = hi;
-  result->lo = lo;
-  result->us = us;
-  result->ps = ps;
+  if (TYPE_SIGNED (time_t))
+    {
+      intmax_t i;
+      if (! (mpz_to_intmax (z, &i) && TIME_T_MIN <= i && i <= TIME_T_MAX))
+       return false;
+      *t = i;
+    }
+  else
+    {
+      uintmax_t i;
+      if (! (mpz_to_uintmax (z, &i) && i <= TIME_T_MAX))
+       return false;
+      *t = i;
+    }
   return true;
 }
 
-/* From the time components HIGH, LOW, USEC and PSEC taken from a Lisp
-   list, generate the corresponding time value.
-   If LOW is floating point, the other components should be zero.
-
-   If RESULT is not null, store into *RESULT the converted time.
-   If *DRESULT is not null, store into *DRESULT the number of
-   seconds since the start of the POSIX Epoch.
-
-   Return 1 if successful, 0 if the components are of the
-   wrong type, and -1 if the time is out of range.  */
-int
-decode_time_components (Lisp_Object high, Lisp_Object low, Lisp_Object usec,
-                       Lisp_Object psec,
-                       struct lisp_time *result, double *dresult)
+/* Convert T to struct timespec, returning an invalid timespec
+   if T does not fit.  */
+static struct timespec
+lisp_to_timespec (struct lisp_time t)
 {
-  EMACS_INT hi, us, ps;
-  intmax_t lo;
-  if (! (FIXNUMP (high)
-        && FIXNUMP (usec) && FIXNUMP (psec)))
-    return 0;
-  if (! INTEGERP (low))
+  struct timespec result = invalid_timespec ();
+  int ns;
+  mpz_t *q = &mpz[0];
+
+  if (FASTER_TIMEFNS && EQ (t.hz, timespec_hz))
     {
-      if (FLOATP (low))
-       {
-         double t = XFLOAT_DATA (low);
-         if (result && ! decode_float_time (t, result))
-           return -1;
-         if (dresult)
-           *dresult = t;
-         return 1;
-       }
-      else if (NILP (low))
+      if (FIXNUMP (t.ticks))
        {
-         struct timespec now = current_timespec ();
-         if (result)
+         EMACS_INT s = XFIXNUM (t.ticks) / TIMESPEC_HZ;
+         ns = XFIXNUM (t.ticks) % TIMESPEC_HZ;
+         if (ns < 0)
+           s--, ns += TIMESPEC_HZ;
+         if ((TYPE_SIGNED (time_t) ? TIME_T_MIN <= s : 0 <= s)
+             && s <= TIME_T_MAX)
            {
-             result->hi = hi_time (now.tv_sec);
-             result->lo = lo_time (now.tv_sec);
-             result->us = now.tv_nsec / 1000;
-             result->ps = now.tv_nsec % 1000 * 1000;
+             result.tv_sec = s;
+             result.tv_nsec = ns;
            }
-         if (dresult)
-           *dresult = now.tv_sec + now.tv_nsec / 1e9;
-         return 1;
+         return result;
        }
       else
-       return 0;
+       ns = mpz_fdiv_q_ui (*q, XBIGNUM (t.ticks)->value, TIMESPEC_HZ);
     }
-
-  hi = XFIXNUM (high);
-  if (! integer_to_intmax (low, &lo))
-    return -1;
-  us = XFIXNUM (usec);
-  ps = XFIXNUM (psec);
-
-  /* Normalize out-of-range lower-order components by carrying
-     each overflow into the next higher-order component.  */
-  us += ps / 1000000 - (ps % 1000000 < 0);
-  lo += us / 1000000 - (us % 1000000 < 0);
-  if (INT_ADD_WRAPV (lo >> LO_TIME_BITS, hi, &hi))
-    return -1;
-  ps = ps % 1000000 + 1000000 * (ps % 1000000 < 0);
-  us = us % 1000000 + 1000000 * (us % 1000000 < 0);
-  lo &= (1 << LO_TIME_BITS) - 1;
-
-  if (result)
+  else if (FASTER_TIMEFNS && EQ (t.hz, make_fixnum (1)))
     {
-      if (FIXNUM_OVERFLOW_P (hi))
-       return -1;
-      result->hi = hi;
-      result->lo = lo;
-      result->us = us;
-      result->ps = ps;
+      ns = 0;
+      if (FIXNUMP (t.ticks))
+       {
+         EMACS_INT s = XFIXNUM (t.ticks);
+         if ((TYPE_SIGNED (time_t) ? TIME_T_MIN <= s : 0 <= s)
+             && s <= TIME_T_MAX)
+           {
+             result.tv_sec = s;
+             result.tv_nsec = ns;
+           }
+         return result;
+       }
+      else
+       q = &XBIGNUM (t.ticks)->value;
     }
-
-  if (dresult)
+  else
     {
-      double dhi = hi;
-      *dresult = (us * 1e6 + ps) / 1e12 + lo + dhi * (1 << LO_TIME_BITS);
+      mpz_mul_ui (*q, *bignum_integer (q, t.ticks), TIMESPEC_HZ);
+      mpz_fdiv_q (*q, *q, *bignum_integer (&mpz[1], t.hz));
+      ns = mpz_fdiv_q_ui (*q, *q, TIMESPEC_HZ);
     }
 
-  return 1;
+  if (mpz_time (*q, &result.tv_sec))
+    result.tv_nsec = ns;
+  return result;
 }
 
-struct timespec
-lisp_to_timespec (struct lisp_time t)
+/* Convert (HIGH LOW USEC PSEC) to struct timespec.
+   Return true if successful.  */
+bool
+list4_to_timespec (Lisp_Object high, Lisp_Object low,
+                  Lisp_Object usec, Lisp_Object psec,
+                  struct timespec *result)
 {
-  if (! ((TYPE_SIGNED (time_t) ? TIME_T_MIN >> LO_TIME_BITS <= t.hi : 0 <= 
t.hi)
-        && t.hi <= TIME_T_MAX >> LO_TIME_BITS))
-    return invalid_timespec ();
-  time_t s = (t.hi << LO_TIME_BITS) + t.lo;
-  int ns = t.us * 1000 + t.ps / 1000;
-  return make_timespec (s, ns);
+  struct lisp_time t;
+  if (! decode_time_components (TIMEFORM_HI_LO_US_PS, high, low, usec, psec,
+                               &t, 0))
+    return false;
+  *result = lisp_to_timespec (t);
+  return timespec_valid_p (*result);
 }
 
 /* Decode a Lisp list SPECIFIED_TIME that represents a time.
-   Store its effective length into *PLEN.
    If SPECIFIED_TIME is nil, use the current time.
    Signal an error if SPECIFIED_TIME does not represent a time.  */
 static struct lisp_time
-lisp_time_struct (Lisp_Object specified_time, int *plen)
+lisp_time_struct (Lisp_Object specified_time, enum timeform *pform)
 {
-  Lisp_Object high, low, usec, psec;
+  int flags = WARN_OBSOLETE_TIMESTAMPS;
   struct lisp_time t;
-  int len = disassemble_lisp_time (specified_time, &high, &low, &usec, &psec);
-  if (!len)
+  if (! decode_lisp_time (specified_time, flags, pform, &t, 0))
     invalid_time ();
-  int val = decode_time_components (high, low, usec, psec, &t, 0);
-  check_time_validity (val);
-  *plen = len;
   return t;
 }
 
-/* Like lisp_time_struct, except return a struct timespec.
-   Discard any low-order digits.  */
+/* Decode a Lisp list SPECIFIED_TIME that represents a time.
+   Discard any low-order (sub-ns) resolution.
+   If SPECIFIED_TIME is nil, use the current time.
+   Signal an error if SPECIFIED_TIME does not represent a timespec.  */
 struct timespec
 lisp_time_argument (Lisp_Object specified_time)
 {
-  int len;
-  struct lisp_time lt = lisp_time_struct (specified_time, &len);
+  struct lisp_time lt = lisp_time_struct (specified_time, 0);
   struct timespec t = lisp_to_timespec (lt);
   if (! timespec_valid_p (t))
     time_overflow ();
   return t;
 }
 
-/* Like lisp_time_argument, except decode only the seconds part,
-   and do not check the subseconds part.  */
+/* Like lisp_time_argument, except decode only the seconds part, and
+   do not check the subseconds part.  */
 static time_t
 lisp_seconds_argument (Lisp_Object specified_time)
 {
-  Lisp_Object high, low, usec, psec;
-  struct lisp_time t;
-
-  int val = disassemble_lisp_time (specified_time, &high, &low, &usec, &psec);
-  if (val != 0)
-    {
-      val = decode_time_components (high, low, make_fixnum (0),
-                                   make_fixnum (0), &t, 0);
-      if (0 < val
-         && ! ((TYPE_SIGNED (time_t)
-                ? TIME_T_MIN >> LO_TIME_BITS <= t.hi
-                : 0 <= t.hi)
-               && t.hi <= TIME_T_MAX >> LO_TIME_BITS))
-       val = -1;
-    }
-  check_time_validity (val);
-  return (t.hi << LO_TIME_BITS) + t.lo;
-}
-
-static struct lisp_time
-time_add (struct lisp_time ta, struct lisp_time tb)
-{
-  EMACS_INT hi = ta.hi + tb.hi;
-  int lo = ta.lo + tb.lo;
-  int us = ta.us + tb.us;
-  int ps = ta.ps + tb.ps;
-  us += (1000000 <= ps);
-  ps -= (1000000 <= ps) * 1000000;
-  lo += (1000000 <= us);
-  us -= (1000000 <= us) * 1000000;
-  hi += (1 << LO_TIME_BITS <= lo);
-  lo -= (1 << LO_TIME_BITS <= lo) << LO_TIME_BITS;
-  return (struct lisp_time) { hi, lo, us, ps };
-}
-
-static struct lisp_time
-time_subtract (struct lisp_time ta, struct lisp_time tb)
-{
-  EMACS_INT hi = ta.hi - tb.hi;
-  int lo = ta.lo - tb.lo;
-  int us = ta.us - tb.us;
-  int ps = ta.ps - tb.ps;
-  us -= (ps < 0);
-  ps += (ps < 0) * 1000000;
-  lo -= (us < 0);
-  us += (us < 0) * 1000000;
-  hi -= (lo < 0);
-  lo += (lo < 0) << LO_TIME_BITS;
-  return (struct lisp_time) { hi, lo, us, ps };
+  int flags = WARN_OBSOLETE_TIMESTAMPS | DECODE_SECS_ONLY;
+  struct lisp_time lt;
+  if (! decode_lisp_time (specified_time, flags, 0, &lt, 0))
+    invalid_time ();
+  struct timespec t = lisp_to_timespec (lt);
+  if (! timespec_valid_p (t))
+    time_overflow ();
+  return t.tv_sec;
 }
 
+/* Given Lisp operands A and B, add their values, and return the
+   result as a Lisp timestamp that is in (TICKS . HZ) form if either A
+   or B are in that form, (HI LO US PS) form otherwise.  Subtract
+   instead of adding if SUBTRACT.  */
 static Lisp_Object
 time_arith (Lisp_Object a, Lisp_Object b, bool subtract)
 {
@@ -627,45 +974,80 @@ time_arith (Lisp_Object a, Lisp_Object b, bool subtract)
   if (FLOATP (b) && !isfinite (XFLOAT_DATA (b)))
     return subtract ? make_float (-XFLOAT_DATA (b)) : b;
 
-  int alen, blen;
-  struct lisp_time ta = lisp_time_struct (a, &alen);
-  struct lisp_time tb = lisp_time_struct (b, &blen);
-  struct lisp_time t = (subtract ? time_subtract : time_add) (ta, tb);
-  if (FIXNUM_OVERFLOW_P (t.hi))
-    time_overflow ();
-  Lisp_Object val = Qnil;
+  enum timeform aform, bform;
+  struct lisp_time ta = lisp_time_struct (a, &aform);
+  struct lisp_time tb = lisp_time_struct (b, &bform);
+  Lisp_Object ticks, hz;
 
-  switch (max (alen, blen))
+  if (FASTER_TIMEFNS && EQ (ta.hz, tb.hz))
     {
-    default:
-      val = Fcons (make_fixnum (t.ps), val);
-      FALLTHROUGH;
-    case 3:
-      val = Fcons (make_fixnum (t.us), val);
-      FALLTHROUGH;
-    case 2:
-      val = Fcons (make_fixnum (t.lo), val);
-      val = Fcons (make_fixnum (t.hi), val);
-      break;
+      hz = ta.hz;
+      if (FIXNUMP (ta.ticks) && FIXNUMP (tb.ticks))
+       ticks = make_int (subtract
+                         ? XFIXNUM (ta.ticks) - XFIXNUM (tb.ticks)
+                         : XFIXNUM (ta.ticks) + XFIXNUM (tb.ticks));
+      else
+       {
+         (subtract ? mpz_sub : mpz_add)
+           (mpz[0],
+            *bignum_integer (&mpz[0], ta.ticks),
+            *bignum_integer (&mpz[1], tb.ticks));
+         ticks = make_integer_mpz ();
+       }
+    }
+  else
+    {
+      /* The plan is to decompose ta into na/da and tb into nb/db.
+        Start by computing da and db.  */
+      mpz_t *da = bignum_integer (&mpz[1], ta.hz);
+      mpz_t *db = bignum_integer (&mpz[2], tb.hz);
+
+      /* The plan is to compute (na * (db/g) + nb * (da/g)) / lcm (da, db)
+        where g = gcd (da, db).  Start by computing g.  */
+      mpz_t *g = &mpz[3];
+      mpz_gcd (*g, *da, *db);
+
+      /* fa = da/g, fb = db/g.  */
+      mpz_t *fa = &mpz[1], *fb = &mpz[3];
+      mpz_tdiv_q (*fa, *da, *g);
+      mpz_tdiv_q (*fb, *db, *g);
+
+      /* FIXME: Maybe omit need for extra temp by computing fa * db here?  */
+
+      /* hz = fa * db.  This is equal to lcm (da, db).  */
+      mpz_mul (mpz[0], *fa, *db);
+      hz = make_integer_mpz ();
+
+      /* ticks = (fb * na) OPER (fa * nb), where OPER is + or -.
+        OP is the multiply-add or multiply-sub form of OPER.  */
+      mpz_t *na = bignum_integer (&mpz[0], ta.ticks);
+      mpz_mul (mpz[0], *fb, *na);
+      mpz_t *nb = bignum_integer (&mpz[3], tb.ticks);
+      (subtract ? mpz_submul : mpz_addmul) (mpz[0], *fa, *nb);
+      ticks = make_integer_mpz ();
     }
 
-  return val;
+  /* Return the (TICKS . HZ) form if either argument is that way,
+     otherwise the (HI LO US PS) form for backward compatibility.  */
+  return (aform == TIMEFORM_TICKS_HZ || bform == TIMEFORM_TICKS_HZ
+         ? Fcons (ticks, hz)
+         : ticks_hz_list4 (ticks, hz));
 }
 
 DEFUN ("time-add", Ftime_add, Stime_add, 2, 2, 0,
-       doc: /* Return the sum of two time values A and B, as a time value.
-A nil value for either argument stands for the current time.
-See `current-time-string' for the various forms of a time value.  */)
+       doc: /* Return the sum of two time values A and B, as a timestamp.
+See Info node `(elisp)Time of Day' for time value formats.
+For example, nil stands for the current time.  */)
   (Lisp_Object a, Lisp_Object b)
 {
   return time_arith (a, b, false);
 }
 
 DEFUN ("time-subtract", Ftime_subtract, Stime_subtract, 2, 2, 0,
-       doc: /* Return the difference between two time values A and B, as a 
time value.
-Use `float-time' to convert the difference into elapsed seconds.
-A nil value for either argument stands for the current time.
-See `current-time-string' for the various forms of a time value.  */)
+       doc: /* Return the difference between two time values A and B, as a 
timestamp.
+You can use `float-time' to convert the difference into elapsed seconds.
+See Info node `(elisp)Time of Day' for time value formats.
+For example, nil stands for the current time.  */)
   (Lisp_Object a, Lisp_Object b)
 {
   return time_arith (a, b, true);
@@ -685,54 +1067,52 @@ time_cmp (Lisp_Object a, Lisp_Object b)
       return da < db ? -1 : da != db;
     }
 
-  int alen, blen;
-  struct lisp_time ta = lisp_time_struct (a, &alen);
-  struct lisp_time tb = lisp_time_struct (b, &blen);
-  return (ta.hi != tb.hi ? (ta.hi < tb.hi ? -1 : 1)
-         : ta.lo != tb.lo ? (ta.lo < tb.lo ? -1 : 1)
-         : ta.us != tb.us ? (ta.us < tb.us ? -1 : 1)
-         : ta.ps < tb.ps ? -1 : ta.ps != tb.ps);
+  struct lisp_time ta = lisp_time_struct (a, 0);
+
+  /* Compare nil to nil correctly, and other eq values while we're at it.
+     Compare here rather than earlier, to handle NaNs and check formats.  */
+  if (EQ (a, b))
+    return 0;
+
+  struct lisp_time tb = lisp_time_struct (b, 0);
+  mpz_t *za = bignum_integer (&mpz[0], ta.ticks);
+  mpz_t *zb = bignum_integer (&mpz[1], tb.ticks);
+  if (! (FASTER_TIMEFNS && EQ (ta.hz, tb.hz)))
+    {
+      /* This could be sped up by looking at the signs, sizes, and
+        number of bits of the two sides; see how GMP does mpq_cmp.
+        It may not be worth the trouble here, though.  */
+      mpz_mul (mpz[0], *za, *bignum_integer (&mpz[2], tb.hz));
+      mpz_mul (mpz[1], *zb, *bignum_integer (&mpz[2], ta.hz));
+      za = &mpz[0];
+      zb = &mpz[1];
+    }
+  return mpz_cmp (*za, *zb);
 }
 
 DEFUN ("time-less-p", Ftime_less_p, Stime_less_p, 2, 2, 0,
-       doc: /* Return non-nil if time value T1 is earlier than time value T2.
-A nil value for either argument stands for the current time.
-See `current-time-string' for the various forms of a time value.  */)
-  (Lisp_Object t1, Lisp_Object t2)
+       doc: /* Return non-nil if time value A is less than time value B.
+See Info node `(elisp)Time of Day' for time value formats.
+For example, nil stands for the current time.  */)
+  (Lisp_Object a, Lisp_Object b)
 {
-  return time_cmp (t1, t2) < 0 ? Qt : Qnil;
+  return time_cmp (a, b) < 0 ? Qt : Qnil;
 }
 
 DEFUN ("time-equal-p", Ftime_equal_p, Stime_equal_p, 2, 2, 0,
-       doc: /* Return non-nil if T1 and T2 are equal time values.
-A nil value for either argument stands for the current time.
-See `current-time-string' for the various forms of a time value.  */)
-  (Lisp_Object t1, Lisp_Object t2)
+       doc: /* Return non-nil if A and B are equal time values.
+See Info node `(elisp)Time of Day' for time value formats.  */)
+  (Lisp_Object a, Lisp_Object b)
 {
-  return time_cmp (t1, t2) == 0 ? Qt : Qnil;
+  return time_cmp (a, b) == 0 ? Qt : Qnil;
 }
 
 
-/* Make a Lisp list that represents the Emacs time T.  T may be an
-   invalid time, with a slightly negative tv_nsec value such as
-   UNKNOWN_MODTIME_NSECS; in that case, the Lisp list contains a
-   correspondingly negative picosecond count.  */
-Lisp_Object
-make_lisp_time (struct timespec t)
-{
-  time_t s = t.tv_sec;
-  int ns = t.tv_nsec;
-  return list4i (hi_time (s), lo_time (s), ns / 1000, ns % 1000 * 1000);
-}
-
 DEFUN ("float-time", Ffloat_time, Sfloat_time, 0, 1, 0,
        doc: /* Return the current time, as a float number of seconds since the 
epoch.
-If SPECIFIED-TIME is given, it is the time to convert to float
-instead of the current time.  The argument should have the form
-\(HIGH LOW) or (HIGH LOW USEC) or (HIGH LOW USEC PSEC).  Thus,
-you can use times from `current-time' and from `file-attributes'.
-SPECIFIED-TIME can also have the form (HIGH . LOW), but this is
-considered obsolete.
+If SPECIFIED-TIME is given, it is a Lisp time value to convert to
+float instead of the current time.  See Info node `(elisp)Time of Day'
+for time value formats.
 
 WARNING: Since the result is floating point, it may not be exact.
 If precise time stamps are required, use either `current-time',
@@ -740,9 +1120,7 @@ or (if you need time as a string) `format-time-string'.  
*/)
   (Lisp_Object specified_time)
 {
   double t;
-  Lisp_Object high, low, usec, psec;
-  if (! (disassemble_lisp_time (specified_time, &high, &low, &usec, &psec)
-        && decode_time_components (high, low, usec, psec, 0, &t)))
+  if (! decode_lisp_time (specified_time, 0, 0, 0, &t))
     invalid_time ();
   return make_float (t);
 }
@@ -849,10 +1227,7 @@ format_time_string (char const *format, ptrdiff_t 
formatlen,
 
 DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
        doc: /* Use FORMAT-STRING to format the time TIME, or now if omitted or 
nil.
-TIME is specified as (HIGH LOW USEC PSEC), as returned by
-`current-time' or `file-attributes'.  It can also be a single integer
-number of seconds since the epoch.  The obsolete form (HIGH . LOW) is
-also still accepted.
+TIME is a Lisp time value; see Info node `(elisp)Time of Day'.
 
 The optional ZONE is omitted or nil for Emacs local time, t for
 Universal Time, `wall' for system wall clock time, or a string as in
@@ -925,10 +1300,8 @@ usage: (format-time-string FORMAT-STRING &optional TIME 
ZONE)  */)
 
 DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0,
        doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST 
UTCOFF).
-The optional TIME should be a list of (HIGH LOW . IGNORED),
-as from `current-time' and `file-attributes', or nil to use the
-current time.  It can also be a single integer number of seconds since
-the epoch.  The obsolete form (HIGH . LOW) is also still accepted.
+The optional TIME is the Lisp time value to convert.  See Info node
+`(elisp)Time of Day' for time value formats.
 
 The optional ZONE is omitted or nil for Emacs local time, t for
 Universal Time, `wall' for system wall clock time, or a string as in
@@ -983,32 +1356,71 @@ usage: (decode-time &optional TIME ZONE)  */)
 }
 
 /* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that
-   the result is representable as an int.  */
+   the result is representable as an int.  0 <= OFFSET <= TM_YEAR_BASE.  */
 static int
 check_tm_member (Lisp_Object obj, int offset)
 {
-  CHECK_FIXNUM (obj);
-  EMACS_INT n = XFIXNUM (obj);
-  int result;
-  if (INT_SUBTRACT_WRAPV (n, offset, &result))
-    time_overflow ();
-  return result;
+  if (FASTER_TIMEFNS && INT_MAX <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)
+    {
+      CHECK_FIXNUM (obj);
+      EMACS_INT n = XFIXNUM (obj);
+      int i;
+      if (INT_SUBTRACT_WRAPV (n, offset, &i))
+       time_overflow ();
+      return i;
+    }
+  else
+    {
+      CHECK_INTEGER (obj);
+      mpz_sub_ui (mpz[0], *bignum_integer (&mpz[0], obj), offset);
+      intmax_t i;
+      if (! (mpz_to_intmax (mpz[0], &i) && INT_MIN <= i && i <= INT_MAX))
+       time_overflow ();
+      return i;
+    }
 }
 
-DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
-       doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to 
internal time.
-This is the reverse operation of `decode-time', which see.
-
-The optional ZONE is omitted or nil for Emacs local time, t for
-Universal Time, `wall' for system wall clock time, or a string as in
-the TZ environment variable.  It can also be a list (as from
+DEFUN ("encode-time", Fencode_time, Sencode_time, 1, MANY, 0,
+       doc: /* Convert TIME to a timestamp.
+Optional FORM specifies how the returned value should be encoded.
+This can act as the reverse operation of `decode-time', which see.
+
+If TIME is a list (SECOND MINUTE HOUR DAY MONTH YEAR IGNORED DST ZONE)
+it a decoded time in the style of `decode-time', so that (encode-time
+(decode-time ...)) works.  TIME can also be a Lisp time value; see
+Info node `(elisp)Time of Day'.
+
+If FORM is a positive integer, the time is returned as a pair of
+integers (TICKS . FORM), where TICKS is the number of clock ticks and FORM
+is the clock frequency in ticks per second.  (Currently the positive
+integer should be at least 65536 if the returned value is expected to
+be given to standard functions expecting Lisp timestamps.)  If FORM is
+t, the time is returned as (TICKS . PHZ), where PHZ is a
+platform-dependent clock frequency.  If FORM is `integer', the time is
+returned as an integer count of seconds.  If FORM is `list', the time is
+returned as an integer list (HIGH LOW USEC PSEC), where HIGH has the
+most significant bits of the seconds, LOW has the least significant 16
+bits, and USEC and PSEC are the microsecond and picosecond counts.
+Returned values are rounded toward minus infinity.  Although an
+omitted or nil FORM currently acts like `list', this is planned to
+change, so callers requiring list timestamps should specify `list'.
+
+As an obsolescent calling convention, the first 6 arguments SECOND,
+MINUTE, HOUR, DAY, MONTH, and YEAR specify the components of a decoded
+time, where DST assumed to be -1 and FORM is omitted.  If there are more
+than 6 arguments the *last* argument is used as ZONE and any other
+extra arguments are ignored, so that (apply \\='encode-time
+(decode-time ...)) works; otherwise ZONE is assumed to be nil.
+
+If the input is a decoded time, ZONE is nil for Emacs local time, t
+for Universal Time, `wall' for system wall clock time, or a string as
+in the TZ environment variable.  It can also be a list (as from
 `current-time-zone') or an integer (as from `decode-time') applied
 without consideration for daylight saving time.
 
-You can pass more than 7 arguments; then the first six arguments
-are used as SECOND through YEAR, and the *last* argument is used as ZONE.
-The intervening arguments are ignored.
-This feature lets (apply \\='encode-time (decode-time ...)) work.
+If the input is a decoded time and ZONE specifies a time zone with
+daylight-saving transitions, DST is t for daylight saving time and nil
+for standard time.  If DST is -1, the daylight saving flag is guessed.
 
 Out-of-range values for SECOND, MINUTE, HOUR, DAY, or MONTH are allowed;
 for example, a DAY of 0 means the day preceding the given month.
@@ -1018,21 +1430,55 @@ If you want them to stand for years in this century, 
you must do that yourself.
 Years before 1970 are not guaranteed to work.  On some systems,
 year values as low as 1901 do work.
 
-usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
+usage: (encode-time TIME &optional FORM)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
   time_t value;
   struct tm tm;
-  Lisp_Object zone = (nargs > 6 ? args[nargs - 1] : Qnil);
-
-  tm.tm_sec  = check_tm_member (args[0], 0);
-  tm.tm_min  = check_tm_member (args[1], 0);
-  tm.tm_hour = check_tm_member (args[2], 0);
-  tm.tm_mday = check_tm_member (args[3], 0);
-  tm.tm_mon  = check_tm_member (args[4], 1);
-  tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE);
+  Lisp_Object form = Qnil, zone = Qnil;
+  Lisp_Object a = args[0];
   tm.tm_isdst = -1;
 
+  if (nargs <= 2)
+    {
+      if (nargs == 2)
+       form = args[1];
+      Lisp_Object tail = a;
+      for (int i = 0; i < 9; i++, tail = XCDR (tail))
+       if (! CONSP (tail))
+         {
+           struct lisp_time t;
+           if (! decode_lisp_time (a, 0, 0, &t, 0))
+             invalid_time ();
+           return lisp_time_form_stamp (t, form);
+         }
+      tm.tm_sec  = check_tm_member (XCAR (a), 0); a = XCDR (a);
+      tm.tm_min  = check_tm_member (XCAR (a), 0); a = XCDR (a);
+      tm.tm_hour = check_tm_member (XCAR (a), 0); a = XCDR (a);
+      tm.tm_mday = check_tm_member (XCAR (a), 0); a = XCDR (a);
+      tm.tm_mon  = check_tm_member (XCAR (a), 1); a = XCDR (a);
+      tm.tm_year = check_tm_member (XCAR (a), TM_YEAR_BASE); a = XCDR (a);
+      a = XCDR (a);
+      if (SYMBOLP (XCAR (a)))
+       tm.tm_isdst = !NILP (XCAR (a));
+      a = XCDR (a);
+      zone = XCAR (a);
+    }
+  else if (nargs < 6)
+    xsignal2 (Qwrong_number_of_arguments, Qencode_time, make_fixnum (nargs));
+  else
+    {
+      if (6 < nargs)
+       zone = args[nargs - 1];
+      form = Qnil;
+      tm.tm_sec  = check_tm_member (a, 0);
+      tm.tm_min  = check_tm_member (args[1], 0);
+      tm.tm_hour = check_tm_member (args[2], 0);
+      tm.tm_mday = check_tm_member (args[3], 0);
+      tm.tm_mon  = check_tm_member (args[4], 1);
+      tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE);
+    }
+
   timezone_t tz = tzlookup (zone, false);
   value = emacs_mktime_z (tz, &tm);
   xtzfree (tz);
@@ -1040,15 +1486,17 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR 
&optional ZONE)  */)
   if (value == (time_t) -1)
     time_overflow ();
 
-  return list2i (hi_time (value), lo_time (value));
+  return time_form_stamp (value, form);
 }
 
 DEFUN ("current-time", Fcurrent_time, Scurrent_time, 0, 0, 0,
-       doc: /* Return the current time, as the number of seconds since 
1970-01-01 00:00:00.
-The time is returned as a list of integers (HIGH LOW USEC PSEC).
-HIGH has the most significant bits of the seconds, while LOW has the
-least significant 16 bits.  USEC and PSEC are the microsecond and
-picosecond counts.  */)
+       doc: /* Return the current time, counting the number of seconds since 
the epoch.
+
+See Info node `(elisp)Time of Day' for the format of the returned
+timestamp.  Although this is currently list format, it may change in
+future versions of Emacs.  Use `encode-time' if you need a particular
+form; for example, (encode-time nil \\='list) returns the current time
+in list form.  */)
   (void)
 {
   return make_lisp_time (current_timespec ());
@@ -1064,12 +1512,9 @@ The format is `Sun Sep 16 01:03:52 1973'.
 However, see also the functions `decode-time' and `format-time-string'
 which provide a much more powerful and general facility.
 
-If SPECIFIED-TIME is given, it is a time to format instead of the
-current time.  The argument should have the form (HIGH LOW . IGNORED).
-Thus, you can use times obtained from `current-time' and from
-`file-attributes'.  SPECIFIED-TIME can also be a single integer number
-of seconds since the epoch.  The obsolete form (HIGH . LOW) is also
-still accepted.
+If SPECIFIED-TIME is given, it is the Lisp time value to format
+instead of the current time.  See Info node `(elisp)Time of Day' for
+time value formats.
 
 The optional ZONE is omitted or nil for Emacs local time, t for
 Universal Time, `wall' for system wall clock time, or a string as in
@@ -1113,11 +1558,8 @@ OFFSET is an integer number of seconds ahead of UTC 
(east of Greenwich).
     A negative value means west of Greenwich.
 NAME is a string giving the name of the time zone.
 If SPECIFIED-TIME is given, the time zone offset is determined from it
-instead of using the current time.  The argument should have the form
-\(HIGH LOW . IGNORED).  Thus, you can use times obtained from
-`current-time' and from `file-attributes'.  SPECIFIED-TIME can also be
-a single integer number of seconds since the epoch.  The obsolete form
-(HIGH . LOW) is also still accepted.
+instead of using the current time.  The argument should be a Lisp
+time value; see Info node `(elisp)Time of Day'.
 
 The optional ZONE is omitted or nil for Emacs local time, t for
 Universal Time, `wall' for system wall clock time, or a string as in
@@ -1272,6 +1714,21 @@ emacs_setenv_TZ (const char *tzstring)
 void
 syms_of_timefns (void)
 {
+#ifndef timespec_hz
+  timespec_hz = make_int (TIMESPEC_HZ);
+  staticpro (&timespec_hz);
+#endif
+#ifndef trillion
+  trillion = make_int (1000000000000);
+  staticpro (&trillion);
+#endif
+#if (ULONG_MAX < TRILLION || !FASTER_TIMEFNS) && !defined ztrillion
+  mpz_init_set_ui (ztrillion, 1000000);
+  mpz_mul_ui (ztrillion, ztrillion, 1000000);
+#endif
+
+  DEFSYM (Qencode_time, "encode-time");
+
   defsubr (&Scurrent_time);
   defsubr (&Stime_add);
   defsubr (&Stime_subtract);
diff --git a/test/src/timefns-tests.el b/test/src/timefns-tests.el
index 8418b50..435dcf7 100644
--- a/test/src/timefns-tests.el
+++ b/test/src/timefns-tests.el
@@ -77,3 +77,6 @@
                (format-time-string "%Y-%m-%d %H:%M:%S" (- (ash 1 31) 3600) t)
                "2038-01-19 02:14:08")
               (timefns-tests--have-leap-seconds))))
+
+(ert-deftest time-equal-p-nil-nil ()
+  (should (time-equal-p nil nil)))



reply via email to

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