TO_TIMESTAMP(x) 调用正在进行两次隐式转换,因此它仅在您的 NLS_DATE_FORMAT 和 NLS_TIMESTAMP_FORMAT 相似时才有效。该函数接受一个字符串参数,因此您实际上是在使用TO_TIMESTAMP(TO_CHAR(x)),并且由于您不应该依赖 NLS 设置,您应该真正将其扩展为 TO_TIMESTAMP(TO_CHAR(x, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS')。
显然,这只会让你的涂抹变得更糟,除非你按照 TrippKinetics 的建议在函数中这样做。或者您可以在视图中或使用虚拟列进行操作;但客户可能也不热衷于其中任何一个。
要完全在查询中执行此操作,您可以使用CAST 而不是TO_TIMESTAMP,这比原来的输入要好一些:
SELECT TO_CHAR(FROM_TZ(CAST(x AS TIMESTAMP), 'America/New_York'),
'YYYY-MM-DD HH24:MI:SSTZHTZM') AS x
如果您的会话始终与数据库处于同一时区,或者您可以合理地设置它,那么您可以绕过每个查询的 FROM_TZ 部分:
ALTER SESSION SET TIME_ZONE = 'America/New_York';
SELECT TO_CHAR(CAST(x AS TIMESTAMP WITH TIME ZONE),
'YYYY-MM-DD HH24:MI:SSTZHTZM') AS x
但您必须确保您可以/将始终更改会话。
如果你总是改变会话,这可能是设置你的 NLS_TIMESTAMP_TZ_FORMAT 的合法时间(尽管我怀疑我会因为考虑建议它而被否决!) ,这意味着您可以使用隐式转换进行显示:
ALTER SESSION SET TIME_ZONE = 'America/New_York';
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SSTZHTZM';
SELECT CAST(x AS TIMESTAMP WITH TIME ZONE) AS x
使用您的示例日期/时间和一个在夏季,这给出了:
X
-------------------------
2014-05-27 14:48:12-0400
2013-12-14 15:12:46-0500
更改会话后,您只需将所需的每个日期列以扩展格式包装在 CAST() 中。这仍然有点痛苦,但更易于管理。尽管使用视图或虚拟列很接近,但无法在数据库中完全透明地做到这一点。定义自己的函数或设置会话以便安全地CAST 需要更多的工作。
可以说依赖会话更改会增加一些风险、不确定性或混乱,但这是为了方便而做出的权衡。
所有这些都假设所有原始日期确实都是针对一个时区的。