CTeX-org / forum

A temporarily alternate forum of `bbs.ctex.org`
https://t.me/chinesetex
Apache License 2.0
210 stars 16 forks source link

datetime2宏包如何在使用xelatex编译时取当前详细时间(秒数+时区)并转换为东八区 #321

Closed Mikachu2333 closed 1 month ago

Mikachu2333 commented 2 months ago

检查清单

操作系统

Win10 22h2 + Ubuntu 22.04.4 LTS (x64)

TeX 发行版

Tex Live 2024

描述问题

我尝试干什么

  1. 我在试图使用xelatex编译我的中文文稿(含有引用等多种复杂结构,需2次编译)
  2. 我在试图引入GitHub Actions以使每次push都能自动编译
  3. 我在试图在不同时区设置的系统上统一将获取的时间转换为东八区时间(举例,我的ubuntu是格林尼治标准时间,而我的Windows是东八区时间,我希望在这两个系统上都能把直接获取的时间转换为东八区时间)
  4. 我试图调整datetime2宏包的显示格式(以粗暴的封装方式,还是因为读不明白文档,同时也没能找到可以让我照着画瓢的葫芦)

我知道什么

  1. 我知道datetime2宏包里面提到了解决xelatex取不到秒数的解决方案但是我完全看不懂……
  2. 我知道datetime2宏包中有设置时区的函数\DTMsetcurrentzone{08}{00}但其只是粗暴地将获取到的时间由2024.5.16 22:00(current time) 00:00(time zone)变成了2024.5.16 22:00 08:00而并没有进行换算

其他

  1. 我认为datetime2宏包里应当有用于计算并转换时区的函数但是我没有找到
  2. 我希望通过latex本身解决此问题而非调整github action的时区和ubuntu的时区

最小工作示例(MWE)

\documentclass{ctexart}
\usepackage{datetime2}

% 时间样式重设定
\newcommand{\CurrentCustomTime}{
    \DTMsavenow{Compiled_time}%保存编译时间
    \DTMfetchyear{Compiled_time}.%
    \DTMtwodigits{\DTMfetchmonth{Compiled_time}}.%
    \DTMtwodigits{\DTMfetchday{Compiled_time}}\quad%
    \DTMtwodigits{\DTMfetchhour{Compiled_time}}:%
    \DTMtwodigits{\DTMfetchminute{Compiled_time}}\quad%
    GMT+08:00
}

\begin{document}

\CurrentCustomTime

\end{document}

用 XeLaTeX 编译后,无任何错误发生,文档成功编译(我的代码没有问题,只是缺少了相关的功能)

链接

No response

其他信息

原始pdf: http://mirrors.ibiblio.org/CTAN/macros/latex/contrib/datetime2/datetime2.pdf (关于xelatex的时区与秒数提醒在第6页下方)

其他资料: https://www.latexstudio.net/archives/7627.html https://zhuanlan.zhihu.com/p/667106521

附件

No response

muzimuzhi commented 2 months ago
\documentclass{article}
\usepackage[calc]{datetime2}

\begin{document}
\obeylines % treat line end as \par
\DTMsavenow{now}

Year: \DTMfetchyear{now}.
Month: \DTMfetchmonth{now}.
Day: \DTMfetchday{now}.
DOW: \DTMfetchdow{now}.

Hour: \DTMfetchhour{now}.
Minute: \DTMfetchminute{now}.
Second: \DTMfetchsecond{now}.
Time Zone Hour: \DTMfetchTZhour{now}.
Time Zone Minute: \DTMfetchTZminute{now}.
\end{document}

得到

Year: 2024.
Month: 05.
Day: 17.
DOW: 4.
Hour: 03.
Minute: 16.
Second: 09.
Time Zone Hour: +08.
Time Zone Minute: 00.
muzimuzhi commented 2 months ago

用 XeLaTeX 编译后,日志文件输出一切正常,无问题发生

「一切正常,无问题发生」可能带来误导,建议更明确地指出「现在的结果和想要的结果之间的差别」。比如,结合你的问题描述,我猜测差别是没能获取秒、时区是硬写的 GTM+9 GTM+8 和时间不匹配。可以直接地描述出来「哪些实现了、哪些还没实现」,避免让他人猜。

Mikachu2333 commented 2 months ago

我猜测差别是没能获取秒、时区是硬写的 GTM+9 和时间不匹配

的确如此,感谢指正,下次提问时会注意拆分问题并明确描述。

另,已更正上面的问题使之对后来者更清晰易懂。


此外,我倾向于您给出的第二个解决方案,即通过latex本身获取详细时间与日期并格式化为东八区时间而非通过设置github actions的时区来迁就,以免在其他设备编译时因为忘记设置而错误地获取时间。


结合上面的上面的问题及参考资料,我发现了一种比较完善的解决方案(但是从实现原理上来讲还是很生硬,例如因为没读明白doc导致format的时候只能自己硬生生每项获取再硬写),如下所示。

注,必须使用latexmk(xe)编译

main.tex

\documentclass{article}
\usepackage{texosquery}%xelatex取秒数及时区
\usepackage[calc]{datetime2}

\newcommand{\CurrentCustomTime}{
    \DTMsavenow{Compiled_time}%保存编译时间
    \DTMfetchyear{Compiled_time}.%
    \DTMfetchmonth{Compiled_time}.%
    \DTMfetchday{Compiled_time}\quad%
    \DTMfetchhour{Compiled_time}:%
    \DTMfetchminute{Compiled_time}:%
    \DTMfetchsecond{Compiled_time}%
    \quad%
    GMT+08:00
}

\begin{document}

\CurrentCustomTime

\end{document}

latexmkrc

本项的设置可保证无论在何种时区下编译均可正确转换为东八区时间,因此此时可以在main文档里硬写GMT+08:00

$ENV{'TZ'}='Asia/Shanghai';

效果

2024.05.17 09:43:10 GMT+08:00
muzimuzhi commented 2 months ago

此外,我倾向于您给出的第二个解决方案,即通过latex本身获取详细时间与日期并格式化为东八区时间而非通过设置github actions的时区来迁就,以免在其他设备编译时因为忘记设置而错误地获取时间。

我的本意是同时使用那两条,是「且」的关系。

\usepackage{texosquery}%xelatex取秒数及时区

我感觉不用加载 texosquery。XeTeX 直接提供了 primitive \creationdatedatetime2 会按不同引擎使用相应的 primitive,无需通过 texosquery 执行 java 再获取一遍。

建议在 \CurrentCustomTime 里用 \DTMfetchTZhour\DTMfetchTZminute 来获取时区信息,而不是手动写死 GMT+08:00。手动写死会比较难发现(latexmkrc 设置失效的)错误。

Mikachu2333 commented 2 months ago
  • 所以可以不用 \DTMtwodigits

我感觉不用加载 texosquery。XeTeX 直接提供了 primitive \creationdatedatetime2 会按不同引擎使用相应的 primitive,无需通过 texosquery 执行 java 再获取一遍。

经过测试,在本地的ubuntu编译时,时间显示是一位的,例如2024.5.13 14:5:00

并且,无法在不使用 texosquery 的前提下获得gmt时区和秒数,无论如何修改系统都是gmt00:00,秒数始终是00

但是,github action就显示为正常,很奇怪,明明传参都一样……

Mikachu2333 commented 2 months ago

我的本意是同时使用那两条,是「且」的关系。

一开始我也想同时使用来着,想了想决定还是不改系统时间了。 毕竟如果程序未成功转换东八区时间,但因为系统时间为正常东八区时间而掩盖了这种潜在问题就不好了

Mikachu2333 commented 2 months ago

再次测试,更奇怪的问题出现了,本地编译ubuntu获取秒数失败,时区也失败,无论是否使用了texosquery……

本地运行命令: latexmk --shell-escape -synctex=1 -interaction=nonstopmode -halt-on-error -file-line-error -xelatex main.tex

自动build:https://github.com/Mikachu2333/sdsmu_welcome_tex/actions/runs/9123346871/workflow


找到原因了,我是大傻子。

texosquery 要求jre8,我系统里没有jre……

此外,根据实测,必须安装并使用texosquery否则xelatex无法获取秒数和时区

muzimuzhi commented 1 month ago

我感觉不用加载 texosquery。XeTeX 直接提供了 primitive \creationdatedatetime2 会按不同引擎使用相应的 primitive,无需通过 texosquery 执行 java 再获取一遍。

并且,无法在不使用 texosquery 的前提下获得gmt时区和秒数,无论如何修改系统都是gmt00:00,秒数始终是00

嗯,这是 datetime2 v1.5.7 (2021-03-21) 的缺陷,它还不支持从 xetex 的 \creationdate 获得时间戳(从 XeTeX 0.999991, texlive 2019 起提供)。我之前错以为已经支持,例子也没有用 xelatex 编译。

datetime2 的作者已经知道这个问题,只是还没有更新包

XeLaTeX now provides \creationdate. The next version of datetime2 will add support for this. In the meantime, if you are using a new version of XeLaTeX, add the following line before you load datetime2:

\providecommand{\pdfcreationdate}{\creationdate}

我还尝试了直接使用 datetime2「切换」时区。过程要麻烦许多,也暴露出一点 datetime2 内部实现的不一致,见 \DTMtwodigits{\DTMfetchsecond{tmp@cest} 的部分。

想这么做是因为我不确定 latexmkrc 通过 Perl $ENV{key} = 'val' 设置的环境变量是不是生效、xelatex 等 latex 程序是否认 TZ 环境变量。只是猜测和担心,我没试。

% !TeX program = xelatex
\documentclass{article}
\usepackage{iftex}
% workaround for datetime2 <= 1.5.7 running in XeTeX
% see https://www.dickimaw-books.com/faq.php?id=273
\ifXeTeX
  \providecommand{\pdfcreationdate}{\creationdate}
\fi

\usepackage[calc]{datetime2}

% \myDTMtoTZ{<name1>}{<name2>}{<time zone hour>}{<time zone minute>}
% based on https://tex.stackexchange.com/a/634977
\NewDocumentCommand{\myDTMtoTZ}{ mmmm }{%
  % convert <name1> to UTC+0
  \DTMtozulu{#1}{tmp@zulu}%
  % add requested timezone offset to zulu time
  \DTMsaveaszulutime{tmp@cest}%
    {\DTMfetchyear{tmp@zulu}}{\DTMfetchmonth{tmp@zulu}}{\DTMfetchday{tmp@zulu}}%
    {\DTMfetchhour{tmp@zulu}}{\DTMfetchminute{tmp@zulu}}%
    {\DTMfetchsecond{tmp@zulu}}{\numexpr-#3\relax}{#4}%
  % save new datetime to <name2>, in requested timezone
  \DTMsavetimestamp{#2}{\expanded{%
    \DTMfetchyear{tmp@cest}-\DTMfetchmonth{tmp@cest}-\DTMfetchday{tmp@cest}%
    T%
    % second (sec) field of a datetime created by \DTMsaveaszulutime is not
    % guaranteed to be two digits
    \DTMfetchhour{tmp@cest}:\DTMfetchminute{tmp@cest}:\DTMtwodigits{\DTMfetchsecond{tmp@cest}}%
    \space
    #3:#4}}%
}

% shortcut
\NewDocumentCommand{\myDTMuseinTZ}{ mm }{%
  \leavevmode\llap{\makebox[4em][l]{UTC\ifnum#2=0\else#2\fi}: }%
  \myDTMtoTZ{#1}{tmp@now}{#2}{0}%
  \DTMuse{tmp@now}\par
}

\begin{document}
\ttfamily

\DTMsavenow{now}

\leavevmode\llap{now: }%
\DTMuse{now}

\medskip

\myDTMuseinTZ{now}{00}
\myDTMuseinTZ{now}{+08}
\myDTMuseinTZ{now}{-10}
\myDTMuseinTZ{now}{+14}
\end{document}

得到

    now: 2024-05-17 19:21:22+08:00

UTC    : 2024-05-17 11:21:22Z
UTC+08 : 2024-05-17 19:21:22+08:00
UTC-10 : 2024-05-17 01:21:22-10:00
UTC+14 : 2024-05-18 01:21:22+14:00
Mikachu2333 commented 1 month ago

想这么做是因为我不确定 latexmkrc 通过 Perl $ENV{key} = 'val' 设置的环境变量是不是生效、xelatex 等 latex 程序是否认 TZ 环境变量。只是猜测和担心,我没试。

经过测试,完美支持。

我还尝试了直接使用 datetime2「切换」时区。过程要麻烦许多,也暴露出一点 datetime2 内部实现的不一致,见 \DTMtwodigits{\DTMfetchsecond{tmp@cest} 的部分。

看了半天没看懂(捂脸)自己的latex水平还是太低了,当前阶段(在datetime2没发新版之前)还是用着rc文件比较简单易行一点

此外,我也试图尝试过您的思路,碍于知识不足中途放弃,在努力的过程中我也是感觉datetime2似乎没有给出一些类似 rust 中 format!() 之类的成熟的格式化与运算接口(与tex语言本身也不是擅长与这方面也有关系看起来,不过没有转换时区的成熟的包装好的接口有些令人意外),导致用户在自定义输出格式与转换时区时分外艰难

muzimuzhi commented 1 month ago

所以 issue 已经解决、可以关闭了?

(不用说「您」的)

Mikachu2333 commented 1 month ago

所以 issue 已经解决、可以关闭了?

是的

(不用说「您」的)

必要的礼貌,毕竟是有求于人(

muzimuzhi commented 1 month ago

顺便一提,datetime2 提供了设置输出样式的方式,见文档 sec. 5 "Styles"。但我感觉如果只需要输出一次,用 \DTMfetch<field>{<name>} 手动拼接就行了。

muzimuzhi commented 1 month ago

在努力的过程中我也是感觉datetime2似乎没有给出一些类似 rust 中 format!() 之类的成熟的格式化与运算接口

可以试试用 tex 调用 python 的包,比较新的有 pythonimmediate,支持所有引擎、只要一次编译。pythontex 则需要两次编译,把 python 输出写入了辅助文件。https://ctan.org/topic/callback 列举了类似的宏包。