我有一个日期(格式为 YYYY.MM.DD)和一个时间(格式为 HH:MM)。
根据这些,我想计算一种 时间戳/总分钟数。
\documentclass{article}
\makeatletter
% Timestamp
\newcommand{\timeStamp}[2]{%{#1-Date (YYYY.MM.DD)}{#2-Time (HH:MM)}
\expandafter\timeStamp@t#1 #2\@nil%
}%
\def\timeStamp@t#1.#2.#3 #4:#5\@nil{%
\the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%
\makeatother
\begin{document}
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\timeStamp{\dOne}{\tOne}\\%
\end{document}
编译器说:“失控参数?”
这可能是由于第二个参数的扩展存在问题,因为这个调用(\timeStamp{\dOne}{03:00}
)是可以的。我做错了什么?
答案1
\expandafter
仅在后面的标记后扩展一个标记(除非有参数)。 在你的情况下,这只是。 获取结果的一种方法是,在插入之前在宏中#1
收集扩展:#1 #2
\documentclass{article}
\makeatletter
% Timestamp
\newcommand{\timeStamp}[2]{%{#1-Date (YYYY.MM.DD)}{#2-Time (HH:MM)}
\edef\mytmp{#1 #2}\expandafter\timeStamp@t\mytmp\@nil%
}%
\def\timeStamp@t#1.#2.#3 #4:#5\@nil{%
\the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%
\makeatother
\begin{document}
\tracingmacros=2\tracingcommands=2
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\timeStamp{\dOne}{\tOne}
\end{document}
这种方法可以很好地推广到两个以上的论点。
答案2
人们可以对 进行一些处理\expandafter
,但直接的方法可能更好:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Nn \joseph_time_stamp:nn
{
\__joseph_time_stamp:w #1.#2\q_stop
}
\cs_generate_variant:Nn \joseph_time_stamp:nn { ff }
% a devious trick for the colon
\use:x
{
\cs_new:Npn
\exp_not:N \__joseph_time_stamp:w
##1.##2.##3.##4\token_to_str:N :##5
\exp_not:N \q_stop
}
{
\int_eval:n
{
#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
}
}
\NewExpandableDocumentCommand{\timeStamp}{mm}
{
\joseph_time_stamp:ff { #1 } { #2 }
}
\ExplSyntaxOff
\begin{document}
\def\tOne{03:00}
\def\dOne{2017.08.01}
\timeStamp{\dOne}{\tOne}
\timeStamp{2017.1.1}{0:0} % should print 0
\end{document}
如果时间规范不包含冒号,那就更容易了;问题是:
在的范围内是特殊的\ExplSyntaxOn
,所以直接
\cs_new:Npn \__joseph_time_stamp:w #1.#2.#3.#4:#5\q_stop
{
\int_eval:n
{
#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
}
}
不会起作用,需要采用间接方法来“字符串化”冒号。
一个可扩展的解决方案,带有\expandafter
参数处理(OS
针对“旧式”):
\makeatletter
\newcommand{\timeStampOS}[2]{%
\expandafter\timeStampOS@a\expandafter{#2}{#1}%
}
\newcommand{\timeStampOS@a}[2]{%
\expandafter\timeStampOS@b\expandafter{#2}{#1}%
}
\newcommand{\timeStampOS@b}[2]{\timeStampOS@c #1 #2\@nil}
\def\timeStampOS@c #1.#2.#3 #4:#5\@nil{%
\the\numexpr
#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
\relax
}
\makeatother
答案3
展开 #1和#2
% Timestamp
\newcommand\timeStamp[2]{%
\expandafter\timeStamp@t#2 #1\@nil}%
\def\timeStamp@t#1:#2 #3\@nil{\expandafter\timeStamp@@t#3 #1:#2\@nil}%
\def\timeStamp@@t#1.#2.#3 #4:#5\@nil{%
\the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%
答案4
更清楚的是:
将宏用于子计算,可能在其他地方重复使用,
在宏名中指明其对其参数的扩展类型。
像这样:(O
意思是“扩展一次参数”;通常人们更喜欢使用“F”来进行 f 型扩展,它会重复扩展第一个标记,因为这允许嵌套。否则,人们将需要例如T
“扩展两次参数”,因为使用\def\foo{\the\numexpr...}
,需要两次扩展才能完全扩展)。
\documentclass{article}
\newcommand{\TimeStampOO}[2]
{\the\numexpr\expandafter\DateToMinutes\expandafter{#1}+
\expandafter\TimeToMinutes\expandafter{#2}\relax}
\makeatletter
\newcommand{\DateToMinutes}[1]{\the\numexpr\Date@ToMinutes#1\relax}
\def\Date@ToMinutes#1.#2.#3\relax
{(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax}
\newcommand{\TimeToMinutes}[1]{\the\numexpr\Time@ToMinutes#1\relax}
\def\Time@ToMinutes#1:#2\relax
{#2+#1*60\relax}
\makeatother
\begin{document}
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\TimeStampOO{\dOne}{\tOne}%
\end{document}
这是可扩展的。