From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp11.migadu.com ([2001:41d0:403:478a::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms9.migadu.com with LMTPS id AFj2GpRWrWSl8QAASxT56A (envelope-from ) for ; Tue, 11 Jul 2023 15:18:12 +0200 Received: from aspmx1.migadu.com ([2001:41d0:403:478a::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp11.migadu.com with LMTPS id GNozG5RWrWR5DAEA9RJhRA (envelope-from ) for ; Tue, 11 Jul 2023 15:18:12 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id B289B4B6C9 for ; Tue, 11 Jul 2023 15:18:11 +0200 (CEST) Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20221208 header.b=G5gjIKcg; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1689081492; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=8VYZfgQonoaUu3D+jPNAi++DHExQNKkCt6l3yBjeACI=; b=scAPitEXiLPqPIDlD8FLEL1saFJPLmY9GTGnVFeZ6LM0ZqNMa6zbsIA7bLF9ATbQL3ifPr Jjg9E5J0MwATpFJdynGLV2mv1189e1+SZ0jAJ5ONn47ejzkBPEuoeB/AhBDDm0jlw/d4Da rTcTftCSOkUgxQfmw6F3u/5ffr9sAxISRfsO9Vjn63UNaHld41IAx7NjZDQnjOMpV5Pd37 6/b1GoLD4+m23cswQw6D8uJR0BIw8lNH2S0oYTVkMTsMih07NXlPURoDKQo4n2TnZ5aCpy oF1j/eglFd4Q1CtlZkRCx2TO1LpQx9QGZ9l8xBiwE5nAVewyYbEEoJL8Op0tUA== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20221208 header.b=G5gjIKcg; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=key1; d=yhetil.org; t=1689081492; a=rsa-sha256; cv=none; b=G7CtEZYV2gArtGjJWzIjfo8oVs09+TK25n3uM4zQRijjFfspqGNtPjeo7g5yw5SIpwKPlQ qywhkTs43yoL1LkxBVQKqdFnwwQ/KFHMh6o1Z+28NWnahKp4nCUmrGnKDNnV6ES3gmdmDL 8D+vuLfUv8X/9FGduyVrEMMkFUcgNclVKqRq/E7NaQ9o+1IuAcN51Y3JSnGA+XcQ7T6OEY mflIK84e3vNNcWlmcWOJXwHBinAEI70yZgq/C8cpZkXOlARTUrsdCZUK3xrd3k7ofExmAu depheAtJ8OrDy3eQGOTdVWcKOoC6oDt9b/OusHObPzO4wx8qp6+6hOojgkmbng== Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qJDEq-0001YZ-AX; Tue, 11 Jul 2023 09:17:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qJDEm-0001YL-9S for emacs-orgmode@gnu.org; Tue, 11 Jul 2023 09:17:04 -0400 Received: from mail-lf1-x136.google.com ([2a00:1450:4864:20::136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qJDEg-0005iG-Vl for emacs-orgmode@gnu.org; Tue, 11 Jul 2023 09:17:03 -0400 Received: by mail-lf1-x136.google.com with SMTP id 2adb3069b0e04-4fa48b5dc2eso8979825e87.1 for ; Tue, 11 Jul 2023 06:16:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1689081416; x=1691673416; h=mime-version:message-id:date:references:in-reply-to:subject:cc:to :from:from:to:cc:subject:date:message-id:reply-to; bh=8VYZfgQonoaUu3D+jPNAi++DHExQNKkCt6l3yBjeACI=; b=G5gjIKcgbnMNoazQYG1U1nM1Y0ofnCr/xCC300EyQhom3rc6jfKrP4lEofwRSedUez a/LnaZFvrmYRUL9oheCckle1+eGK+DBsOujcPrtMmlen88pqOHnuCxUSln9rHk6Wbh4C G/GMLQ45Ah5B4DxqBh4IBeS2JSXewPZKWqWfkuAVHgB9Dy0SFTcGgHAfwsVRVIY8XE0X VQ032jk3j9vCc0bmMDG/1YcScsBLTHXlgN5SXNKoXI7xbjYdH8+zbbOalVXZbdkI+76B a+Cw0E2rzFteBOjV+bftHM8gPtYclSGcduVmbD/rO+RuekiCr/3yGmFpBS1guI8TGDAV wW/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689081416; x=1691673416; h=mime-version:message-id:date:references:in-reply-to:subject:cc:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=8VYZfgQonoaUu3D+jPNAi++DHExQNKkCt6l3yBjeACI=; b=emzKMIcjp609stf31QqjgQoxwnGKGWv7G5Rma9DNi4D/wx4+J3gPtgYrl7bWB1tZfB EMW00rWe0FLJ9vl0yurBcTLXUEI/gKnjNGfhlrWPN/Sr6rpnCOrdbnhQC2saVE/j+oSK 1JXR5RENTlhjnqH7pIvTyWPuS6qUcbQXzMeb+PNTyvC8mCxUk729GewFvfzk8oXTdpXq YpA4pITHMskWkruHxSyfLSf/4QFsDegrrV9HzTUjTBeApsvoYQaciefkV1MntikQaJ9P PbvkOo+4BzHIGAuuwgoDj/s6rSwFELAmhXxrjj6tNXFpZ+SJ+rgMTy9qTvyjr7X/40Rn lYBQ== X-Gm-Message-State: ABy/qLYhkBJwnkdadE0VDMvEaTSLjzA6X/LRmEGia3mp2p1HG9vtDaht mRYhm6EMxpN25o7i59xL34JlQtTmCrY= X-Google-Smtp-Source: APBJJlGO/vArSNqDwsw7MMux4hCjD6RVg9UG6XwSGwKCUJThGCH0Ftl0sF+oUWx24kWyOvfHuWOpMQ== X-Received: by 2002:a05:6512:15a3:b0:4fb:89f2:594c with SMTP id bp35-20020a05651215a300b004fb89f2594cmr15056588lfb.56.1689081415589; Tue, 11 Jul 2023 06:16:55 -0700 (PDT) Received: from sonyvaio ([92.127.245.54]) by smtp.gmail.com with ESMTPSA id j14-20020ac253ae000000b004fb915e8b9csm318090lfh.53.2023.07.11.06.16.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 Jul 2023 06:16:54 -0700 (PDT) From: Ilya Chernyshov To: Ihor Radchenko Cc: emacs-orgmode Subject: Re: [PATCH] org-element-timestamp-interpreter: Return daterange anyway, if DATERANGE is non-nil In-Reply-To: <87o7kiom6i.fsf@localhost> References: <87y1ot6dqz.fsf@gmail.com> <87wn4cegt4.fsf@localhost> <9E22693E-7D20-470A-B57B-EA17BBE9160F@gmail.com> <87a616c5do.fsf@localhost> <87wmzi4s6n.fsf@localhost> <87edlkyyit.fsf@gmail.com> <87ttueajih.fsf@localhost> <87edlfljcx.fsf@gmail.com> <87o7kiom6i.fsf@localhost> Date: Tue, 11 Jul 2023 20:16:50 +0700 Message-ID: <87wmz6ziyl.fsf@gmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2a00:1450:4864:20::136; envelope-from=ichernyshovvv@gmail.com; helo=mail-lf1-x136.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Migadu-Queue-Id: B289B4B6C9 X-Migadu-Spam-Score: -6.47 X-Migadu-Scanner: mx0.migadu.com X-Spam-Score: -6.47 X-TUID: 62nsNflrW+re --=-=-= Content-Type: text/plain Ihor Radchenko writes: > The patch looks good in general, but there is at least one test failing > when I run make test. May you please check? Inside `org-timestamp-split-range', there was an attempt to intepret a timestamp object with :type `active'/`inactive' and :range-type `daterange'/`timerange' which caused an error. Fixed by setting :range-type to nil before `(org-element-interpret-data split-ts)'. > Also, see the attached diff where I suggest some comments to explain the > code logic. Looks good to me, I just changed a note about `org-element-timestamp-parser' setting nil for end part if :type is `active'/`inactive'. That's not true. The actual behavior is that it sets end date/time to the same values as start date/time even if it's `active'/`inactive'. So here is the patch: --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-lisp-org-element.el-Add-new-timestamp-property-range.patch >From 823e7f39d33977854605485fcae814af0a3fdefe Mon Sep 17 00:00:00 2001 From: Ilya Chernyshov Date: Sat, 18 Feb 2023 14:55:39 +0700 Subject: [PATCH] lisp/org-element.el: Add new timestamp property :range-type * lisp/org-element.el (org-element-timestamp-interpreter): Preserve old behavior when :range-type is `nil'. Take into account :range-type value when interpreting ranges. When :range-type is `timerange', return a timerange (). If :range-type is `daterange' return a daterange (<...>--<...>). When :range-type is nil, return a daterange (as it was before). When :range-type is `daterange' or `timerange' and :type is `active'/`inactive', throw an error. (org-element-timestamp-parser): Add :range-type property. * lisp/org.el (org-timestamp-split-range): Make sure that :range-type is nil for a split timestamp. * testing/lisp/test-org-element.el (test-org-element/timestamp-interpreter): Add new tests. (test-org-element/timestamp-parser): Add tests for :range-type property. * etc/ORG-NEWS (Major changes and additions to Org API): Add news about this property. --- etc/ORG-NEWS | 7 + lisp/org-element.el | 215 +++++++++++++++------------- lisp/org.el | 1 + testing/lisp/test-org-element.el | 231 ++++++++++++++++++++++++++++++- 4 files changed, 355 insertions(+), 99 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 973a97a2f..a4725ae8c 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -200,6 +200,13 @@ a newly created one. Previously, one had to use : (apply #'org-element-create 'section nil (org-element-contents node)) +**** New property ~:range-type~ for org-element timestamp object + +~org-element-timestamp-parser~ now adds =:range-type= property to each +timestamp object. Possible values: ~timerange~, ~daterange~, ~nil~. + +~org-element-timestamp-interpreter~ takes into account this property +and returns an appropriate timestamp string. *** ~org-priority=show~ command no longer adjusts for scheduled/deadline diff --git a/lisp/org-element.el b/lisp/org-element.el index 1c9707573..075f64920 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -4043,7 +4043,7 @@ Assume point is at the target." "Parse time stamp at point, if any. When at a time stamp, return a new syntax node of `timestamp' type -containing `:type', `:raw-value', `:year-start', `:month-start', +containing `:type', `:range-type', `:raw-value', `:year-start', `:month-start', `:day-start', `:hour-start', `:minute-start', `:year-end', `:month-end', `:day-end', `:hour-end', `:minute-end', `:repeater-type', `:repeater-value', `:repeater-unit', @@ -4077,6 +4077,10 @@ Assume point is at the beginning of the timestamp." (activep 'active) ((or date-end time-range) 'inactive-range) (t 'inactive))) + (range-type (cond + (date-end 'daterange) + (time-range 'timerange) + (t nil))) (repeater-props (and (not diaryp) (string-match "\\([.+]?\\+\\)\\([0-9]+\\)\\([hdwmy]\\)" @@ -4123,6 +4127,7 @@ Assume point is at the beginning of the timestamp." (org-element-create 'timestamp (nconc (list :type type + :range-type range-type :raw-value raw-value :year-start year-start :month-start month-start @@ -4142,99 +4147,121 @@ Assume point is at the beginning of the timestamp." (defun org-element-timestamp-interpreter (timestamp _) "Interpret TIMESTAMP object as Org syntax." - (let* ((repeat-string - (concat - (pcase (org-element-property :repeater-type timestamp) - (`cumulate "+") (`catch-up "++") (`restart ".+")) - (let ((val (org-element-property :repeater-value timestamp))) - (and val (number-to-string val))) - (pcase (org-element-property :repeater-unit timestamp) - (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) - (warning-string - (concat - (pcase (org-element-property :warning-type timestamp) - (`first "--") (`all "-")) - (let ((val (org-element-property :warning-value timestamp))) - (and val (number-to-string val))) - (pcase (org-element-property :warning-unit timestamp) - (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) - (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) - (let ((ts (format-time-string - (org-time-stamp-format with-time-p) - 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)))) - (dolist (s (list repeat-string warning-string)) - (when (org-string-nw-p s) - (setq ts (concat (substring ts 0 -1) - " " - s - (substring ts -1))))) - ;; Return value. - ts))) - (type (org-element-property :type timestamp))) - (pcase type - ((or `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 - (org-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)))) - ((or `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 (org-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 - (org-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))))) - (_ (org-element-property :raw-value timestamp))))) - - + (let((type (org-element-property :type timestamp))) + (if (member type '(active inactive inactive-range active-range)) + (let ((day-start (org-element-property :day-start timestamp)) + (month-start (org-element-property :month-start timestamp)) + (year-start (org-element-property :year-start timestamp))) + ;; Return nil when start date is not available. Could also + ;; throw an error, but the current behavior is historical. + (when (and day-start month-start year-start) + (let* ((repeat-string + (concat + (pcase (org-element-property :repeater-type timestamp) + (`cumulate "+") (`catch-up "++") (`restart ".+")) + (let ((val (org-element-property :repeater-value timestamp))) + (and val (number-to-string val))) + (pcase (org-element-property :repeater-unit timestamp) + (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) + (range-type (org-element-property :range-type timestamp)) + (warning-string + (concat + (pcase (org-element-property :warning-type timestamp) + (`first "--") (`all "-")) + (let ((val (org-element-property :warning-value timestamp))) + (and val (number-to-string val))) + (pcase (org-element-property :warning-unit timestamp) + (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) + (hour-start (org-element-property :hour-start timestamp)) + (minute-start (org-element-property :minute-start timestamp)) + (brackets + (if (member + type + '(inactive inactive-range)) + (cons "[" "]") + (cons "<" ">"))) + (timestamp-end + (concat + (and (org-string-nw-p repeat-string) (concat " " repeat-string)) + (and (org-string-nw-p warning-string) (concat " " warning-string)) + (cdr brackets)))) + (concat + ;; Opening backet: [ or < + (car brackets) + ;; Starting date/time: YYYY-MM-DD DAY[ HH:MM] + (format-time-string + ;; `org-time-stamp-formats'. + (org-time-stamp-format + ;; Ignore time unless both HH:MM are available. + ;; Ignore means (car org-timestamp-formats). + (and minute-start hour-start) + 'no-brackets) + (org-encode-time + 0 (or minute-start 0) (or hour-start 0) + day-start month-start year-start)) + ;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM] + (let ((hour-end (org-element-property :hour-end timestamp)) + (minute-end (org-element-property :minute-end timestamp))) + (pcase type + ((or `active `inactive) + ;; `org-element-timestamp-parser' uses this type + ;; when no time/date range is provided. So, + ;; should normally return nil in this clause. + (pcase range-type + (`nil + ;; `org-element-timestamp-parser' assigns end times for `active'/`inactive' TYPE + ;; if start time is not nil. But manually built timestamps + ;; may not contain end times, so check for end times anyway. + (when (and hour-start hour-end minute-start minute-end + (or (/= hour-start hour-end) + (/= minute-start minute-end))) + ;; Could also throw an error. Return range + ;; timestamp nevertheless to preserve + ;; historical behavior. + (format "-%02d:%02d" hour-end minute-end))) + ((or `timerange `daterange) + (error "`:range-type' must be `nil' for `active'/`inactive' type")))) + ;; Range must be present. + ((or `active-range `inactive-range) + (pcase range-type + ;; End time: -HH:MM. + ;; Fall back to start time if end time is not defined (arbitrary historical choice). + ;; Error will be thrown if both end and begin time is not defined. + (`timerange (format "-%02d:%02d" (or hour-end hour-start) (or minute-end minute-start))) + ;; End date: TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM + ((or `daterange + ;; Should never happen in the output of `org-element-timestamp-parser'. + ;; Treat as an equivalent of `daterange' arbitrarily. + `nil) + (concat + ;; repeater + warning + closing > or ] + ;; This info is duplicated in date ranges. + timestamp-end + "--" (car brackets) + (format-time-string + ;; `org-time-stamp-formats'. + (org-time-stamp-format + ;; Ignore time unless both HH:MM are available. + ;; Ignore means (car org-timestamp-formats). + (and minute-end hour-end) + 'no-brackets) + (org-encode-time + ;; Closing HH:MM missing is a valid scenario. + 0 (or minute-end 0) (or hour-end 0) + ;; YEAR/MONTH/DAY-END will always be present + ;; for `daterange' range-type, as parsed by + ;; `org-element-timestamp-parser'. + ;; For manually constructed timestamp + ;; object, arbitrarily fall back to starting + ;; date. + (or (org-element-property :day-end timestamp) day-start) + (or (org-element-property :month-end timestamp) month-start) + (or (org-element-property :year-end timestamp) year-start))))))))) + ;; repeater + warning + closing > or ] + ;; This info is duplicated in date ranges. + timestamp-end)))) + ;; diary type. + (org-element-property :raw-value timestamp)))) ;;;; Underline (defun org-element-underline-parser () diff --git a/lisp/org.el b/lisp/org.el index 62278ec77..590f8ab96 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -20043,6 +20043,7 @@ Return a new timestamp object." ;; Set new type. (org-element-put-property split-ts :type (if (eq type 'active-range) 'active 'inactive)) + (org-element-put-property split-ts :range-type nil) ;; Copy start properties over end properties if END is ;; non-nil. Otherwise, copy end properties over `start' ones. (let ((p-alist '((:minute-start . :minute-end) diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index 283ade10f..2e3a249ab 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -3138,8 +3138,73 @@ Outside list" (org-test-with-temp-text "<2012-03-29 Thu +1y -1y>" (let ((ts (org-element-context))) (list (org-element-property :repeater-type ts) - (org-element-property :warning-type ts))))))) - + (org-element-property :warning-type ts)))))) + ;; :range-type property + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun>" + (org-element-property :range-type (org-element-timestamp-parser))) + nil)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + nil)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00-13:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'timerange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00-12:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'timerange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun>--<2023-07-02 Sun>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun>--<2023-07-03 Mon>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun 12:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-03 Mon 13:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-03 Mon>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun 13:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00>--<2023-07-02 Sun>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange)) + (should + (eq + (org-test-with-temp-text "<2023-07-02 Sun 12:00 +5d>--<2023-07-02 Sun 13:00>" + (org-element-property :range-type (org-element-timestamp-parser))) + 'daterange))) ;;;; Underline @@ -3685,6 +3750,14 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu '(timestamp (:type active :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40)) nil))) + (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 :year-end 2012 :month-end 3 + :day-end 29 :hour-end 16 :minute-end 40)) nil))) ;; Inactive. (should (string-match "\\[2012-03-29 .* 16:40\\]" @@ -3696,11 +3769,45 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu '(timestamp (:type inactive :year-start 2012 :month-start 3 :day-start 29 :hour-start 16 :minute-start 40)) nil))) - ;; Active range. + ;; Active daterange. (should (string-match "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" (org-test-parse-and-interpret "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>"))) + ;;; No end time, dates are not equal + (should + ;; Expected result: "<2012-03-29 Thu 16:40>--<2012-03-30 Fri>" + (string= + (format + "<%s>--<%s>" + (format-time-string (cdr org-time-stamp-formats) (org-encode-time 0 40 16 29 03 2012)) + (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 30 03 2012))) + (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 30)) nil))) + ;;; No start time, dates are not equal + (should + ;; Expected result: "<2012-03-29 Thu>--<2012-03-30 Fri 16:40>" + (string= + (format + "<%s>--<%s>" + (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 29 03 2012)) + (format-time-string (cdr org-time-stamp-formats) (org-encode-time 0 40 16 30 03 2012))) + (org-element-timestamp-interpreter + '(timestamp + (:type active-range :year-start 2012 :month-start 3 :day-start 29 + :hour-end 16 :minute-end 40 :year-end 2012 :month-end 3 + :day-end 30)) nil))) + (should + (string-match + "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:40>" + (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 40)) nil))) (should (string-match "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>" @@ -3709,7 +3816,7 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu (: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. + ;; Inactive daterange. (should (string-match "\\[2012-03-29 .* 16:40\\]--\\[2012-03-29 .* 16:41\\]" (org-test-parse-and-interpret @@ -3722,6 +3829,11 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu (: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))) + ;; Active timerange + (should + (string-match "<2012-03-29 .* 16:40-16:41>" + (org-test-parse-and-interpret + "<2012-03-29 thu. 16:40-16:41>"))) ;; Diary. (should (equal (org-test-parse-and-interpret "<%%diary-float t 4 2>") "<%%diary-float t 4 2>\n")) @@ -3767,7 +3879,116 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu (:type active-range :year-start 2012 :month-start 3 :day-start 29 :year-end 2012 :month-end 3 :day-end 30 :repeater-type cumulate :repeater-value 1 :repeater-unit year)) - nil)))) + nil))) + ;; Tests for :range-type property + ;;; Errors + (should-error + (org-element-timestamp-interpreter + '(timestamp + (:range-type timerange + :type active + :year-start 2023 :month-start 7 :day-start 10 + :year-end 2023 :month-end 7 :day-end 10 + :hour-start 17 :minute-start 30 + :hour-end 17 :minute-end 30)) + nil)) + (should-error + (org-element-timestamp-interpreter + '(timestamp + (:range-type daterange + :type active :year-start 2023 :month-start 7 :day-start 10 + :hour-start 17 :minute-start 30)) nil)) + (should-error + (org-element-timestamp-interpreter + '(timestamp + (:range-type timerange + :type inactive + :year-start 2023 :month-start 7 :day-start 10 + :year-end 2023 :month-end 7 :day-end 10 + :hour-start 17 :minute-start 30 + :hour-end 17 :minute-end 30)) + nil)) + (should-error + (org-element-timestamp-interpreter + '(timestamp + (:range-type daterange + :type inactive :year-start 2023 :month-start 7 :day-start 10 + :hour-start 17 :minute-start 30)) nil)) + + ;;; End part is nil + (should + ;; Expected result: "<2023-07-10 Mon>--<2023-07-10 Mon>" + (string= + (format + "<%s>--<%s>" + (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 10 7 2023)) + (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 10 7 2023))) + (org-element-timestamp-interpreter + '(timestamp + (:range-type daterange + :type active-range :year-start 2023 :month-start 7 :day-start 10)) nil))) + (should + (string-match "<2023-07-10 .* 17:30-17:30>" + (org-element-timestamp-interpreter + '(timestamp + (:range-type timerange + :type active-range :year-start 2023 :month-start 7 :day-start 10 + :hour-start 17 :minute-start 30)) nil))) + (should + ;; Expected result: "<2023-07-10 Mon 17:30>--<2023-07-10 Mon>" + (string= + (format + "<%s>--<%s>" + (format-time-string (cdr org-time-stamp-formats) (org-encode-time 0 30 17 10 7 2023)) + (format-time-string (car org-time-stamp-formats) (org-encode-time 0 0 0 10 7 2023))) + (org-element-timestamp-interpreter + '(timestamp + (:range-type daterange + :type active-range :year-start 2023 :month-start 7 :day-start 10 + :hour-start 17 :minute-start 30)) nil))) + ;;; End is equal to start + (should + (string-match "<2023-07-10 .* 17:30-17:30>" + (org-element-timestamp-interpreter + '(timestamp + (:range-type timerange + :type active-range + :year-start 2023 :month-start 7 :day-start 10 + :year-end 2023 :month-end 7 :day-end 10 + :hour-start 17 :minute-start 30 + :hour-end 17 :minute-end 30)) nil))) + (should + (string-match "<2023-07-10 .* 17:30>--<2023-07-10 .* 17:30>" + (org-element-timestamp-interpreter + '(timestamp + (:range-type daterange + :type active-range + :year-start 2023 :month-start 7 :day-start 10 + :year-end 2023 :month-end 7 :day-end 10 + :hour-start 17 :minute-start 30 + :hour-end 17 :minute-end 30)) nil))) + ;;;; End date is not equal to start date, but interpret the object as a timerange (:range-type 'timerange) + (should + (string-match "<2023-07-10 .* 17:30-18:30>" + (org-element-timestamp-interpreter + '(timestamp + (:range-type timerange + :type active-range + :year-start 2023 :month-start 7 :day-start 10 + :year-end 2023 :month-end 8 :day-end 10 + :hour-start 17 :minute-start 30 + :hour-end 18 :minute-end 30)) nil))) + ;;;; End date is not equal to start date, interpret the object as a daterange (:range-type 'daterange) + (should + (string-match "<2023-07-10 .* 17:30>--<2023-08-10 .* 18:30>" + (org-element-timestamp-interpreter + '(timestamp + (:range-type daterange + :type active-range + :year-start 2023 :month-start 7 :day-start 10 + :year-end 2023 :month-end 8 :day-end 10 + :hour-start 17 :minute-start 30 + :hour-end 18 :minute-end 30)) nil)))) (ert-deftest test-org-element/verse-block-interpreter () "Test verse block interpretation." -- 2.40.1 --=-=-= Content-Type: text/plain > And please provide an additional patch for WORG: > https://orgmode.org/worg/dev/org-element-api.html#org6ae377e Sure. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-dev-org-element-api.org-Add-range-type-for-timestamp.patch >From ea809755deef50cf91f41d50241ab86a0787f2cf Mon Sep 17 00:00:00 2001 From: Ilya Chernyshov Date: Tue, 11 Jul 2023 19:00:40 +0700 Subject: [PATCH] dev/org-element-api.org: Add :range-type for timestamp objects --- dev/org-element-api.org | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/org-element-api.org b/dev/org-element-api.org index baf7e8a1..ffcda274 100644 --- a/dev/org-element-api.org +++ b/dev/org-element-api.org @@ -708,6 +708,8 @@ Object. (integer or ~nil~). - ~:type~ :: Type of timestamp (symbol: ~active~, ~active-range~, ~diary~, ~inactive~, ~inactive-range~). +- ~:range-type~ :: Type of range (symbol: ~daterange~, ~timerange~ or + ~nil~). - ~:warning-type~ :: Type of warning, if any (symbol: ~all~, ~first~ or ~nil~) - ~:warning-unit~ :: Unit of delay, if one is defined (symbol: ~year~, -- 2.40.1 --=-=-=--