将基于十进制列的时间戳转换为实际时间戳时,使用 HH24MISS 扩展“格式字符串”的问题

Posted

技术标签:

【中文标题】将基于十进制列的时间戳转换为实际时间戳时,使用 HH24MISS 扩展“格式字符串”的问题【英文标题】:Problems extending `format-string` with HH24MISS when converting decimal column based timestamp to actual timestamp 【发布时间】:2015-08-21 13:32:34 【问题描述】:

tl;博士

为什么我无法转换以下字符串时间戳

select timestamp_format('2015-08-21 000000', 'YYYY-MM-DD HH24MISS') as timestamp
  from sysibm.sysdummy1;

在 i7.1.0/OS 机器上?

特别是因为我可以转换

select timestamp_format('000000' , 'HH24MISS') as timestamp
  from sysibm.sysdummy1;

到:

timestamp
-------------------------
2015-08-01 00:00:00.000000

上下文

在 i7.1.0/OS 机器上,我有一个表,其中时间戳数据分成几个十进制列,例如

declare global temporary table tstamp
(
  year dec(4,0),
  month dec(2,0),
  day dec(2,0),
  time dec(6,0)
);

像这样的数据

insert into session.tstamp
  values (2015,8,21,92601),
         (2015,8,21,132601);

我想对其进行一些日期过滤。鉴于格式有些不灵活,我认为将其转换为时间戳并使用它来查询表可能会更好。于是我咨询了i/OS 7.1 Manual on timestamp_format

我从构建日期部分开始,最后是

select
  timestamp_format(YEAR || '-' || MONTH || '-' || DAY, 'YYYY-MM-DD') as timestamp
  from session.tstamp;

返回

TIMESTAMP
--------------------------
2015-08-21 00:00:00.000000  
2015-08-21 00:00:00.000000  

完美,让我们添加时间部分和明确的lpad 它包含六个字符:

select
  timestamp_format(YEAR || '-' || MONTH || '-' || DAY || ' ' || lpad(TIME, 6, '0'), 'YYYY-MM-DD HH24MISS') as timestamp
  from session.tstamp;

这会导致以下错误:

SQL 状态:22007

供应商代码:-20448

消息:[SQ20448] 使用为 TIMESTAMP_FORMAT 指定的格式字符串的表达式无效。原因 。 . . . . : TIMESTAMP_FORMAT 函数的参数 1 无法使用参数 2 中指定的格式字符串进行解释,原因如下之一: -- 字符串表达式太短或太长。 -- 字符串表达式不符合格式字符串中指定的模板。 -- 在字符串表达式中为格式字符串中的相应格式元素指定了太多数字。 -- 字符串表达式中的值对于格式字符串中的相应格式元素无效。恢复 。 . . : 为函数指定一个有效的格式字符串。再次尝试请求。

根据manual关于format-string,字段之间的分隔符是可选的:

[...] 两个格式元素可以选择由以下一个或多个分隔符分隔: [...]

问题

既然我明确将时间长度限制为六个字符,为什么在使用 'YYYY-MM-DD HH24MISS' 作为 format-string 时我的值不被接受?

旁注

可以将HH24MISS 单独用作format-string,所以我无法完全理解这一点。

select timestamp_format(lpad(TIME, 6, '0'), 'HH24MISS') as timestamp from session.tstamp;

TIMESTAMP
--------------------------
2015-08-01 13:26:01.000000  
2015-08-01 09:26:01.000000  

【问题讨论】:

...最好将它存储为一个时间戳,尽管您现在可能无法更改它。不过,为您省去很多关于查询和无效值的麻烦(这里至少有一种潜在的解决方法可能不是 DST 安全的)。请注意,将WHERE 子句转换为时间戳将使查询不可搜索;它不能使用索引(几乎适用于所有功能)。如果省略更多分隔符会怎样? 我完全同意你关于将它放在时间戳字段中的专业人士的架构问题。在这种情况下,查询将用于获取测试数据。所以速度不是最大的问题。省略所有分隔符按预期工作,但这迫使我离开填充所有字段。但是您肯定在这里做某事;如果我只有空格作为唯一的分隔符,我会得到同样的错误。 是的,我认为,与文档中暗示的相反,您要么需要分离所有内容,要么什么都不分离。 它在 i/OS 7.2 中按预期工作。 【参考方案1】:

所描述的困难是由于 TIMESTAMP_FORMAT [aka TO_DATE] 标量的缺陷。显示的请求经过测试,如预期的那样,与 IBM i 7.3 的 DB2 [以及对 OP 的评论建议,也在 v7r2 上] 运行。我曾问过一个类似的问题,“为什么使用我之前的示例会失败?”在SQL convert text mm/dd/yy to date and timestamp 中,但我还没有在新版本中重新访问这些示例。 FWiW,IBM i 7.1 上可能有一些更新的代码用于该功能的最新代码;我没有那种级别的维护,所以我无法测试该版本即将推出的 [最后一项] 增强功能是否包括较新版本中明显存在的代码修复。

请注意,TO_DATE 功能不是真正的内置功能 [相反,是系统提供的用户定义函数 (UDF)],因此我个人会推荐一个替代方案;即,编写和使用特定于任务的标量 UDF,和/或选择更兼容和更简单的方法从定义的这些列生成 TIMESTAMP。考虑以下表达式 [假设所有日期都在 1000 年之后,否则表达式必须更改为使用 DIGITS(YEAR) 而不是 YEAR]:

timestamp( YEAR concat digits( MONTH ) concat digits( DAY )
           concat digits( TIME )
         ) 

其中的一种变体是使用算术来实现与14–character timestamp-string form 'yyyymmddhhmmss' 相同的效果:

timestamp( concat( YEAR * 10000  + MONTH * 100 + DAY
                 , digits ( TIME ) ) )              

可以创建以下标量函数以避免在 [VIEW] 查询或其他地方对表达式进行编码。按照编码,除了 RETURN 语句上的表达式外,什么都没有,应该允许 in-lining;我没有指定任何其他可能与性能相关的子句,例如并行或空输入:

create function y4m2d2t6TS                   
( year   dec(4, 0)                           
, month  dec(2, 0)                           
, day    dec(2, 0)                           
, time   dec(6, 0)                           
) returns timestamp                          
language sql deterministic                   
return                                       
   digits( YEAR ) concat digits( MONTH )     
   concat digits( DAY ) concat digits( TIME )

; -- this semicolon is a statement separator, not terminator of above CREATE
select 
  y4m2d2t6TS( year, month, day, time ) as timestamp
from session.tstamp
; -- likeness of report from above query:
TIMESTAMP                 
2015-08-21-09.26.01.000000
2015-08-21-13.26.01.000000
********  End of data  ***

【讨论】:

【参考方案2】:

您可以在 DB2 中使用它:

values(VARCHAR_FORMAT(current_date,'YYYY-MM-DD HH24:MI:SS'))

再见

【讨论】:

这对我有什么帮助?第一个参数是一个实际的时间戳,这不是我可用的。我想创建那个实际的时间戳。 如果您只想要特殊格式的实际时间戳。将代码中的 current_date 替换为您的 timestamp 。并更改参数 'YYYY-MM-DD HH24:MI:SS' 。或者给我举出你想要的 IN 和 OUT 的例子。

以上是关于将基于十进制列的时间戳转换为实际时间戳时,使用 HH24MISS 扩展“格式字符串”的问题的主要内容,如果未能解决你的问题,请参考以下文章

将纪元转换为时间戳时获取不正确的日期

将 Varchar 转换为 Snowflake 中的时间戳

如何将时间戳从十六进制格式转换为 EPOCH 时间戳?

将 Epoch 时间戳转换为 sql server(人类可读格式)

如何在 Kotlin 中将 Firestore 日期/时间戳转换为日期?

蜂巢中的时间戳?