ParseDateTime 不会为 SQL 服务器日期时间查询输出 ODBC 文字

ParseDateTime won't output ODBC literals for SQL Server datetime queries

设置 ColdFusion 2021 后,我发现当使用 ParseDateTime 格式化来自 SQL 服务器的日期时间值时,它不会像 ColdFusion 2016 那样将日期格式化为 ODBC 文字,例如{ts '2021-05-15 13:20:51'}。相反,它只是从数据库中输出不变的值。

这是我的环境:

Coldfusion 2016 Server:
Version: 2016,0,17,325979 
Tomcat Version: 8.5.61.0
Edition: Developer  
Operating System: Windows 7

Coldfusion 2021 Server:
Version: 2021,0,01,325996
Tomcat Version: 9.0.41.0
Edition: Developer  
Operating System: Windows 10

数据库是 SQL Server 2008 R2,ColdFusion 数据源正在使用 MS SQL 服务器驱动程序。

示例查询:

<cfquery name="qryDates">
    select id, expiry_date
    from purchases
</cfquery>

<cfoutput>#ParseDateTime(qryDates.expiry_date)#</cfoutput>

Coldfusion 2016 输出:

{ts '2021-05-15 13:20:51'}

Coldfusion 2021 输出:

2021-05-15 13:20:51.0

有没有办法让 ParseDateTime 的行为方式与它在我的 CF 2016 和我过去使用过的所有其他 CF 服务器上的行为方式相同?我不想更改格式来完成此操作(例如 #DateTimeFormat(ParseDateTime(qryDates.expiry_date))#)。

感谢您的任何建议。

我现在没有任何方法来检验我的假设,但我假设您看到了 ParseDateTime() 处理 Java 对象的方式的副作用,以及该函数介于 CF2016 和 CF2021/18.

之间

由于 expriry_date 是您数据库中的实际 datetime 对象,因此数据库中的 cfquery 将为您提供一个 sql 对象,而不仅仅是一个看起来像这样的字符串一个约会。由于它同时具有 datetime 组件,我相信 JDBC 将向 CF 发送一个 java.sql.Timestamp 对象。

ParseDateTime() 的目的是将字符串转换为日期对象,尽管它可以“格式化”日期对象,但这不是它的目的为了。它的初始假设是它被赋予一个字符串,并且由于 CF 是动态类型的,它通常会尝试将值隐式转换为正确的类型。并且由于数据库值是日期类型的,CF 将尝试使其适当地看起来像一个字符串。至少那是它打算工作的方式。看起来这就是 "{ts...".

看来 CF2021/18 正在接收一个 date 对象 而实际上并没有对它做太多事情。它似乎只是将 Java 对象 (没有 "{ts...") 退回,当 ColdFusion 试图将其转换为字符串时,它不是 ColdFusion时间戳变量所以没有 "{ts...".

演示代码:

<cfscript>
    /*** CREATE QUERY OBJECT ***/
    theDateQuery = QueryNew("dt","timestamp",[{"dt":createODBCDateTime('2021-04-09 12:01:02.345')}]) ;
    writeDump(theDateQuery);
    
    /*** SET QUERY RESULT TO A VARIABLE ***/
    dobj = theDateQuery.dt[1] ;
    writeOutput("dobj: " & dobj & " ("& getMetadata(dobj).getName() & ")<br>");
    
    /*** CONVERT QUERY VARIABLE TO A STRING ***/
    dstr = theDateQuery.dt[1].toString() ;
    writeOutput("dstr: " & dstr & " ("& getMetadata(dstr).getName() & ")<br>");
    
    /*** PARSEDATETIME OF OBJECT ***/
    x = parseDateTime(dobj) ;
    writeOutput("x: " & x & " ("& getMetadata(x).getName() & ")<br>");
    
    /*** PARSEDATETIME OF STRING ***/
    y = parseDateTime(dstr) ;
    writeOutput("y: " & y & " ("& getMetadata(y).getName() & ")<br>");
</cfscript>

CF 2016 及更低

https://trycf.com/gist/c9c35dccb04f91cd6c06e4082ed306ca/acf2016?theme=monokai

您的数据库正在向 ColdFusion 查询返回一个 Java 对象,该对象将进入 ParseDateTime 函数,而 CF 正在发挥它的魔力,然后返回一个 ColdFusion 日期对象,看起来就像一个 CF 时间戳,很容易被强制转换为您想要显示的内容。

dobj: 2021-04-09 12:01:02.345 (java.sql.Timestamp)

x: {ts '2021-04-09 12:01:02'} (coldfusion.runtime.OleDateTime)

CF2021(和CF2018)

https://trycf.com/gist/4c9bbc036a63a135962f0912b8591e00/acf2021?theme=monokai (trycf link for CF2021 貌似默认是Lucee5,所以你得重新选择CF2021)

CF2018 中的行为似乎略有变化。我不知道 ParseDateTime 函数的内部工作原理,但我假设在早期版本中,它将日期对象转换为日期字符串,然后再将其转换为 [=92= 上的 ColdFusion 对象].截至 2018 年,似乎如果它收到一个 Java 数据对象,它就会识别出它已经是一个日期对象并且只是 return 了它(绕过到 ColdFusion 日期字符串和对象的转换)。这似乎更符合最初陈述的行为。由于 Java 时间戳值没有 "{ts...",但 CF 有,它改变了我们最终从函数的 return 值创建字符串的方式。这可能是一个兼容性错误(因为行为略有不同),但我不完全确定。

无论如何,这似乎是由一个简单的事实造成的,即 ParseDateTime 在这里被用于其预期目的之外的其他用途。就像使用 TimeFormat(now(),"yyyy-MMM-dd hh:nn:ss") 一样。 ACF 将 return 一个带有年和日屏蔽的字符串,即使这不是 TimeFormat() 的目的(注意:Lucee 似乎可以正确处理它) .如果以意外方式使用函数,则不应期望 return 在不同版本中获得一致的结果。

这就是我不确定它是否应该是一个错误的地方。过去,大多数 ColdFusion 函数不太关心“日期”是字符串还是对象。 CF2018+好像有。或者至少以不同的方式对待他们。

最终,您的代码可能应该被修复以按照它们应有的方式使用函数。但是,我知道这可能有点多。您可以通过在解析之前将查询对象显式转换为字符串来解决当前问题。

ParseDateTime(qryDates.expiry_date.toString())

另一件需要注意的事情是,在 CF2016 中,如果您的 datetime 值有一个毫秒组件,那么它会在您的最终 ParseDateTime 值中被删除。但是,在 CF2021/18 中,由于 Java 值似乎没有更改,因此毫秒部分仍将在您的 return 值中。 toString() 也应该解决这个问题,您将得到与之前完全相同的结果。