aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Fitzsimmons2020-09-29 17:15:40 -0400
committerThomas Fitzsimmons2020-09-29 20:01:29 -0400
commite7670a3ce02dfb4bfe7e94aa02f7171ec0598ef5 (patch)
tree436e1d93c7565af2b52071c877c6bc07cf4fc700
parent7e45ed3a9674e9f436c337bed647ce9f60939ee0 (diff)
downloademacs-e7670a3ce02dfb4bfe7e94aa02f7171ec0598ef5.tar.gz
emacs-e7670a3ce02dfb4bfe7e94aa02f7171ec0598ef5.zip
soap-client: Update soap-decode-date-time
* lisp/net/soap-client.el (soap-decode-date-time): Add support for Emacs versions that support fractional seconds. Make DATATYPE optional. Remove FIXME comment. Co-authored-by: Paul Eggert <eggert@cs.ucla.edu>
-rw-r--r--lisp/net/soap-client.el143
1 files changed, 110 insertions, 33 deletions
diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el
index 81bbc336dc3..8b5ac613b3b 100644
--- a/lisp/net/soap-client.el
+++ b/lisp/net/soap-client.el
@@ -551,30 +551,77 @@ This is a specialization of `soap-encode-value' for
551 (soap-validate-xs-basic-type value-string type) 551 (soap-validate-xs-basic-type value-string type)
552 (insert value-string))))) 552 (insert value-string)))))
553 553
554;; Inspired by rng-xsd-convert-date-time. 554(defun soap-decode-date-time (date-time-string &optional datatype)
555(defun soap-decode-date-time (date-time-string datatype)
556 "Decode DATE-TIME-STRING as DATATYPE. 555 "Decode DATE-TIME-STRING as DATATYPE.
557DATE-TIME-STRING should be in ISO 8601 basic or extended format. 556DATE-TIME-STRING should be in ISO 8601 basic or extended format.
558DATATYPE is one of dateTime, time, date, gYearMonth, gYear, 557DATATYPE can be omitted, or one of the symbols dateTime, time,
559gMonthDay, gDay or gMonth. 558date, gYearMonth, gYear, gMonthDay, gDay, or gMonth. If Emacs is
560 559a version that supports fractional seconds, DATATYPE can also be
561Return a list in a format (SEC MINUTE HOUR DAY MONTH YEAR 560dateTime-subsecond, or time-subsecond. On older versions of
562SEC-FRACTION DATATYPE ZONE). This format is meant to be similar 561Emacs (prior to 27.1), which do not support fractional seconds,
563to that returned by `decode-time' (and compatible with 562leaving DATATYPE nil means that subseconds in DATE-TIME-STRING
564`encode-time'). The differences are the SEC (seconds) 563will be ignored.
565field is always an integer, the DOW (day-of-week) field 564
566is replaced with SEC-FRACTION, a float representing the 565Return a list in a format identical or similar to that returned
567fractional seconds, and the DST (daylight savings time) field is 566by `decode-time'. The returned format is always compatible with
568replaced with DATATYPE, a symbol representing the XSD primitive 567`encode-time'. If DATATYPE is omitted or nil, this function will
569datatype. This symbol can be used to determine which fields 568return a list that has exactly the same format as that returned
570apply and which don't when it's not already clear from context. 569by `decode-time'.
571For example a datatype of `time' means the year, month and day 570
571Note that on versions of Emacs that predate support for
572fractional seconds, `encode-time' will not notice the SUBSECOND
573field so it must be handled specially.
574
575The formats returned by this function are as follows, where _
576means \"should be ignored\":
577
578 DATATYPE | Return format
579------------+----------------------------------------------------------------
580 nil | (SECOND MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF)
581 dateTime | (SECOND MINUTE HOUR DAY MONTH YEAR SUBSECOND dateTime UTCOFF)
582 time | (SECOND MINUTE HOUR _ _ _ SUBSECOND time _)
583 date | (_ _ _ DAY MONTH YEAR _ date _)
584 gYearMonth | (_ _ _ _ MONTH YEAR _ gYearMonth _)
585 gYear | (_ _ _ _ _ YEAR _ gYear _)
586 gMonthDay | (_ _ _ DAY MONTH _ _ gMonthDay _)
587 gDay | (_ _ _ DAY _ _ _ gDay _)
588 gMonth | (_ _ _ _ MONTH _ _ gMonth _)
589
590When DATATYPE is dateTime or time, the DOW (day-of-week) field is
591replaced with SUBSECOND, a float representing the fractional
592seconds, and the DST (daylight savings time) field is replaced
593with DATATYPE, a symbol representing the XSD primitive datatype.
594This symbol can be used to determine which fields apply and which
595do not, when it is not already clear from context. For example a
596datatype of `time' means the year, month, day and time zone
572fields should be ignored. 597fields should be ignored.
573 598
574This function will throw an error if DATE-TIME-STRING represents 599New code that depends on Emacs 27.1 or newer anyway, and that
575a leap second, since the XML Schema 1.1 standard explicitly 600wants dateTime or time but with the first argument with subsecond
576disallows them." 601resolution, i.e., (TICKS . HZ), can set DATATYPE to
577 (let* ((datetime-regexp (cadr (get datatype 'rng-xsd-convert))) 602dateTime-subsecond or time-subsecond respectively. This function
603throws an error if dateTime-subsecond or time-subsecond is
604specified when Emacs does not support subsecond resolution.
605
606This function throws an error if DATE-TIME-STRING represents a
607leap second, since the XML Schema 1.1 standard does not support
608representing leap seconds."
609 (let* ((new-decode-time (condition-case nil
610 (not (null
611 (with-no-warnings (decode-time nil nil t))))
612 (wrong-number-of-arguments)))
613 (new-decode-time-second nil)
614 (no-support "This Emacs version does not support %s")
615 (datetime-regexp-type
616 (cl-case datatype
617 ((dateTime-subsecond time-subsecond)
618 (if new-decode-time
619 (intern (replace-regexp-in-string
620 "-subsecond" "" (symbol-name datatype)))
621 (error (format no-support (symbol-name datatype)))))
622 ((nil) 'dateTime)
623 (otherwise datatype)))
624 (datetime-regexp (cadr (get datetime-regexp-type 'rng-xsd-convert)))
578 (year-sign (progn 625 (year-sign (progn
579 (string-match datetime-regexp date-time-string) 626 (string-match datetime-regexp date-time-string)
580 (match-string 1 date-time-string))) 627 (match-string 1 date-time-string)))
@@ -585,6 +632,7 @@ disallows them."
585 (minute (match-string 6 date-time-string)) 632 (minute (match-string 6 date-time-string))
586 (second (match-string 7 date-time-string)) 633 (second (match-string 7 date-time-string))
587 (second-fraction (match-string 8 date-time-string)) 634 (second-fraction (match-string 8 date-time-string))
635 (time-zone nil)
588 (has-time-zone (match-string 9 date-time-string)) 636 (has-time-zone (match-string 9 date-time-string))
589 (time-zone-sign (match-string 10 date-time-string)) 637 (time-zone-sign (match-string 10 date-time-string))
590 (time-zone-hour (match-string 11 date-time-string)) 638 (time-zone-hour (match-string 11 date-time-string))
@@ -605,11 +653,28 @@ disallows them."
605 (if hour (string-to-number hour) 0)) 653 (if hour (string-to-number hour) 0))
606 (setq minute 654 (setq minute
607 (if minute (string-to-number minute) 0)) 655 (if minute (string-to-number minute) 0))
656 (when new-decode-time
657 (setq new-decode-time-second
658 (if second
659 (if second-fraction
660 (let* ((second-fraction-significand
661 (replace-regexp-in-string "\\." "" second-fraction))
662 (hertz
663 (expt 10 (length second-fraction-significand)))
664 (ticks (+ (* hertz (string-to-number second))
665 (string-to-number
666 second-fraction-significand))))
667 (cons ticks hertz))
668 (cons second 1)))))
608 (setq second 669 (setq second
609 (if second (string-to-number second) 0)) 670 (if second (string-to-number second) 0))
610 (setq second-fraction 671 (setq second-fraction
611 (if second-fraction 672 (if second-fraction
612 (float (string-to-number second-fraction)) 673 (progn
674 (when (and (not datatype) (not new-decode-time))
675 (message
676 "soap-decode-date-time: Discarding fractional seconds"))
677 (float (string-to-number second-fraction)))
613 0.0)) 678 0.0))
614 (setq has-time-zone (and has-time-zone t)) 679 (setq has-time-zone (and has-time-zone t))
615 (setq time-zone-sign 680 (setq time-zone-sign
@@ -618,6 +683,14 @@ disallows them."
618 (if time-zone-hour (string-to-number time-zone-hour) 0)) 683 (if time-zone-hour (string-to-number time-zone-hour) 0))
619 (setq time-zone-minute 684 (setq time-zone-minute
620 (if time-zone-minute (string-to-number time-zone-minute) 0)) 685 (if time-zone-minute (string-to-number time-zone-minute) 0))
686 (setq time-zone (if has-time-zone
687 (* (rng-xsd-time-to-seconds
688 time-zone-hour
689 time-zone-minute
690 0)
691 time-zone-sign)
692 ;; UTC.
693 0))
621 (unless (and 694 (unless (and
622 ;; XSD does not allow year 0. 695 ;; XSD does not allow year 0.
623 (> year 0) 696 (> year 0)
@@ -635,18 +708,22 @@ disallows them."
635 (>= time-zone-minute 0) 708 (>= time-zone-minute 0)
636 (<= time-zone-minute 59)) 709 (<= time-zone-minute 59))
637 (error "Invalid or unsupported time: %s" date-time-string)) 710 (error "Invalid or unsupported time: %s" date-time-string))
638 ;; Return a value in a format similar to that returned by decode-time, and 711 ;; Return a value in a format identical or similar to that
639 ;; suitable for (apply #'encode-time ...). 712 ;; returned by decode-time, and always suitable for (apply
640 ;; FIXME: Nobody uses this idiosyncratic value. Perhaps stop returning it? 713 ;; #'encode-time ...).
641 (list second minute hour day month year second-fraction datatype 714 (if datatype
642 (if has-time-zone 715 (list (if (memq datatype '(dateTime-subsecond time-subsecond))
643 (* (rng-xsd-time-to-seconds 716 new-decode-time-second
644 time-zone-hour 717 second)
645 time-zone-minute 718 minute hour day month year second-fraction datatype time-zone)
646 0) 719 (let ((time
647 time-zone-sign) 720 (apply
648 ;; UTC. 721 #'encode-time (list
649 0)))) 722 (if new-decode-time new-decode-time-second second)
723 minute hour day month year nil nil time-zone))))
724 (if new-decode-time
725 (with-no-warnings (decode-time time nil t))
726 (decode-time time))))))
650 727
651(defun soap-decode-xs-basic-type (type node) 728(defun soap-decode-xs-basic-type (type node)
652 "Use TYPE, a `soap-xs-basic-type', to decode the contents of NODE. 729 "Use TYPE, a `soap-xs-basic-type', to decode the contents of NODE.