if 子句中的日期时间比较

datetime comparison in if clause

我有以下代码:

%if "&min_date"dt <= "02DEC2014:00:00:00"dt %then %do;
  /* some code */
%end

它的行为与我预期的不一样。

我的 min_date 较差,但测试的表达式被评估为 0

%put &min_date; /* 31JAN1600:00:00:00 */
%put %eval("&min_date"dt <= "02DEC2014:00:00:00"dt); /* 0 */
%put %eval("31JAN1600:00:00:00"dt <= "02DEC2014:00:00:00"dt); /* 0 */

我该如何进行这项工作?

即使您使用的是文字,我认为宏环境也会将被比较的值视为字符串 - 它不会在执行比较之前尝试将文字解析为基础数值。如果您希望比较按预期进行,则必须将日期转换为宏语言可以作为数字进行比较的日期,例如

%put %eval(%sysfunc(putn("&min_date"dt,16.)) <= %sysfunc(putn("02DEC2014:00:00:00"dt,16.)));

或者,您可以暂时跳出宏语言,然后直接使用文字,例如:

%put %eval(%sysfunc(intck(second,"&min_date"dt,"02DEC2014:00:00:00"dt)) > 0);

如您所示,%EVAL 不知道日期时间文字,因此它将它们视为字符串值。由于 %EVAL 是由 %IF 语句隐式调用的。 %IF 语句进行文本比较而不是数字比较。

好消息是 %SYSEVALF 知道日期时间文字:

%let min_date = 31JAN1600:00:00:00 ; 
%put %sysevalf("&min_date"dt) ;   /*-11357884800*/
%put %sysevalf("&min_date"dt <= "02DEC2014:00:00:00"dt);  /* 1 */
%put %sysevalf("31JAN1600:00:00:00"dt <= "02DEC2014:00:00:00"dt); /* 1 */

在宏 %IF 中:

%macro test(min_date=) ;
  %if %sysevalf("&min_date"dt <= "02DEC2014:00:00:00"dt) %then %do;
    %put &min_date is less than or equal to "02DEC2014:00:00:00"dt ;
  %end ;
  %else %do ;
    %put &min_date is NOT less than or equal to "02DEC2014:00:00:00"dt ;
  %end ;
%mend ;

returns 日志如下:

124  %test(min_date=31JAN1600:00:00:00)
31JAN1600:00:00:00 is less than or equal to "02DEC2014:00:00:00"dt
125  %test(min_date=31JAN2015:00:00:00)
31JAN2015:00:00:00 is NOT less than or equal to "02DEC2014:00:00:00"dt

实际上,%EVAL() 在您的示例中做了 "work"。 :) 不符合预期,但原因如下:

  • 首先,%EVAL() 检查操作数是否可以计算为算术(仅整数)或逻辑。

  • 因为您提供了 2 个包含非整数字符的日期时间文字,所以它根据字符排序顺序执行了逻辑比较。

%SYSEVALF() - 如前所述 - 将起作用,因为与 %EVAL() 不同,它支持非整数字符作为操作数并对其进行额外评估。

这就是 %SYSEVALF() 支持浮点运算的方式,而 %EVAL() 只能支持整数。

顺便说一句,%EVAL() 也用于评估 %IF 或 %DO 语句等中的表达式 - 这就是为什么在这些情况下也不支持日期时间文字的原因。

注意:牢记以上几点,通常好的做法是:

  • 对于整数运算,使用 %EVAL()
  • 对于浮点运算,使用 %SYSEVAL()
  • 始终将日期时间文字转换为整数,如上面的答案所示,使用 %sysfunc(inputn(&datetimeliteral,datetimex.))

其中 x 将根据日期时间文字变化...