Function: decoded-time-add

decoded-time-add is a byte-compiled function defined in time-date.el.gz.

Signature

(decoded-time-add TIME DELTA)

Documentation

Add DELTA to TIME, both of which are decoded-time structures.

TIME should represent a time, while DELTA should have non-nil entries only for the values that should be altered.

For instance, if you want to "add two months" to TIME, then leave all other fields but the month field in DELTA nil, and make the month field 2. For instance:

  (decoded-time-add (decode-time) (make-decoded-time :month 2))

The values in DELTA can be negative.

If applying a month/year delta leaves the time spec invalid, it is decreased to be valid ("add one month" to January 31st 2019 will yield a result of February 28th 2019 and "add one year" to February 29th 2020 will result in February 28th 2021).

Fields are added in a most to least significant order, so if the adjustment described above happens, it happens before adding days, hours, minutes or seconds.

When changing the time bits in TIME (i.e., second/minute/hour), changes in daylight saving time are not taken into account.

Probably introduced at or before Emacs version 27.1.

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/time-date.el.gz
(defun decoded-time-add (time delta)
  "Add DELTA to TIME, both of which are `decoded-time' structures.
TIME should represent a time, while DELTA should have non-nil
entries only for the values that should be altered.

For instance, if you want to \"add two months\" to TIME, then
leave all other fields but the month field in DELTA nil, and make
the month field 2.  For instance:

  (decoded-time-add (decode-time) (make-decoded-time :month 2))

The values in DELTA can be negative.

If applying a month/year delta leaves the time spec invalid, it
is decreased to be valid (\"add one month\" to January 31st 2019
will yield a result of February 28th 2019 and \"add one year\" to
February 29th 2020 will result in February 28th 2021).

Fields are added in a most to least significant order, so if the
adjustment described above happens, it happens before adding
days, hours, minutes or seconds.

When changing the time bits in TIME (i.e., second/minute/hour),
changes in daylight saving time are not taken into account."
  (let ((time (copy-sequence time))
        seconds)
    ;; Years are simple.
    (when (decoded-time-year delta)
      (incf (decoded-time-year time) (decoded-time-year delta)))

    ;; Months are pretty simple, but start at 1 (for January).
    (when (decoded-time-month delta)
      (let ((new (+ (1- (decoded-time-month time)) (decoded-time-month delta))))
        (setf (decoded-time-month time) (1+ (mod new 12)))
        (incf (decoded-time-year time) (floor new 12))))

    ;; Adjust for month length (as described in the doc string).
    (setf (decoded-time-day time)
          (min (date-days-in-month (decoded-time-year time)
                                   (decoded-time-month time))
               (decoded-time-day time)))

    ;; Days are iterative.
    (when-let* ((days (decoded-time-day delta)))
      (let ((increase (> days 0))
            (days (abs days)))
        (while (> days 0)
          (decoded-time--alter-day time increase)
          (decf days))))

    ;; Do the time part, which is pretty simple (except for leap
    ;; seconds, I guess).
    ;; Time zone adjustments are basically the same as time adjustments.
    (setq seconds (time-convert (or (decoded-time-second delta) 0) t))
    (setq seconds
	  (time-add seconds
		    (time-convert (+ (* (or (decoded-time-hour delta) 0) 3600)
				     (* (or (decoded-time-minute delta) 0) 60)
				     (or (decoded-time-zone delta) 0))
				  (cdr seconds))))

    (decoded-time--alter-second time seconds)
    time))