Org timestamp 范围问题

测试 Org 导出时发现 [4444-04-04] 时间戳无法导出,且存在 (error "Invalid time specification") 问题,发现是 encode-timeformat-time-string 调用问题。

我写了个测试,在 Windows 上可以得到最早和最晚的合理导出时间是 UNIX 时间的 -43200 秒到 32536850399 秒,前者是 1969 年 12 月 31 日 12:00 UTC,后者是 3001 年 1 月 19 日 21:59:59 UTC。

感觉这可能和 Windows 的相关 C 运行时有点关系,但目前机器上没装 WSL 测不了,至于 MacOS 那更是设备都没有。Linux/MacOS 上这两个范围值分别是多少?

以下是测试代码,用了简单的二分查找:

(defun my/find (bound up)
  (cl-flet ((fmt (num)
              (condition-case nil
                  (format-time-string "%F %R" num t)
                (error nil))))
    (while (not (and (fmt bound) (not (fmt (1+ bound)))))
      (let ((mid (/ (+ bound up) 2)))
        (if (fmt mid) (setq bound mid)
          (setq up mid))))
    bound))
(defun my/find2 (bound up)
  (cl-flet ((fmt (num)
              (condition-case nil
                  (format-time-string "%F %R" num t)
                (error nil))))
    (while (not (and (fmt (- bound)) (not (fmt (- (1+ bound))))))
      (let ((mid (/ (+ bound up) 2)))
        (if (fmt (- mid)) (setq bound mid)
          (setq up mid))))
    (- bound)))
(my/find 0 most-positive-fixnum)
;;=> 32536850399
(my/find2 0 most-positive-fixnum)
;;=> -43200

1 个赞
ELISP> (my/find 0 most-positive-fixnum)
67767976233532799
 (#o3606051660642776577, #xf0c29d868bfd7f)

ELISP> (my/find2 0 most-positive-fixnum)
-67768040609740800
 (#o-3606052620352574000, #x-f0c2ac83aaf800)

ELISP> emacs-version
"31.0.50"

ELISP> system-configuration
"aarch64-unknown-linux-android29"
ELISP> (my/find 0 most-positive-fixnum)
32535291599
 (#o362320223317, #x7934126cf)

ELISP> (my/find2 0 most-positive-fixnum)
-43200
 (#o-124300, #x-a8c0)

ELISP> emacs-version
"31.0.50"

ELISP> system-configuration
"x86_64-w64-mingw32"
1 个赞
Welcome to the Emacs shell

~ $ (my/find 0 most-positive-fixnum)
67768036191676799
~ $ (my/find2 0 most-positive-fixnum)
-67768040609740800
~ $ emacs-version
GNU Emacs 31.0.50 (build 2, aarch64-apple-darwin24.5.0, NS appkit-2575.60 Version 15.5 (Build 24F74))
 of 2025-06-14
~ $ system-configuration
system-configuration: command not found
1 个赞

感谢 @Doerthous, @TomoeMami

那看来就是 C runtime 的问题了,google 32536850399 唯一能找到的只有 ぼやきごと/2021-04-21/VC++:3001年問題 - ルーチェ’s Homepage 这一个页面。里面有这样一个表格。

注意到 VC++ 2013 还是 32535291599,而 2015 之后就是 32536850399。而 UCRT 运行时第一次出现就是在 VS 2015。

Linux 和 MacOS 看来支持的时间范围要原大于 Windows,看上去更加符合 64 位 time_t。

(log 67768036191676799 2) 
;;=> 55.91145448328166
(log 32536850399 2)
;;=> 34.9213555522607
2 个赞
ELISP> (org-read-date nil nil "0000-01-01")
"2000-01-01"

ELISP> (org-read-date nil nil "1970-01-01")
"1970-01-01"

ELISP> (org-read-date nil nil "1950-01-01")
"1970-01-01"

ELISP> system-type
android

尽管 Linux 支持的时间范围更大,但 org 本身似对时间戳范围进行了限制?我之前用 org-read-date 读 0 年也遇过这种下限问题。

(defun org-read-date ...
    ...
    (when org-read-date-analyze-forced-year
      (message "Year was forced into %s"
	       (if org-read-date-force-compatible-dates
		   "compatible range (1970-2037)"
		 "range representable on this machine"))
      (ding))
    ...
1 个赞

Not all dates can be represented in a given Emacs implementation. By default Org mode forces dates into the compatibility range 1970–2037 which works on all Emacs implementations. If you want to use dates outside of this range, read the docstring of the variable org-read-date-force-compatible-dates.

The date/time prompt (The Org Manual)

org-read-date-force-compatible-dates is a variable defined in ‘org.el’.

Its value is t

Should date/time prompt force dates that are guaranteed to work in Emacs?

Depending on the system Emacs is running on, certain dates cannot be represented with the type used internally to represent time. Dates between 1970-1-1 and 2038-1-1 can always be represented correctly. Some systems allow for earlier dates, some for later, some for both. One way to find out is to insert any date into an Org buffer, putting the cursor on the year and hitting S-up and S-down to test the range.

When this variable is set to t, the date/time prompt will not let you specify dates outside the 1970-2037 range, so it is certain that these dates will work in whatever version of Emacs you are running, and also that you can move a file from one Emacs implementation to another. Whenever Org is forcing the year for you, it will display a message and beep.

When this variable is nil, Org will check if the date is representable in the specific Emacs implementation you are using. If not, it will force a year, usually the current year, and beep to remind you. Currently this setting is not recommended because the likelihood that you will open your Org files in an Emacs that has limited date range is not negligible.

A workaround for this problem is to use diary sexp dates for time stamps outside of this range.

This variable was introduced, or its default value was changed, in version 24.1 of Emacs. You can customize this variable.

[back]

2 个赞

我的emacs在wsl 时间慢了4分钟 可恶 莫名其妙的