Browse Source

org-element: Improve timestamps parsing

* lisp/org-element.el (org-element-timestamp-parser): Modify timestamp
  objects properties.
(org-element-headline-parser, org-element-inlinetask-parser): Remove
`:timestamp' and `:clock' property.  Add `:clockedp' property.  Also,
set `:closed', `:deadline' and `:scheduled' values to timestamp
objects, not strings.  Small refactoring.
(org-element-clock-parser): Rename `:time' property into `:duration'.
Also, set `:value' value as a timestamp object, not a string.
(org-element-planning-parser): Set `:closed', `:deadline' and
`:scheduled' values to timestamp objects, not strings.
(org-element-clock-interpreter, org-element-planning-interpreter)
(org-element-timestamp-interpreter): Update interpreters.
(org-element--current-element): Tiny refactoring.
* testing/lisp/test-org-element.el: Add tests.
Nicolas Goaziou 5 years ago
parent
commit
c1e3aaece3
2 changed files with 374 additions and 152 deletions
  1. 261 94
      lisp/org-element.el
  2. 113 58
      testing/lisp/test-org-element.el

+ 261 - 94
lisp/org-element.el

@@ -116,6 +116,8 @@
 (eval-when-compile (require 'cl))
 (require 'org)
 
+(declare-function org-clocking-buffer "org-clock" ())
+
 
 
 ;;; Definitions And Rules
@@ -711,9 +713,8 @@ Return a list whose CAR is `headline' and CDR is a plist
 containing `:raw-value', `:title', `:begin', `:end',
 `:pre-blank', `:hiddenp', `:contents-begin' and `:contents-end',
 `:level', `:priority', `:tags', `:todo-keyword',`:todo-type',
-`:scheduled', `:deadline', `:timestamp', `:clock', `:category',
-`:quotedp', `:archivedp', `:commentedp' and `:footnote-section-p'
-keywords.
+`:scheduled', `:deadline', `:closed', `:clockedp', `:quotedp',
+`:archivedp', `:commentedp' and `:footnote-section-p' keywords.
 
 The plist also contains any property set in the property drawer,
 with its name in lowercase, the underscores replaced with hyphens
@@ -745,23 +746,36 @@ Assume point is at beginning of the headline."
 				    (string= org-footnote-section raw-value)))
 	   ;; Normalize property names: ":SOME_PROP:" becomes
 	   ;; ":some-prop".
-	   (standard-props (let (plist)
-			     (mapc
-			      (lambda (p)
-				(let ((p-name (downcase (car p))))
-				  (while (string-match "_" p-name)
-				    (setq p-name
-					  (replace-match "-" nil nil p-name)))
-				  (setq p-name (intern (concat ":" p-name)))
-				  (setq plist
-					(plist-put plist p-name (cdr p)))))
-			      (org-entry-properties nil 'standard))
-			     plist))
-	   (time-props (org-entry-properties nil 'special "CLOCK"))
-	   (scheduled (cdr (assoc "SCHEDULED" time-props)))
-	   (deadline (cdr (assoc "DEADLINE" time-props)))
-	   (clock (cdr (assoc "CLOCK" time-props)))
-	   (timestamp (cdr (assoc "TIMESTAMP" time-props)))
+	   (standard-props
+	    (let (plist)
+	      (mapc
+	       (lambda (p)
+		 (setq plist
+		       (plist-put plist
+				  (intern (concat ":"
+						  (replace-regexp-in-string
+						   "_" "-" (downcase (car p)))))
+				  (cdr p))))
+	       (org-entry-properties nil 'standard))
+	      plist))
+	   (time-props
+	    ;; Read time properties on the line below the headline.
+	    (save-excursion
+	      (when (progn (forward-line)
+			   (looking-at org-planning-or-clock-line-re))
+		(let ((end (line-end-position)) plist)
+		  (while (re-search-forward
+			  org-keyword-time-not-clock-regexp end t)
+		    (goto-char (match-end 1))
+		    (skip-chars-forward " \t")
+		    (let ((keyword (match-string 1))
+			  (time (org-element-timestamp-parser)))
+		      (cond ((equal keyword org-scheduled-string)
+			     (setq plist (plist-put plist :scheduled time)))
+			    ((equal keyword org-deadline-string)
+			     (setq plist (plist-put plist :deadline time)))
+			    (t (setq plist (plist-put plist :closed time))))))
+		  plist))))
 	   (begin (point))
 	   (end (save-excursion (goto-char (org-end-of-subtree t t))))
 	   (pos-after-head (progn (forward-line) (point)))
@@ -773,7 +787,13 @@ Assume point is at beginning of the headline."
 			      (progn (goto-char end)
 				     (skip-chars-backward " \r\t\n")
 				     (forward-line)
-				     (point)))))
+				     (point))))
+	   (clockedp (and (eq (org-clocking-buffer)
+			      (or (buffer-base-buffer) (current-buffer)))
+			  (save-excursion
+			    (goto-char (marker-position org-clock-marker))
+			    (org-back-to-heading t)
+			    (= (point) begin)))))
       ;; Clean RAW-VALUE from any quote or comment string.
       (when (or quotedp commentedp)
 	(let ((case-fold-search nil))
@@ -803,10 +823,6 @@ Assume point is at beginning of the headline."
 			  :tags tags
 			  :todo-keyword todo
 			  :todo-type todo-type
-			  :scheduled scheduled
-			  :deadline deadline
-			  :timestamp timestamp
-			  :clock clock
 			  :post-blank (count-lines
 				       (if (not contents-end) pos-after-head
 					 (goto-char contents-end)
@@ -815,8 +831,10 @@ Assume point is at beginning of the headline."
 				       end)
 			  :footnote-section-p footnote-section-p
 			  :archivedp archivedp
+			  :clockedp clockedp
 			  :commentedp commentedp
 			  :quotedp quotedp)
+		    time-props
 		    standard-props))))
 	(org-element-put-property
 	 headline :title
@@ -880,7 +898,7 @@ Return a list whose CAR is `inlinetask' and CDR is a plist
 containing `:title', `:begin', `:end', `:hiddenp',
 `:contents-begin' and `:contents-end', `:level', `:priority',
 `:raw-value', `:tags', `:todo-keyword', `:todo-type',
-`:scheduled', `:deadline', `:timestamp', `:clock' and
+`:scheduled', `:deadline', `:clockedp', `:closed' and
 `:post-blank' keywords.
 
 The plist also contains any property set in the property drawer,
@@ -904,27 +922,45 @@ Assume point is at beginning of the inline task."
 	   (raw-value (or (nth 4 components) ""))
 	   ;; Normalize property names: ":SOME_PROP:" becomes
 	   ;; ":some-prop".
-	   (standard-props (let (plist)
-			     (mapc
-			      (lambda (p)
-				(let ((p-name (downcase (car p))))
-				  (while (string-match "_" p-name)
-				    (setq p-name
-					  (replace-match "-" nil nil p-name)))
-				  (setq p-name (intern (concat ":" p-name)))
-				  (setq plist
-					(plist-put plist p-name (cdr p)))))
-			      (org-entry-properties nil 'standard))
-			     plist))
-	   (time-props (org-entry-properties nil 'special "CLOCK"))
-	   (scheduled (cdr (assoc "SCHEDULED" time-props)))
-	   (deadline (cdr (assoc "DEADLINE" time-props)))
-	   (clock (cdr (assoc "CLOCK" time-props)))
-	   (timestamp (cdr (assoc "TIMESTAMP" time-props)))
+	   (standard-props
+	    (let (plist)
+	      (mapc
+	       (lambda (p)
+		 (setq plist
+		       (plist-put plist
+				  (intern (concat ":"
+						  (replace-regexp-in-string
+						   "_" "-" (downcase (car p)))))
+				  (cdr p))))
+	       (org-entry-properties nil 'standard))
+	      plist))
+	   (time-props
+	    ;; Read time properties on the line below the inlinetask
+	    ;; opening string.
+	    (save-excursion
+	      (when (progn (forward-line)
+			   (looking-at org-planning-or-clock-line-re))
+		(let ((end (line-end-position)) plist)
+		  (while (re-search-forward
+			  org-keyword-time-not-clock-regexp end t)
+		    (goto-char (match-end 1))
+		    (skip-chars-forward " \t")
+		    (let ((keyword (match-string 1))
+			  (time (org-element-timestamp-parser)))
+		      (cond ((equal keyword org-scheduled-string)
+			     (setq plist (plist-put plist :scheduled time)))
+			    ((equal keyword org-deadline-string)
+			     (setq plist (plist-put plist :deadline time)))
+			    (t (setq plist (plist-put plist :closed time))))))
+		  plist))))
 	   (task-end (save-excursion
 		       (end-of-line)
 		       (and (re-search-forward "^\\*+ END" limit t)
 			    (match-beginning 0))))
+	   (clockedp (and (eq (org-clocking-buffer)
+			      (or (buffer-base-buffer) (current-buffer)))
+			  (let ((clock (marker-position org-clock-marker)))
+			    (and (> clock begin) (< clock task-end)))))
 	   (contents-begin (progn (forward-line)
 				  (and task-end (< (point) task-end) (point))))
 	   (hidden (and contents-begin (org-invisible-p2)))
@@ -950,11 +986,9 @@ Assume point is at beginning of the inline task."
 			 :tags tags
 			 :todo-keyword todo
 			 :todo-type todo-type
-			 :scheduled scheduled
-			 :deadline deadline
-			 :timestamp timestamp
-			 :clock clock
+			 :clockedp clockedp
 			 :post-blank (count-lines before-blank end))
+		   time-props
 		   standard-props
 		   (cadr keywords)))))
       (org-element-put-property
@@ -1427,13 +1461,13 @@ as keywords."
     (let* ((case-fold-search nil)
 	   (begin (point))
 	   (value (progn (search-forward org-clock-string (line-end-position) t)
-			 (org-skip-whitespace)
-			 (looking-at "\\[.*\\]")
-			 (org-match-string-no-properties 0)))
-	   (time (and (progn (goto-char (match-end 0))
-			     (looking-at " +=> +\\(\\S-+\\)[ \t]*$"))
-		      (org-match-string-no-properties 1)))
-	   (status (if time 'closed 'running))
+			 (skip-chars-forward " \t")
+			 (org-element-timestamp-parser)))
+	   (duration (and (search-forward " => " (line-end-position) t)
+			  (progn (skip-chars-forward " \t")
+				 (looking-at "\\(\\S-+\\)[ \t]*$"))
+			  (org-match-string-no-properties 1)))
+	   (status (if duration 'closed 'running))
 	   (post-blank (let ((before-blank (progn (forward-line) (point))))
 			 (skip-chars-forward " \r\t\n" limit)
 			 (skip-chars-backward " \t")
@@ -1443,7 +1477,7 @@ as keywords."
       (list 'clock
 	    (list :status status
 		  :value value
-		  :time time
+		  :duration duration
 		  :begin begin
 		  :end end
 		  :post-blank post-blank)))))
@@ -1452,13 +1486,14 @@ as keywords."
   "Interpret CLOCK element as Org syntax.
 CONTENTS is nil."
   (concat org-clock-string " "
-	  (org-element-property :value clock)
-	  (let ((time (org-element-property :time clock)))
-	    (and time
+	  (org-element-timestamp-interpreter
+	   (org-element-property :value clock) nil)
+	  (let ((duration (org-element-property :duration clock)))
+	    (and duration
 		 (concat " => "
 			 (apply 'format
 				"%2s:%02s"
-				(org-split-string time ":")))))))
+				(org-split-string duration ":")))))))
 
 
 ;;;; Comment
@@ -2049,13 +2084,11 @@ and `:post-blank' keywords."
 	   (end (point))
 	   closed deadline scheduled)
       (goto-char begin)
-      (while (re-search-forward org-keyword-time-not-clock-regexp
-				(line-end-position) t)
+      (while (re-search-forward org-keyword-time-not-clock-regexp end t)
 	(goto-char (match-end 1))
-	(org-skip-whitespace)
-	(let ((time (buffer-substring-no-properties
-		     (1+ (point)) (1- (match-end 0))))
-	      (keyword (match-string 1)))
+	(skip-chars-forward " \t" end)
+	(let ((keyword (match-string 1))
+	      (time (org-element-timestamp-parser)))
 	  (cond ((equal keyword org-closed-string) (setq closed time))
 		((equal keyword org-deadline-string) (setq deadline time))
 		(t (setq scheduled time)))))
@@ -2073,13 +2106,18 @@ CONTENTS is nil."
   (mapconcat
    'identity
    (delq nil
-	 (list (let ((closed (org-element-property :closed planning)))
-		 (when closed (concat org-closed-string " [" closed "]")))
-	       (let ((deadline (org-element-property :deadline planning)))
-		 (when deadline (concat org-deadline-string " <" deadline ">")))
+	 (list (let ((deadline (org-element-property :deadline planning)))
+		 (when deadline
+		   (concat org-deadline-string " "
+			   (org-element-timestamp-interpreter deadline nil))))
 	       (let ((scheduled (org-element-property :scheduled planning)))
 		 (when scheduled
-		   (concat org-scheduled-string " <" scheduled ">")))))
+		   (concat org-scheduled-string " "
+			   (org-element-timestamp-interpreter scheduled nil))))
+	       (let ((closed (org-element-property :closed planning)))
+		 (when closed
+		   (concat org-closed-string " "
+			   (org-element-timestamp-interpreter closed nil))))))
    " "))
 
 
@@ -3358,39 +3396,168 @@ Assume point is at the beginning of the timestamp."
   (save-excursion
     (let* ((begin (point))
 	   (activep (eq (char-after) ?<))
-	   (main-value
+	   (raw-value
 	    (progn
-	      (looking-at "[<[]\\(\\(%%\\)?.*?\\)[]>]\\(?:--[<[]\\(.*?\\)[]>]\\)?")
-	      (match-string-no-properties 1)))
-	   (range-end (match-string-no-properties 3))
-	   (type (cond ((match-string 2) 'diary)
-		       ((and activep range-end) 'active-range)
+	      (looking-at "\\([<[]\\(%%\\)?.*?\\)[]>]\\(?:--\\([<[].*?[]>]\\)\\)?")
+	      (match-string-no-properties 0)))
+	   (date-start (match-string-no-properties 1))
+	   (date-end (match-string 3))
+	   (diaryp (match-beginning 2))
+	   (type (cond (diaryp 'diary)
+		       ((and activep date-end) 'active-range)
 		       (activep 'active)
-		       (range-end 'inactive-range)
+		       (date-end 'inactive-range)
 		       (t 'inactive)))
 	   (post-blank (progn (goto-char (match-end 0))
 			      (skip-chars-forward " \t")))
-	   (end (point)))
+	   (end (point))
+	   (with-time-p (string-match "[012]?[0-9]:[0-5][0-9]" date-start))
+	   (repeater-props
+	    (and (not diaryp)
+		 (string-match "\\([.+]?\\+\\)\\([0-9]+\\)\\([hdwmy]\\)>"
+			       raw-value)
+		 (list
+		  :repeater-type
+		  (let ((type (match-string 1 raw-value)))
+		    (cond ((equal "++" type) 'catch-up)
+			  ((equal ".+" type) 'restart)
+			  (t 'cumulate)))
+		  :repeater-value (string-to-number (match-string 2 raw-value))
+		  :repeater-unit
+		  (case (string-to-char (match-string 3 raw-value))
+		    (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (t 'year)))))
+	   time-range year-start month-start day-start hour-start minute-start
+	   year-end month-end day-end hour-end minute-end)
+      ;; Extract time range, if any, and remove it from date start.
+      (setq time-range
+	    (and (not diaryp)
+		 (string-match
+		  "[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
+		  date-start)
+		 (cons (string-to-number (match-string 2 date-start))
+		       (string-to-number (match-string 3 date-start)))))
+      (when time-range
+	(setq date-start (replace-match "" nil nil date-start 1)))
+      ;; Parse date-start.
+      (unless diaryp
+	(let ((date (org-parse-time-string date-start)))
+	  (setq year-start (nth 5 date)
+		month-start (nth 4 date)
+		day-start (nth 3 date)
+		hour-start (and with-time-p (nth 2 date))
+		minute-start (and with-time-p (nth 1 date)))))
+      ;; Compute date-end.  It can be provided directly in time-stamp,
+      ;; or extracted from time range.  Otherwise, it defaults to the
+      ;; same values as date-start.
+      (unless diaryp
+	(let ((date (and date-end (org-parse-time-string date-end))))
+	  (setq year-end (or (nth 5 date) year-start)
+		month-end (or (nth 4 date) month-start)
+		day-end (or (nth 3 date) day-start)
+		hour-end (or (nth 2 date) (car time-range) hour-start)
+		minute-end (or (nth 1 date) (cdr time-range) minute-start))))
       (list 'timestamp
-	    (list :type type
-		  :value main-value
-		  :range-end range-end
-		  :begin begin
-		  :end end
-		  :post-blank post-blank)))))
+	    (nconc (list :type type
+			 :raw-value raw-value
+			 :year-start year-start
+			 :month-start month-start
+			 :day-start day-start
+			 :hour-start hour-start
+			 :minute-start minute-start
+			 :year-end year-end
+			 :month-end month-end
+			 :day-end day-end
+			 :hour-end hour-end
+			 :minute-end minute-end
+			 :begin begin
+			 :end end
+			 :post-blank post-blank)
+		   repeater-props)))))
 
 (defun org-element-timestamp-interpreter (timestamp contents)
   "Interpret TIMESTAMP object as Org syntax.
 CONTENTS is nil."
-  (let ((type (org-element-property :type timestamp) ))
-    (concat
-     (format (if (memq type '(inactive inactive-range)) "[%s]" "<%s>")
-	     (org-element-property :value timestamp))
-     (let ((range-end (org-element-property :range-end timestamp)))
-       (when range-end
-	 (concat "--"
-		 (format (if (eq type 'inactive-range) "[%s]" "<%s>")
-			 range-end)))))))
+  ;; Use `:raw-value' if specified.
+  (or (org-element-property :raw-value timestamp)
+      ;; Otherwise, build timestamp string.
+      (let ((build-ts-string
+	     ;; Build an Org timestamp string from TIME.  ACTIVEP is
+	     ;; non-nil when time stamp is active.  If WITH-TIME-P is
+	     ;; non-nil, add a time part.  HOUR-END and MINUTE-END
+	     ;; specify a time range in the timestamp.  REPEAT-STRING
+	     ;; is the repeater string, if any.
+	     (lambda (time activep
+		      &optional with-time-p hour-end minute-end repeat-string)
+	       (let ((ts (format-time-string
+			  (funcall (if with-time-p 'cdr 'car)
+				   org-time-stamp-formats)
+			  time)))
+		 (when (and hour-end minute-end)
+		   (string-match "[012]?[0-9]:[0-5][0-9]" ts)
+		   (setq ts
+			 (replace-match
+			  (format "\\&-%02d:%02d" hour-end minute-end)
+			  nil nil ts)))
+		 (unless activep (setq ts (format "[%s]" (substring ts 1 -1))))
+		 (when (org-string-nw-p repeat-string)
+		   (setq ts (concat (substring ts 0 -1)
+				    " "
+				    repeat-string
+				    (substring ts -1))))
+		 ;; Return value.
+		 ts)))
+	    (type (org-element-property :type timestamp)))
+	(case type
+	  ((active inactive)
+	   (let* ((minute-start (org-element-property :minute-start timestamp))
+		  (minute-end (org-element-property :minute-end timestamp))
+		  (hour-start (org-element-property :hour-start timestamp))
+		  (hour-end (org-element-property :hour-end timestamp))
+		  (time-range-p (and hour-start hour-end minute-start minute-end
+				     (or (/= hour-start hour-end)
+					 (/= minute-start minute-end)))))
+	     (funcall
+	      build-ts-string
+	      (encode-time 0
+			   (or minute-start 0)
+			   (or hour-start 0)
+			   (org-element-property :day-start timestamp)
+			   (org-element-property :month-start timestamp)
+			   (org-element-property :year-start timestamp))
+	      (eq type 'active)
+	      (and hour-start minute-start)
+	      (and time-range-p hour-end)
+	      (and time-range-p minute-end)
+	      (concat (case (org-element-property :repeater-type timestamp)
+			(cumulate "+") (catch-up "++") (restart ".+"))
+		      (org-element-property :repeater-value timestamp)
+		      (org-element-property :repeater-unit timestamp)))))
+	  ((active-range inactive-range)
+	   (let ((minute-start (org-element-property :minute-start timestamp))
+		 (minute-end (org-element-property :minute-end timestamp))
+		 (hour-start (org-element-property :hour-start timestamp))
+		 (hour-end (org-element-property :hour-end timestamp)))
+	     (concat
+	      (funcall
+	       build-ts-string (encode-time
+				0
+				(or minute-start 0)
+				(or hour-start 0)
+				(org-element-property :day-start timestamp)
+				(org-element-property :month-start timestamp)
+				(org-element-property :year-start timestamp))
+	       (eq type 'active-range)
+	       (and hour-start minute-start))
+	      "--"
+	      (funcall build-ts-string
+		       (encode-time 0
+				    (or minute-end 0)
+				    (or hour-end 0)
+				    (org-element-property :day-end timestamp)
+				    (org-element-property :month-end timestamp)
+				    (org-element-property :year-end timestamp))
+		       (eq type 'active-range)
+		       (and hour-end minute-end)))))))))
 
 (defun org-element-timestamp-successor (limit)
   "Search for the next timestamp object.
@@ -3539,7 +3706,7 @@ element it has to parse."
        ;; a footnote definition: next item is always a paragraph.
        ((not (bolp)) (org-element-paragraph-parser limit (list (point))))
        ;; Planning and Clock.
-       ((and (looking-at org-planning-or-clock-line-re))
+       ((looking-at org-planning-or-clock-line-re)
 	(if (equal (match-string 1) org-clock-string)
 	    (org-element-clock-parser limit)
 	  (org-element-planning-parser limit)))

+ 113 - 58
testing/lisp/test-org-element.el

@@ -278,22 +278,24 @@ Some other text
   ;; Running clock.
   (let* ((org-clock-string "CLOCK:")
 	 (clock (org-test-with-temp-text "CLOCK: [2012-01-01 sun. 00:01]"
-		  (org-element-map
-		   (org-element-parse-buffer) 'clock 'identity nil t))))
+		  (org-element-at-point))))
     (should (eq (org-element-property :status clock) 'running))
-    (should (equal (org-element-property :value clock)
-		   "[2012-01-01 sun. 00:01]"))
-    (should-not (org-element-property :time clock)))
+    (should
+     (equal (org-element-property :raw-value
+				  (org-element-property :value clock))
+	    "[2012-01-01 sun. 00:01]"))
+    (should-not (org-element-property :duration clock)))
   ;; Closed clock.
   (let* ((org-clock-string "CLOCK:")
-	 (clock (org-test-with-temp-text "
-CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"
-		  (org-element-map
-		   (org-element-parse-buffer) 'clock 'identity nil t))))
+	 (clock
+	  (org-test-with-temp-text
+	      "CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"
+	    (org-element-at-point))))
     (should (eq (org-element-property :status clock) 'closed))
-    (should (equal (org-element-property :value clock)
+    (should (equal (org-element-property :raw-value
+					 (org-element-property :value clock))
 		   "[2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02]"))
-    (should (equal (org-element-property :time clock) "0:01"))))
+    (should (equal (org-element-property :duration clock) "0:01"))))
 
 
 ;;;; Code
@@ -1382,26 +1384,29 @@ Outside list"
 (ert-deftest test-org-element/planning-parser ()
   "Test `planning' parser."
   (should
-   (equal
-    (org-element-property
-     :closed
-     (org-test-with-temp-text "CLOSED: [2012-03-29 thu.]"
-       (org-element-map (org-element-parse-buffer) 'planning 'identity nil t)))
-    "2012-03-29 thu."))
+   (equal "[2012-03-29 thu.]"
+	  (org-element-property
+	   :raw-value
+	   (org-element-property
+	    :closed
+	    (org-test-with-temp-text "CLOSED: [2012-03-29 thu.]"
+	      (org-element-at-point))))))
   (should
-   (equal
-    (org-element-property
-     :deadline
-     (org-test-with-temp-text "DEADLINE: <2012-03-29 thu.>"
-       (org-element-map (org-element-parse-buffer) 'planning 'identity nil t)))
-    "2012-03-29 thu."))
+   (equal "<2012-03-29 thu.>"
+	  (org-element-property
+	   :raw-value
+	   (org-element-property
+	    :deadline
+	    (org-test-with-temp-text "DEADLINE: <2012-03-29 thu.>"
+	      (org-element-at-point))))))
   (should
-   (equal
-    (org-element-property
-     :scheduled
-     (org-test-with-temp-text "SCHEDULED: <2012-03-29 thu.>"
-       (org-element-map (org-element-parse-buffer) 'planning 'identity nil t)))
-    "2012-03-29 thu.")))
+   (equal "<2012-03-29 thu.>"
+	  (org-element-property
+	   :raw-value
+	   (org-element-property
+	    :scheduled
+	    (org-test-with-temp-text "SCHEDULED: <2012-03-29 thu.>"
+	      (org-element-at-point)))))))
 
 
 ;;;; Property Drawer
@@ -1707,27 +1712,41 @@ Outside list"
   ;; Active timestamp.
   (should
    (org-test-with-temp-text "<2012-03-29 16:40>"
-     (eq (org-element-property :type
-			       (org-element-map
-				(org-element-parse-buffer)
-				'timestamp 'identity nil t))
-	 'active)))
+     (eq (org-element-property :type (org-element-context)) 'active)))
+  (should
+   (equal '(2012 3 29 16 40)
+	  (org-test-with-temp-text "<2012-03-29 16:40>"
+	    (let ((object (org-element-context)))
+	      (list (org-element-property :year-start object)
+		    (org-element-property :month-start object)
+		    (org-element-property :day-start object)
+		    (org-element-property :hour-start object)
+		    (org-element-property :minute-start object))))))
   ;; Inactive timestamp.
   (should
    (org-test-with-temp-text "[2012-03-29 16:40]"
-     (eq (org-element-property :type
-			       (org-element-map
-				(org-element-parse-buffer)
-				'timestamp 'identity nil t))
-	 'inactive)))
+     (eq (org-element-property :type (org-element-context)) 'inactive)))
+  ;; Time range.
+  (should
+   (equal '(2012 3 29 16 40 7 30)
+	  (org-test-with-temp-text "<2012-03-29 7:30-16:40>"
+	    (let ((object (org-element-context)))
+	      (list (org-element-property :year-end object)
+		    (org-element-property :month-end object)
+		    (org-element-property :day-end object)
+		    (org-element-property :hour-end object)
+		    (org-element-property :minute-end object)
+		    (org-element-property :hour-start object)
+		    (org-element-property :minute-start object))))))
   ;; Date range.
   (should
    (org-test-with-temp-text "[2012-03-29 16:40]--[2012-03-29 16:41]"
-     (eq (org-element-property :type
-			       (org-element-map
-				(org-element-parse-buffer)
-				'timestamp 'identity nil t))
-	 'inactive-range)))
+     (eq (org-element-property :type (org-element-context)) 'inactive-range)))
+  ;; With repeater.
+  (should
+   (eq 'catch-up
+       (org-test-with-temp-text "<2012-03-29 ++1y>"
+	 (org-element-property :repeater-type (org-element-context)))))
   ;; Timestamps are not planning elements.
   (should-not
    (org-test-with-temp-text "SCHEDULED: <2012-03-29 16:40>"
@@ -2102,9 +2121,9 @@ CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"))
      (equal
       (org-test-parse-and-interpret
        "* Headline
-CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>")
+DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01> CLOSED: [2012-01-01]")
       "* Headline
-CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>\n"))))
+DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01> CLOSED: [2012-01-01]\n"))))
 
 (ert-deftest test-org-element/property-drawer-interpreter ()
   "Test property drawer interpreter."
@@ -2159,25 +2178,61 @@ CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>\n"))))
 (ert-deftest test-org-element/timestamp-interpreter ()
   "Test timestamp interpreter."
   ;; Active.
-  (should (equal (org-test-parse-and-interpret "<2012-03-29 16:40>")
-		 "<2012-03-29 16:40>\n"))
+  (should (equal (org-test-parse-and-interpret "<2012-03-29 thu. 16:40>")
+		 "<2012-03-29 thu. 16:40>\n"))
+  (should
+   (string-match "<2012-03-29 .* 16:40>"
+		 (org-element-timestamp-interpreter
+		  '(timestamp
+		    (:type active :year-start 2012 :month-start 3 :day-start 29
+			   :hour-start 16 :minute-start 40)) nil)))
   ;; Inactive.
-  (should (equal (org-test-parse-and-interpret "[2012-03-29 16:40]")
-		 "[2012-03-29 16:40]\n"))
+  (should (equal (org-test-parse-and-interpret "[2012-03-29 thu. 16:40]")
+		 "[2012-03-29 thu. 16:40]\n"))
+  (should
+   (string-match
+    "\\[2012-03-29 .* 16:40\\]"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type inactive :year-start 2012 :month-start 3 :day-start 29
+	      :hour-start 16 :minute-start 40)) nil)))
   ;; Active range.
   (should (equal (org-test-parse-and-interpret
-		  "<2012-03-29 16:40>--<2012-03-29 16:41>")
-		 "<2012-03-29 16:40>--<2012-03-29 16:41>\n"))
+		  "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>")
+		 "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>\n"))
+  (should
+   (string-match
+    "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type active-range :year-start 2012 :month-start 3 :day-start 29
+	      :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3
+	      :day-end 29 :hour-end 16 :minute-end 41)) nil)))
   ;; Inactive range.
   (should (equal (org-test-parse-and-interpret
-		  "[2012-03-29 16:40]--[2012-03-29 16:41]")
-		 "[2012-03-29 16:40]--[2012-03-29 16:41]\n"))
+		  "[2012-03-29 thu. 16:40]--[2012-03-29 thu. 16:41]")
+		 "[2012-03-29 thu. 16:40]--[2012-03-29 thu. 16:41]\n"))
+  (should
+   (string-match
+    "\\[2012-03-29 .* 16:40\\]--\\[2012-03-29 .* 16:41\\]"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type inactive-range :year-start 2012 :month-start 3 :day-start 29
+	      :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3
+	      :day-end 29 :hour-end 16 :minute-end 41)) nil)))
   ;; Diary.
-  (should (equal (org-test-parse-and-interpret "<%%org-float t 4 2>")
-		 "<%%org-float t 4 2>\n"))
+  (should (equal (org-test-parse-and-interpret "<%%(org-float t 4 2)>")
+		 "<%%(org-float t 4 2)>\n"))
   ;; Timestamp with repeater interval.
-  (should (equal (org-test-parse-and-interpret "<2012-03-29 +1y>")
-		 "<2012-03-29 +1y>\n")))
+  (should (equal (org-test-parse-and-interpret "<2012-03-29 thu. +1y>")
+		 "<2012-03-29 thu. +1y>\n"))
+  (should
+   (string-match
+    "<2012-03-29 .* \\+1y>"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type active :year-start 2012 :month-start 3 :day-start 29
+	      :repeater-type cumulate :repeater-value "1y")) nil))))
 
 (ert-deftest test-org-element/verse-block-interpreter ()
   "Test verse block interpretation."